Skip to content

Commit 2247e49

Browse files
SONARJAVA-5430 Check Java version in case of foreach variable (#5091)
The quickfix will only work starting with Java 22. We should not raise an issue before that.
1 parent 38db436 commit 2247e49

5 files changed

Lines changed: 54 additions & 28 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"ruleKey": "S1128",
33
"hasTruePositives": true,
4-
"falseNegatives": 36,
4+
"falseNegatives": 33,
55
"falsePositives": 0
66
}

java-checks-test-sources/default/src/main/java/checks/unused/UnusedLocalVariableCheck.java

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,12 @@
11
package checks.unused;
22

3-
import org.hibernate.validator.internal.engine.validationcontext.ValidatorScopedContext;
4-
5-
import java.util.Arrays;
6-
import java.util.List;
73
import java.util.Queue;
84
import java.util.function.BinaryOperator;
95
import java.util.function.Function;
106
import java.util.function.UnaryOperator;
117
import java.util.stream.Collectors;
128
import java.util.stream.Stream;
139

14-
class UnusedLocalVariable {
15-
private UnusedLocalVariable() {
16-
}
17-
18-
public static int count(int[] elements) {
19-
int count = 0;
20-
for (int element : elements) { // Noncompliant[[quickfixes=qf_ulv]]
21-
// ^^^^^^^
22-
// fix@qf_ulv {{Replace unused local variable with _}}
23-
// edit@qf_ulv [[sc=10;ec=21]]{{var _}}
24-
count++;
25-
}
26-
return count;
27-
}
28-
}
29-
3010
class UnusedLocalVariableCheck {
3111

3212
int unusedField;
@@ -65,12 +45,6 @@ public void f(int unusedParameter, Object o) {
6545
} catch (Exception _) {
6646
}
6747

68-
for (int a : new int[]{0, 1, 2}) { // Noncompliant[[quickfixes=qf_f1]]
69-
// ^
70-
// fix@qf_f1 {{Replace unused local variable with _}}
71-
// edit@qf_f1 [[sc=10;ec=15]]{{var _}}
72-
}
73-
7448
for (int i = 0; condition(); i++) { // Noncompliant
7549
}
7650

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package checks.unused;
2+
3+
public class UnusedLocalVariableCheck_java22 {
4+
private UnusedLocalVariableCheck_java22() {}
5+
6+
public static int count(int[] elements) {
7+
int count = 0;
8+
for (int element : elements) { // Noncompliant[[quickfixes=qf_ulv]]
9+
// ^^^^^^^
10+
// fix@qf_ulv {{Replace unused local variable with _}}
11+
// edit@qf_ulv [[sc=10;ec=21]]{{var _}}
12+
count++;
13+
}
14+
15+
for (int a : new int[]{0, 1, 2}) { // Noncompliant[[quickfixes=qf_f1]]
16+
// ^
17+
// fix@qf_f1 {{Replace unused local variable with _}}
18+
// edit@qf_f1 [[sc=10;ec=15]]{{var _}}
19+
count++;
20+
}
21+
22+
return count;
23+
}
24+
}

java-checks/src/main/java/org/sonar/java/checks/unused/UnusedLocalVariableCheck.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.sonar.java.reporting.JavaQuickFix;
3030
import org.sonar.java.reporting.JavaTextEdit;
3131
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
32+
import org.sonar.plugins.java.api.JavaVersion;
3233
import org.sonar.plugins.java.api.semantic.Symbol;
3334
import org.sonar.plugins.java.api.tree.AssignmentExpressionTree;
3435
import org.sonar.plugins.java.api.tree.CaseLabelTree;
@@ -75,7 +76,7 @@ public void leaveNode(Tree tree) {
7576
IdentifierTree simpleName = variable.simpleName();
7677
if (!simpleName.isUnnamedVariable()) {
7778
boolean unresolved = UNRESOLVED_IDENTIFIERS_AND_SWITCH_CASE_VISITOR.isUnresolved(simpleName.name());
78-
if (!unresolved && isProperLocalVariable(variable) && isUnused(variable.symbol())) {
79+
if (!unresolved && isProperLocalVariable(variable) && isUnused(variable.symbol()) && !isMandatoryForeachVariable(context.getJavaVersion(), variable)) {
7980
QuickFixHelper.newIssue(context)
8081
.forRule(this)
8182
.onTree(simpleName)
@@ -88,6 +89,14 @@ public void leaveNode(Tree tree) {
8889

8990
}
9091

92+
/**
93+
* Before Java 22 it was not possible to remove the variable in a foreach statement even it is unused.
94+
* For instance in {@code for (String element : list) {}}, it is only since Java 22 that it can be rewritten {@code for (var _ : list) {}}.
95+
*/
96+
private static boolean isMandatoryForeachVariable(JavaVersion javaVersion, VariableTree variable) {
97+
return variable.parent() instanceof ForEachStatement && !javaVersion.isJava22Compatible();
98+
}
99+
91100
private static boolean isUnused(Symbol symbol) {
92101
return symbol.usages().stream().noneMatch(UnusedLocalVariableCheck::isRValue);
93102
}

java-checks/src/test/java/org/sonar/java/checks/unused/UnusedLocalVariableCheckTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,25 @@ void test() {
3131
.verifyIssues();
3232
}
3333

34+
@Test
35+
void test_java22() {
36+
CheckVerifier.newVerifier()
37+
.onFile(TestUtils.mainCodeSourcesPath("checks/unused/UnusedLocalVariableCheck_java22.java"))
38+
.withCheck(new UnusedLocalVariableCheck())
39+
.withJavaVersion(22)
40+
.verifyIssues();
41+
}
42+
43+
/** Check that issue that can only be acted upon with Java 22 are not raised for earlier versions. */
44+
@Test
45+
void test_java21() {
46+
CheckVerifier.newVerifier()
47+
.onFile(TestUtils.mainCodeSourcesPath("checks/unused/UnusedLocalVariableCheck_java22.java"))
48+
.withCheck(new UnusedLocalVariableCheck())
49+
.withJavaVersion(21)
50+
.verifyNoIssues();
51+
}
52+
3453
@Test
3554
void test_non_compiling() {
3655
CheckVerifier.newVerifier()

0 commit comments

Comments
 (0)