Skip to content

Commit c1ac90b

Browse files
timtebeekclaude
andauthored
Fix UseVarForGenericMethodInvocations for nested generics with diamond operators (#863)
* Fix UseVarForGenericMethodInvocations to handle nested generics with diamond operators When converting to var, the recipe now makes nested generic type parameters explicit by replacing diamond operators (<>) with the actual type parameters from the variable declaration. This prevents compilation errors when using method references. Fixes #862 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Slight polish * No need to extract unused empty type parameters --------- Co-authored-by: Claude <[email protected]>
1 parent 9092d22 commit c1ac90b

File tree

2 files changed

+90
-10
lines changed

2 files changed

+90
-10
lines changed

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

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,22 @@
1515
*/
1616
package org.openrewrite.java.migrate.lang.var;
1717

18+
import org.jspecify.annotations.Nullable;
1819
import org.openrewrite.ExecutionContext;
1920
import org.openrewrite.Preconditions;
2021
import org.openrewrite.Recipe;
2122
import org.openrewrite.TreeVisitor;
23+
import org.openrewrite.internal.ListUtils;
2224
import org.openrewrite.java.JavaIsoVisitor;
2325
import org.openrewrite.java.search.UsesJavaVersion;
2426
import org.openrewrite.java.tree.Expression;
2527
import org.openrewrite.java.tree.J;
2628
import org.openrewrite.java.tree.JavaType;
29+
import org.openrewrite.java.tree.TypeTree;
30+
31+
import java.util.List;
32+
33+
import static java.util.Objects.requireNonNull;
2734

2835
public class UseVarForGenericMethodInvocations extends Recipe {
2936
@Override
@@ -36,7 +43,7 @@ public String getDisplayName() {
3643
public String getDescription() {
3744
//language=markdown
3845
return "Apply `var` to variables initialized by invocations of generic methods. " +
39-
"This recipe ignores generic factory methods without parameters, because open rewrite cannot handle them correctly ATM.";
46+
"This recipe ignores generic factory methods without parameters, because open rewrite cannot handle them correctly ATM.";
4047
}
4148

4249
@Override
@@ -82,16 +89,53 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
8289
maybeRemoveImport((JavaType.FullyQualified) vd.getType());
8390
}
8491

85-
return DeclarationCheck.transformToVar(vd);
86-
// TODO implement to support cases like `var strs = List.<String>of();`
87-
/*J.VariableDeclarations finalVd = vd;
88-
return DeclarationCheck.<J.MethodInvocation>transformToVar(vd, it -> {
89-
// If left has generics but right has not, copy types parameters
90-
if (finalVd.getTypeExpression() instanceof J.ParameterizedType && !((J.ParameterizedType) finalVd.getTypeExpression()).getTypeParameters().isEmpty() && it.getTypeParameters() == null) {
91-
return it.withTypeParameters(((J.ParameterizedType) finalVd.getTypeExpression()).getPadding().getTypeParameters());
92+
// Make nested generic types explicit before converting to var
93+
J.VariableDeclarations finalVd = vd;
94+
return DeclarationCheck.transformToVar(vd, (J.MethodInvocation mi) -> makeNestedGenericsExplicit(mi, finalVd));
95+
}
96+
97+
/**
98+
* Makes nested generic types explicit by replacing diamond operators in constructor calls
99+
* with explicit type parameters based on the variable declaration type.
100+
*/
101+
private J.MethodInvocation makeNestedGenericsExplicit(J.MethodInvocation mi, J.VariableDeclarations vd) {
102+
// Extract type parameters from the variable declaration
103+
if (!(vd.getTypeExpression() instanceof J.ParameterizedType)) {
104+
return mi;
105+
}
106+
107+
List<Expression> leftTypeParams = ((J.ParameterizedType) vd.getTypeExpression()).getTypeParameters();
108+
if (leftTypeParams == null || leftTypeParams.isEmpty()) {
109+
return mi;
110+
}
111+
112+
// Visit arguments and replace diamond operators with explicit type parameters
113+
return mi.withArguments(ListUtils.map(mi.getArguments(), arg -> {
114+
if (arg instanceof J.NewClass) {
115+
J.NewClass newClass = (J.NewClass) arg;
116+
// Check if using diamond operator (rightTypeParams is empty)
117+
if (!hasTypeParams(newClass.getClazz())) {
118+
// Copy type parameters from left side to right side
119+
J.ParameterizedType rightType = (J.ParameterizedType) newClass.getClazz();
120+
return newClass.withClazz(requireNonNull(rightType).withTypeParameters(leftTypeParams));
121+
}
122+
}
123+
return arg;
124+
}));
125+
}
126+
127+
private static boolean hasTypeParams(@Nullable TypeTree clazz) {
128+
if (clazz instanceof J.ParameterizedType) {
129+
List<Expression> typeParameters = ((J.ParameterizedType) clazz).getTypeParameters();
130+
if (typeParameters != null) {
131+
for (Expression curType : typeParameters) {
132+
if (curType.getType() != null) {
133+
return true;
134+
}
135+
}
92136
}
93-
return it;
94-
});*/
137+
}
138+
return false;
95139
}
96140

97141
private static boolean allArgumentsEmpty(J.MethodInvocation invocation) {

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.junit.jupiter.api.Nested;
1919
import org.junit.jupiter.api.Test;
2020
import org.openrewrite.DocumentExample;
21+
import org.openrewrite.Issue;
2122
import org.openrewrite.test.RecipeSpec;
2223
import org.openrewrite.test.RewriteTest;
2324

@@ -259,5 +260,40 @@ void m() {
259260
)
260261
);
261262
}
263+
264+
@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/862")
265+
@Test
266+
void nestedGenericsSimple() {
267+
//language=java
268+
rewriteRun(
269+
version(
270+
java(
271+
"""
272+
import java.util.ArrayList;
273+
import java.util.Collections;
274+
import java.util.List;
275+
276+
class A {
277+
void dostuff() {
278+
List<String> strs = Collections.synchronizedList(new ArrayList<>());
279+
}
280+
}
281+
""",
282+
"""
283+
import java.util.ArrayList;
284+
import java.util.Collections;
285+
import java.util.List;
286+
287+
class A {
288+
void dostuff() {
289+
var strs = Collections.synchronizedList(new ArrayList<String>());
290+
}
291+
}
292+
"""
293+
),
294+
10
295+
)
296+
);
297+
}
262298
}
263299
}

0 commit comments

Comments
 (0)