Skip to content

Commit f45c429

Browse files
SONARJAVA-4964 Fix FP in S1941 in case of Unknown symbol referencing declared variable (#5121)
1 parent aa0c217 commit f45c429

2 files changed

Lines changed: 35 additions & 13 deletions

File tree

java-checks/src/main/java/org/sonar/java/checks/VariableDeclarationScopeCheck.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,16 @@ public void visitNode(Tree tree) {
5555
}
5656
}
5757

58+
/**
59+
* If there is a {@code return} or {@code throw} in {@code body} at index greater or equal to {@code next} and before any reference to
60+
* {@code variable} then report an issue.
61+
*/
5862
private void check(VariableTree variable, List<StatementTree> body, int bodySize, int next) {
5963
Symbol symbol = variable.symbol();
6064
ReferenceVisitor referenceVisitor = new ReferenceVisitor(symbol);
6165
for (int i = next; i < bodySize; i++) {
6266
referenceVisitor.visit(body.get(i));
63-
if (referenceVisitor.referenceSymbol) {
67+
if (referenceVisitor.referencesSymbol) {
6468
return;
6569
}
6670
if (referenceVisitor.hasBreakingStatement) {
@@ -71,41 +75,37 @@ private void check(VariableTree variable, List<StatementTree> body, int bodySize
7175
}
7276

7377
private static class ReferenceVisitor extends BaseTreeVisitor {
74-
Symbol symbol;
75-
boolean referenceSymbol;
78+
private final Symbol symbol;
79+
boolean referencesSymbol;
7680
boolean hasBreakingStatement;
7781

7882
ReferenceVisitor(Symbol symbol) {
7983
this.symbol = symbol;
8084
}
8185

8286
void visit(StatementTree node) {
83-
referenceSymbol = false;
87+
referencesSymbol = false;
8488
hasBreakingStatement = false;
8589
node.accept(this);
8690
}
8791

8892
@Override
8993
public void visitReturnStatement(ReturnStatementTree tree) {
90-
if (!hasBreakingStatement) {
91-
hasBreakingStatement = true;
92-
}
94+
hasBreakingStatement = true;
9395
super.visitReturnStatement(tree);
9496
}
9597

9698
@Override
9799
public void visitThrowStatement(ThrowStatementTree tree) {
98-
if (!hasBreakingStatement) {
99-
hasBreakingStatement = true;
100-
}
100+
hasBreakingStatement = true;
101101
super.visitThrowStatement(tree);
102102
}
103103

104104
@Override
105105
public void visitIdentifier(IdentifierTree tree) {
106-
if (!referenceSymbol && symbol.equals(tree.symbol())) {
107-
referenceSymbol = true;
108-
}
106+
referencesSymbol |= symbol.equals(tree.symbol());
107+
// If the symbol is "Unknown" and has same name, we assume it's the same to avoid FPs.
108+
referencesSymbol |= tree.symbol().isUnknown() && symbol.name().equals(tree.name());
109109
super.visitIdentifier(tree);
110110
}
111111
}

java-checks/src/test/files/checks/VariableDeclarationScopeCheck.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import java.util.List;
2+
import java.util.ArrayList;
3+
14
abstract class A {
25

36
void m1();
@@ -55,4 +58,23 @@ void unused() {
5558
return;
5659
}
5760
}
61+
62+
void withLambdaUsingVar() {
63+
List<String> outsideLambda = new ArrayList<>(); // Compliant: this cannot be declared anywhere else
64+
Collections.singletonList(23).stream.map(x -> {
65+
outsideLambda.add(x);
66+
return x;
67+
})
68+
.collect(Collectors.toList());
69+
return outsideLambda;
70+
}
71+
72+
void withLambdaNotUsingVar() {
73+
List<String> outsideLambda = new ArrayList<>(); // Noncompliant
74+
Collections.singletonList(23).stream.map(x -> {
75+
return x;
76+
})
77+
.collect(Collectors.toList());
78+
return outsideLambda;
79+
}
5880
}

0 commit comments

Comments
 (0)