Skip to content

Commit 3b7b950

Browse files
Fix ClassCastException on parenthesized initializers in var recipes (#1064)
`UseVarForConstructors` and `UseVarForGenericMethodInvocations` unwrap the initializer for local `instanceof` checks but pass the original `VariableDeclarations` (still wrapping `J.Parentheses`) to downstream casts/`transformToVar`, which re-fetch and cast the raw initializer. Rewrite the declaration to use the unwrapped initializer (preserving the original prefix) before handing it off.
1 parent 461609f commit 3b7b950

4 files changed

Lines changed: 78 additions & 9 deletions

File tree

src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForConstructors.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.openrewrite.Preconditions;
2222
import org.openrewrite.Recipe;
2323
import org.openrewrite.TreeVisitor;
24+
import org.openrewrite.internal.ListUtils;
2425
import org.openrewrite.java.JavaIsoVisitor;
2526
import org.openrewrite.java.search.UsesJavaVersion;
2627
import org.openrewrite.java.tree.*;
@@ -57,11 +58,11 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
5758
return vd;
5859
}
5960

60-
Expression initializer = vd.getVariables().get(0).getInitializer();
61-
if (initializer == null) {
61+
Expression originalInitializer = vd.getVariables().get(0).getInitializer();
62+
if (originalInitializer == null) {
6263
return vd;
6364
}
64-
initializer = initializer.unwrap();
65+
Expression initializer = originalInitializer.unwrap();
6566

6667
// Only transform constructor calls
6768
if (!(initializer instanceof J.NewClass)) {
@@ -77,7 +78,13 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
7778
maybeRemoveImport((JavaType.FullyQualified) vd.getType());
7879
}
7980

80-
return DeclarationCheck.transformToVar(vd, (J.NewClass nc) -> maybeTransferTypeArguments(vd, nc));
81+
if (initializer != originalInitializer) {
82+
Expression unwrapped = initializer.withPrefix(originalInitializer.getPrefix());
83+
vd = vd.withVariables(ListUtils.mapFirst(vd.getVariables(), nv -> nv.withInitializer(unwrapped)));
84+
}
85+
86+
J.VariableDeclarations finalVd = vd;
87+
return DeclarationCheck.transformToVar(vd, (J.NewClass nc) -> maybeTransferTypeArguments(finalVd, nc));
8188
}
8289

8390
private J.NewClass maybeTransferTypeArguments(J.VariableDeclarations vd, J.NewClass initializer) {

src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocations.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,15 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
6666
}
6767

6868
// Now we deal with generics, check for method invocations
69-
Expression initializer = vd.getVariables().get(0).getInitializer();
70-
boolean isMethodInvocation = initializer != null && initializer.unwrap() instanceof J.MethodInvocation;
71-
if (!isMethodInvocation) {
69+
Expression originalInitializer = vd.getVariables().get(0).getInitializer();
70+
if (originalInitializer == null || !(originalInitializer.unwrap() instanceof J.MethodInvocation)) {
7271
return vd;
7372
}
73+
J.MethodInvocation invocation = (J.MethodInvocation) originalInitializer.unwrap();
7474

7575
// If no type parameters and no arguments are present, we assume the type is too hard to determine
76-
boolean hasNoTypeParams = ((J.MethodInvocation) initializer).getTypeParameters() == null;
77-
boolean argumentsEmpty = allArgumentsEmpty((J.MethodInvocation) initializer);
76+
boolean hasNoTypeParams = invocation.getTypeParameters() == null;
77+
boolean argumentsEmpty = allArgumentsEmpty(invocation);
7878
if (hasNoTypeParams && argumentsEmpty) {
7979
return vd;
8080
}
@@ -83,6 +83,11 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
8383
maybeRemoveImport((JavaType.FullyQualified) vd.getType());
8484
}
8585

86+
if (invocation != originalInitializer) {
87+
J.MethodInvocation unwrapped = invocation.withPrefix(originalInitializer.getPrefix());
88+
vd = vd.withVariables(ListUtils.mapFirst(vd.getVariables(), nv -> nv.withInitializer(unwrapped)));
89+
}
90+
8691
// Make nested generic types explicit before converting to var
8792
J.VariableDeclarations finalVd = vd;
8893
return DeclarationCheck.transformToVar(vd, (J.MethodInvocation mi) -> makeNestedGenericsExplicit(mi, finalVd));

src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForConstructorsTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,33 @@ void test() {
184184
);
185185
}
186186

187+
@Test
188+
void parenthesizedConstructorInitializer() {
189+
rewriteRun(
190+
//language=java
191+
java(
192+
"""
193+
import java.util.ArrayList;
194+
195+
class A {
196+
void m() {
197+
ArrayList<String> list = (new ArrayList<String>());
198+
}
199+
}
200+
""",
201+
"""
202+
import java.util.ArrayList;
203+
204+
class A {
205+
void m() {
206+
var list = new ArrayList<String>();
207+
}
208+
}
209+
"""
210+
)
211+
);
212+
}
213+
187214
@Test
188215
void doNotReplaceInvalidPatterns() {
189216
rewriteRun(

src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocationsTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,36 @@ void m() {
395395
);
396396
}
397397

398+
@Test
399+
void parenthesizedMethodInvocationInitializer() {
400+
//language=java
401+
rewriteRun(
402+
version(
403+
java(
404+
"""
405+
import java.util.List;
406+
407+
class A {
408+
void m() {
409+
List<String> strs = (List.of("one", "two"));
410+
}
411+
}
412+
""",
413+
"""
414+
import java.util.List;
415+
416+
class A {
417+
void m() {
418+
var strs = List.of("one", "two");
419+
}
420+
}
421+
"""
422+
),
423+
10
424+
)
425+
);
426+
}
427+
398428
@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/868")
399429
@Test
400430
void genericsCollectorsRegression() {

0 commit comments

Comments
 (0)