Finish loops

This commit is contained in:
Clément Fournier
2020-06-20 02:54:16 +02:00
parent 2448cc73ce
commit d0a96e7808
2 changed files with 243 additions and 43 deletions

View File

@ -5,9 +5,7 @@
package net.sourceforge.pmd.lang.java.rule.errorprone;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@ -16,7 +14,11 @@ import java.util.Set;
import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator;
import net.sourceforge.pmd.lang.java.ast.ASTBreakStatement;
import net.sourceforge.pmd.lang.java.ast.ASTDoStatement;
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
import net.sourceforge.pmd.lang.java.ast.ASTForInit;
import net.sourceforge.pmd.lang.java.ast.ASTForStatement;
import net.sourceforge.pmd.lang.java.ast.ASTForUpdate;
import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTName;
@ -24,6 +26,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement;
import net.sourceforge.pmd.lang.java.ast.ASTStatement;
import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
@ -79,13 +82,8 @@ public class UnusedAssignmentRule extends AbstractJavaRule {
static class LivenessVisitor extends JavaParserVisitorAdapter {
private final Deque<ScopeData> breakAddresses = new ArrayDeque<>();
private final Map<String, ScopeData> namedBreaks = new HashMap<>();
// following deals with control flow
@Override
public Object visit(JavaNode node, Object data) {
@ -99,29 +97,73 @@ public class UnusedAssignmentRule extends AbstractJavaRule {
@Override
public Object visit(ASTIfStatement node, Object data) {
ScopeData before = (ScopeData) node.getCondition().jjtAccept(this, data);
ScopeData before = acceptOpt(node.getCondition(), (ScopeData) data);
ScopeData thenData = before.fork();
thenData = (ScopeData) node.getThenBranch().jjtAccept(this, thenData);
if (node.hasElse()) {
ScopeData elseData = (ScopeData) node.getElseBranch().jjtAccept(this, before.fork());
return thenData.join(elseData);
} else {
return before.join(thenData);
}
ScopeData thenData = acceptOpt(node.getThenBranch(), before.fork());
ScopeData elseData = acceptOpt(node.getElseBranch(), before.fork());
return thenData.join(elseData);
}
@Override
public Object visit(ASTWhileStatement node, Object data) {
ScopeData before = (ScopeData) node.getCondition().jjtAccept(this, data);
// perform a few "iterations", to make sure that assignments in
// the body can affect themselves in the next iteration, and
// that they affect the condition
ScopeData before = acceptOpt(node.getCondition(), (ScopeData) data);
ScopeData iter = (ScopeData) node.getBody().jjtAccept(this, before.fork());
iter = (ScopeData) node.getCondition().jjtAccept(this, iter);
iter = (ScopeData) node.getBody().jjtAccept(this, iter);
ScopeData iter = acceptOpt(node.getBody(), before.fork());
iter = acceptOpt(node.getCondition(), iter);
iter = acceptOpt(node.getBody(), iter);
return before.join(iter);
}
@Override
public Object visit(ASTDoStatement node, Object data) {
// same as while but don't check the condition first
ScopeData before = (ScopeData) data;
ScopeData iter = acceptOpt(node.getBody(), before.fork());
iter = acceptOpt(node.getCondition(), iter);
iter = acceptOpt(node.getBody(), iter);
return before.join(iter);
}
@Override
public Object visit(ASTForStatement node, Object data) {
ASTStatement body = node.getBody();
if (node.isForeach()) {
// the iterable expression
ScopeData before = (ScopeData) node.getChild(1).jjtAccept(this, data);
ScopeData iter = acceptOpt(body, before.fork());
iter = acceptOpt(body, iter); // the body must be able to affect itself
return before.join(iter);
} else {
ASTForInit init = node.getFirstChildOfType(ASTForInit.class);
ASTExpression cond = node.getCondition();
ASTForUpdate update = node.getFirstChildOfType(ASTForUpdate.class);
ScopeData before = (ScopeData) data;
before = acceptOpt(init, before);
before = acceptOpt(cond, before);
ScopeData iter = acceptOpt(body, before.fork());
iter = acceptOpt(update, iter);
iter = acceptOpt(cond, iter);
iter = acceptOpt(body, iter); // the body must be able to affect itself
return before.join(iter);
}
}
private ScopeData acceptOpt(JavaNode node, ScopeData before) {
return node == null ? before : (ScopeData) node.jjtAccept(this, before);
}
@Override
public Object visit(ASTThrowStatement node, Object data) {
data = super.visit(node, data);
@ -216,14 +258,14 @@ public class UnusedAssignmentRule extends AbstractJavaRule {
if (suffix.isArguments() || suffix.isArrayDereference()) {
return null;
}
return findVar(primary.getScope(), true, suffix.getImage());
return findVar(primary.getScope(), true, substringBeforeFirst(suffix.getImage(), '.'));
} else {
if (inLhs && primary.getNumChildren() > 1) {
return null;
}
if (prefix.getChild(0) instanceof ASTName) {
return findVar(prefix.getScope(), false, prefix.getChild(0).getImage());
return findVar(prefix.getScope(), false, substringBeforeFirst(prefix.getChild(0).getImage(), '.'));
}
}
}
@ -231,6 +273,11 @@ public class UnusedAssignmentRule extends AbstractJavaRule {
return null;
}
private static String substringBeforeFirst(String str, char delim) {
int i = str.indexOf(delim);
return i < 0 ? str : str.substring(0, i);
}
private VariableNameDeclaration findVar(Scope scope, boolean isThis, String name) {
if (name == null) {
return null;

View File

@ -179,8 +179,12 @@ public class Foo {
<test-code>
<description>#1393 PMD hanging during DataflowAnomalyAnalysis</description>
<!-- Note: due to https://sourceforge.net/p/pmd/bugs/1383/ the 3 problems are false positives! -->
<expected-problems>3</expected-problems>
<expected-problems>2</expected-problems>
<expected-linenumbers>10,19</expected-linenumbers>
<expected-messages>
<message>The value assigned to variable 'fail' is never used</message>
<message>The value assigned to variable 'fail' is never used</message>
</expected-messages>
<code><![CDATA[
public class LoopTest {
public static void main(String[] args) {
@ -255,18 +259,65 @@ class Test{
</test-code>
<test-code>
<description>#1905 [java] DataflowAnomalyAnalysis Rule in right order : Case 2. DU-Anomaly(a)</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>5</expected-linenumbers>
<description>For loop</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
class Test{
public static void main(String[] args){
int a = 0 ;
for(int i = 0 ; i <= 10; i ++){
a = a+3;
}
}
}
]]></code>
</test-code>
<test-code>
<description>For loop 2</description>
<expected-problems>2</expected-problems>
<expected-linenumbers>3,5</expected-linenumbers>
<expected-messages>
<message>The value assigned to variable 'a' is never used</message>
<message>The value assigned to variable 'a' is never used</message>
</expected-messages>
<code><![CDATA[
class Test{
public static void main(String[] args){
int a = 0 ;
for(int i = 0 ; i <= 10; i ++){
a = a+3;
a = i * 3;
}
}
}
]]></code>
</test-code>
<test-code>
<description>For loop 3</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
class Test{
public static void main(String[] args){
int a = 0 ;
for(int i = 0 ; i <= 10; i ++){
a = i * 3;
}
System.out.println(a);
}
}
]]></code>
</test-code>
<test-code>
<description>For loop 4</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
class Test{
public static void main(String[] args){
int a = 0 ;
for(int i = 0 ; (i + a) <= 10; i ++){
a = i * 3;
}
}
}
@ -409,12 +460,8 @@ class Test{
</test-code>
<test-code>
<description>#1905 [java] DataflowAnomalyAnalysis Rule in right order : Case 5. DU-Anomaly(a)</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>6</expected-linenumbers>
<expected-messages>
<message>Found 'DU'-anomaly for variable 'a' (lines '6'-'9').</message>
</expected-messages>
<description>Do while 0</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
class Test{
public static void main(String[] args){
@ -429,6 +476,55 @@ class Test{
]]></code>
</test-code>
<test-code>
<description>Do while 1</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>3</expected-linenumbers>
<expected-messages>
<message>The value assigned to variable 'a' is never used</message>
</expected-messages>
<code><![CDATA[
class Test{
public static void main(String[] args){
int a = 0;
int i = 0;
do {
a = i+3;
i += 3;
} while ((a+i) < 30);
}
}
]]></code>
</test-code>
<test-code>
<description>Do while with break</description>
<expected-problems>2</expected-problems>
<expected-linenumbers>7,8</expected-linenumbers>
<expected-messages>
<message>The value assigned to variable 'i' is never used</message>
<message>The value assigned to variable 'a' is never used</message>
</expected-messages>
<code><![CDATA[
class Test{
public static void main(String[] args){
int a = 0;
int i = 0;
do {
if (a >= 20) {
i = 4;
a *= 5;
break;
}
a = i + 3;
i += 3;
} while (i < 30);
}
}
]]></code>
</test-code>
<test-code>
<description>#1905 [java] DataflowAnomalyAnalysis Rule in right order : Case 6. DU-Anomaly(a)</description>
<expected-problems>4</expected-problems>
@ -482,11 +578,11 @@ class Test{
</test-code>
<test-code>
<description>#1905 [java] DataflowAnomalyAnalysis Rule in right order : Case 9. DU-Anomaly(t1)</description>
<description>Usage as LHS of method</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>5</expected-linenumbers>
<expected-messages>
<message>Found 'DU'-anomaly for variable 't1' (lines '5'-'6').</message>
<message>The value assigned to variable 't1' is never used</message>
</expected-messages>
<code><![CDATA[
class Test{
@ -500,11 +596,11 @@ class Test{
</test-code>
<test-code>
<description>#1905 [java] DataflowAnomalyAnalysis Rule in right order : Case 12. DU-Anomaly(t1)</description>
<description>Assignment in operand</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>6</expected-linenumbers>
<expected-messages>
<message>Found 'DU'-anomaly for variable 't1' (lines '6'-'7').</message>
<message>The value assigned to variable 't1' is never used</message>
</expected-messages>
<code><![CDATA[
class Test{
@ -519,7 +615,27 @@ class Test{
</test-code>
<test-code>
<description>#1905 [java] DataflowAnomalyAnalysis Rule in right order : Case 13</description>
<description>Assignment in operand 2</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>7</expected-linenumbers>
<expected-messages>
<message>The value assigned to variable 't1' is never used</message>
</expected-messages>
<code><![CDATA[
class Test{
public static void main(String[] args){
int t1 = 0 ;
int t2 = 0 ;
// the left assignment reaches the right of the ==
if ( (t1 = t1 + t2)
== (t1 = t2 * t1) ); // only this assignment is unused
}
}
]]></code>
</test-code>
<test-code>
<description>Assignment in operand 3</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
class Test{
@ -534,12 +650,12 @@ class Test{
</test-code>
<test-code>
<description>#1905 [java] DataflowAnomalyAnalysis Rule in right order : Case 14. DU-Anomaly(t1, t2)</description>
<description>Assignment in operand 4</description>
<expected-problems>2</expected-problems>
<expected-linenumbers>4,6</expected-linenumbers>
<expected-messages>
<message>Found 'DU'-anomaly for variable 't2' (lines '4'-'7').</message>
<message>Found 'DU'-anomaly for variable 't1' (lines '6'-'7').</message>
<message>The value assigned to variable 't2' is never used</message>
<message>The value assigned to variable 't1' is never used</message>
</expected-messages>
<code><![CDATA[
class Test{
@ -558,13 +674,50 @@ class Test{
<expected-problems>1</expected-problems>
<expected-linenumbers>4</expected-linenumbers>
<expected-messages>
<message>Found 'DU'-anomaly for variable 'a' (lines '4'-'5').</message>
<message>The value assigned to variable 'a' is never used</message>
</expected-messages>
<code><![CDATA[
public class Test {
public void test(){
int a = 0;
a = a + 3;
int i = 0;
i += 3; // same with compound
}
}
]]></code>
</test-code>
<test-code>
<description>Compound assignment</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>4</expected-linenumbers>
<expected-messages>
<message>The value assigned to variable 'a' is never used</message>
</expected-messages>
<code><![CDATA[
public class Test {
public void test(){
int a = 0;
a += 3; // same with compound
}
}
]]></code>
</test-code>
<test-code>
<description>Another case</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>3,5</expected-linenumbers>
<expected-messages>
<message>The value assigned to variable 'iter' is never used</message>
<message>The value assigned to variable 'iter' is never used</message>
</expected-messages>
<code><![CDATA[
public class Test {
public void test(){
ScopeData iter = acceptOpt(node.getBody(), before.fork()); // this assignment is unused
iter = acceptOpt(node.getCondition(), before.fork());
iter = acceptOpt(node.getBody(), iter);
}
}
]]></code>