Skip to content

Commit 1a07e7f

Browse files
JohannisKtimtebeek
andauthored
Java 25: Unused variable to underscore (#844)
* initial * Implementation * polish * Update ReplaceUnusedVariablesWithUnderscore.java * Apply formatter and inline named visitor * Adopt `VariableReferences.findRhsReferences` * Leverage existing `RenameVariable` * Add additional tests and handling * Override individual visit methods and only evaluate params * Simplify logic * Remove generic type --------- Co-authored-by: Tim te Beek <[email protected]>
1 parent 804c6a9 commit 1a07e7f

File tree

4 files changed

+556
-1
lines changed

4 files changed

+556
-1
lines changed

.sdkmanrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Enable auto-env through the sdkman_auto_env config
22
# Add key=value pairs of SDKs to use below
3-
java=21.0.5-tem
3+
java=21.0.7-tem
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.lang;
17+
18+
import lombok.EqualsAndHashCode;
19+
import lombok.Value;
20+
import org.openrewrite.ExecutionContext;
21+
import org.openrewrite.Preconditions;
22+
import org.openrewrite.Recipe;
23+
import org.openrewrite.TreeVisitor;
24+
import org.openrewrite.java.JavaIsoVisitor;
25+
import org.openrewrite.java.RenameVariable;
26+
import org.openrewrite.java.search.SemanticallyEqual;
27+
import org.openrewrite.java.search.UsesJavaVersion;
28+
import org.openrewrite.java.tree.J;
29+
import org.openrewrite.java.tree.Statement;
30+
import org.openrewrite.staticanalysis.VariableReferences;
31+
32+
import java.util.concurrent.atomic.AtomicBoolean;
33+
34+
@EqualsAndHashCode(callSuper = false)
35+
@Value
36+
public class ReplaceUnusedVariablesWithUnderscore extends Recipe {
37+
38+
private static final String UNDERSCORE = "_";
39+
40+
@Override
41+
public String getDisplayName() {
42+
return "Replace unused variables with underscore";
43+
}
44+
45+
@Override
46+
public String getDescription() {
47+
return "Replace unused variable declarations with underscore (_) for Java 22+. " +
48+
"This includes unused variables in enhanced for loops, catch blocks, " +
49+
"and lambda parameters where the variable is never referenced.";
50+
}
51+
52+
@Override
53+
public TreeVisitor<?, ExecutionContext> getVisitor() {
54+
return Preconditions.check(new UsesJavaVersion<>(25), new JavaIsoVisitor<ExecutionContext>() {
55+
@Override
56+
public J.ForEachLoop visitForEachLoop(J.ForEachLoop forLoop, ExecutionContext ctx) {
57+
J.ForEachLoop l = super.visitForEachLoop(forLoop, ctx);
58+
Statement variable = l.getControl().getVariable();
59+
if (variable instanceof J.VariableDeclarations) {
60+
for (J.VariableDeclarations.NamedVariable namedVariable : ((J.VariableDeclarations) variable).getVariables()) {
61+
renameVariableIfUnusedInContext(namedVariable, l.getBody());
62+
}
63+
}
64+
return l;
65+
}
66+
67+
@Override
68+
public J.Try.Catch visitCatch(J.Try.Catch _catch, ExecutionContext ctx) {
69+
J.Try.Catch c = super.visitCatch(_catch, ctx);
70+
for (J.VariableDeclarations.NamedVariable namedVariable : c.getParameter().getTree().getVariables()) {
71+
renameVariableIfUnusedInContext(namedVariable, c.getBody());
72+
}
73+
return c;
74+
}
75+
76+
@Override
77+
public J.Lambda visitLambda(J.Lambda lambda, ExecutionContext ctx) {
78+
J.Lambda l = super.visitLambda(lambda, ctx);
79+
for (J param : l.getParameters().getParameters()) {
80+
if (param instanceof J.VariableDeclarations) {
81+
for (J.VariableDeclarations.NamedVariable namedVariable : ((J.VariableDeclarations) param).getVariables()) {
82+
renameVariableIfUnusedInContext(namedVariable, l.getBody());
83+
}
84+
}
85+
}
86+
return l;
87+
}
88+
89+
private void renameVariableIfUnusedInContext(J.VariableDeclarations.NamedVariable variable, J context) {
90+
if (!UNDERSCORE.equals(variable.getName().getSimpleName()) &&
91+
VariableReferences.findRhsReferences(context, variable.getName()).isEmpty() &&
92+
!usedInModifyingUnary(variable.getName(), context)) {
93+
doAfterVisit(new RenameVariable<>(variable, UNDERSCORE));
94+
}
95+
}
96+
97+
private boolean usedInModifyingUnary(J.Identifier identifier, J context) {
98+
return new JavaIsoVisitor<AtomicBoolean>() {
99+
@Override
100+
public J.Unary visitUnary(J.Unary unary, AtomicBoolean atomicBoolean) {
101+
if (unary.getOperator().isModifying() &&
102+
SemanticallyEqual.areEqual(identifier, unary.getExpression())) {
103+
atomicBoolean.set(true);
104+
}
105+
return super.visitUnary(unary, atomicBoolean);
106+
}
107+
}.reduce(context, new AtomicBoolean(false)).get();
108+
}
109+
});
110+
}
111+
}

src/main/resources/META-INF/rewrite/java-version-25.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ recipeList:
2828
- org.openrewrite.java.migrate.UpgradeToJava21
2929
- org.openrewrite.java.migrate.UpgradeBuildToJava25
3030
- org.openrewrite.java.migrate.lang.MigrateProcessWaitForDuration
31+
- org.openrewrite.java.migrate.lang.ReplaceUnusedVariablesWithUnderscore
3132
- org.openrewrite.java.migrate.util.MigrateInflaterDeflaterToClose
3233
- org.openrewrite.java.migrate.AccessController
3334
- org.openrewrite.java.migrate.RemoveSecurityPolicy

0 commit comments

Comments
 (0)