Skip to content

Commit 92c1f8b

Browse files
authored
Do not remove unused variables with constructor calls (#147)
* Do not remove unused variables with constructor calls * Also visitNewClass for side effects * Document another uncovered case
1 parent 60c789d commit 92c1f8b

File tree

2 files changed

+97
-28
lines changed

2 files changed

+97
-28
lines changed

src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,19 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
8080
private Cursor getCursorToParentScope(Cursor cursor) {
8181
return cursor.dropParentUntil(is ->
8282
is instanceof J.ClassDeclaration ||
83-
is instanceof J.Block ||
84-
is instanceof J.MethodDeclaration ||
85-
is instanceof J.ForLoop ||
86-
is instanceof J.ForEachLoop ||
87-
is instanceof J.ForLoop.Control ||
88-
is instanceof J.ForEachLoop.Control ||
89-
is instanceof J.Case ||
90-
is instanceof J.Try ||
91-
is instanceof J.Try.Resource ||
92-
is instanceof J.Try.Catch ||
93-
is instanceof J.MultiCatch ||
94-
is instanceof J.Lambda ||
95-
is instanceof JavaSourceFile
83+
is instanceof J.Block ||
84+
is instanceof J.MethodDeclaration ||
85+
is instanceof J.ForLoop ||
86+
is instanceof J.ForEachLoop ||
87+
is instanceof J.ForLoop.Control ||
88+
is instanceof J.ForEachLoop.Control ||
89+
is instanceof J.Case ||
90+
is instanceof J.Try ||
91+
is instanceof J.Try.Resource ||
92+
is instanceof J.Try.Catch ||
93+
is instanceof J.MultiCatch ||
94+
is instanceof J.Lambda ||
95+
is instanceof JavaSourceFile
9696
);
9797
}
9898

@@ -106,20 +106,20 @@ public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations
106106
Cursor parentScope = getCursorToParentScope(getCursor());
107107
J parent = parentScope.getValue();
108108
if (parentScope.getParent() == null ||
109-
// skip class instance variables. parentScope.getValue() covers java records.
110-
parentScope.getParent().getValue() instanceof J.ClassDeclaration || parentScope.getValue() instanceof J.ClassDeclaration ||
111-
// skip anonymous class instance variables
112-
parentScope.getParent().getValue() instanceof J.NewClass ||
113-
// skip if method declaration parameter
114-
parent instanceof J.MethodDeclaration ||
115-
// skip if defined in an enhanced or standard for loop, since there isn't much we can do about the semantics at that point
116-
parent instanceof J.ForLoop.Control || parent instanceof J.ForEachLoop.Control ||
117-
// skip if defined in a try's catch clause as an Exception variable declaration
118-
parent instanceof J.Try.Resource || parent instanceof J.Try.Catch || parent instanceof J.MultiCatch ||
119-
// skip if defined as a parameter to a lambda expression
120-
parent instanceof J.Lambda ||
121-
// skip if the initializer may have a side effect
122-
initializerMightSideEffect(variable)
109+
// skip class instance variables. parentScope.getValue() covers java records.
110+
parentScope.getParent().getValue() instanceof J.ClassDeclaration || parentScope.getValue() instanceof J.ClassDeclaration ||
111+
// skip anonymous class instance variables
112+
parentScope.getParent().getValue() instanceof J.NewClass ||
113+
// skip if method declaration parameter
114+
parent instanceof J.MethodDeclaration ||
115+
// skip if defined in an enhanced or standard for loop, since there isn't much we can do about the semantics at that point
116+
parent instanceof J.ForLoop.Control || parent instanceof J.ForEachLoop.Control ||
117+
// skip if defined in a try's catch clause as an Exception variable declaration
118+
parent instanceof J.Try.Resource || parent instanceof J.Try.Catch || parent instanceof J.MultiCatch ||
119+
// skip if defined as a parameter to a lambda expression
120+
parent instanceof J.Lambda ||
121+
// skip if the initializer may have a side effect
122+
initializerMightSideEffect(variable)
123123
) {
124124
return variable;
125125
}
@@ -179,6 +179,12 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInvocat
179179
return methodInvocation;
180180
}
181181

182+
@Override
183+
public J.NewClass visitNewClass(J.NewClass newClass, AtomicBoolean result) {
184+
result.set(true);
185+
return newClass;
186+
}
187+
182188
@Override
183189
public J.Assignment visitAssignment(J.Assignment assignment, AtomicBoolean result) {
184190
result.set(true);
@@ -219,9 +225,10 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation m, ExecutionC
219225
@EqualsAndHashCode(callSuper = true)
220226
private static class AssignmentToLiteral extends JavaVisitor<ExecutionContext> {
221227
J.Assignment assignment;
228+
222229
@Override
223230
public J visitAssignment(J.Assignment a, ExecutionContext executionContext) {
224-
if(assignment.isScope(a)) {
231+
if (assignment.isScope(a)) {
225232
return a.getAssignment().withPrefix(a.getPrefix());
226233
}
227234
return a;

src/test/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariablesTest.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
*/
1616
package org.openrewrite.staticanalysis;
1717

18+
import org.junit.jupiter.api.Nested;
1819
import org.junit.jupiter.api.Test;
20+
import org.junitpioneer.jupiter.ExpectedToFail;
1921
import org.openrewrite.DocumentExample;
2022
import org.openrewrite.Issue;
2123
import org.openrewrite.test.RecipeSpec;
@@ -1011,4 +1013,64 @@ fun foo() {
10111013
)
10121014
);
10131015
}
1016+
1017+
@Test
1018+
void retainJavaUnusedLocalVariableWithNewClass() {
1019+
rewriteRun(
1020+
java(
1021+
"""
1022+
class A {}
1023+
class B {
1024+
void foo() {
1025+
A a = new A();
1026+
}
1027+
}
1028+
"""
1029+
)
1030+
);
1031+
}
1032+
1033+
@Nested
1034+
class Kotlin {
1035+
1036+
@Test
1037+
void retainUnusedLocalVariableWithNewClass() {
1038+
rewriteRun(
1039+
kotlin(
1040+
"""
1041+
class A {}
1042+
class B {
1043+
fun foo() {
1044+
val a = A();
1045+
}
1046+
}
1047+
"""
1048+
)
1049+
);
1050+
}
1051+
1052+
@Test
1053+
@ExpectedToFail("Not yet implemented")
1054+
void retainUnusedLocalVariableConst() {
1055+
rewriteRun(
1056+
kotlin(
1057+
"""
1058+
package constants
1059+
const val FOO = "bar"
1060+
"""
1061+
),
1062+
kotlin(
1063+
"""
1064+
package config
1065+
import constants.FOO
1066+
fun baz() {
1067+
val foo = FOO
1068+
println(foo)
1069+
}
1070+
"""
1071+
)
1072+
);
1073+
}
1074+
1075+
}
10141076
}

0 commit comments

Comments
 (0)