From 83b7c5a3ecc99e68e72cb936d4fad1a67d8b7759 Mon Sep 17 00:00:00 2001 From: Jago de Vreede Date: Wed, 8 Oct 2025 21:24:11 +0200 Subject: [PATCH 1/3] Added tests for issue #868 --- ...UseVarForGenericMethodInvocationsTest.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocationsTest.java b/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocationsTest.java index 436fdce1b9..95d3706f7a 100644 --- a/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocationsTest.java +++ b/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocationsTest.java @@ -295,5 +295,79 @@ void dostuff() { ) ); } + + @Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/868") + @Test + void applyVarAndAddExplicitTypeArgumentForNestedGenericFactoryMethod() { + //language=java + rewriteRun( + version( + java( + """ + public class A { + void getX(final Root root) { + Path x = root.get("x"); + } + } + interface Root{ + Path get(String var1); + } + interface Path { } + """, + """ + public class A { + void getX(final Root root) { + var x = root.get("x"); + } + } + interface Root{ + Path get(String var1); + } + interface Path { } + """ + ), + 10 + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/868") + @Test + void genericsCollectorsRegression() { + //language=java + rewriteRun( + version( + java( + """ + import java.time.LocalDate; + import java.util.ArrayList; + import java.util.Set; + import java.util.stream.Collectors; + + public class A { + void getX() { + Set strDates = new ArrayList().stream() + .map(date -> date.toString()).collect(Collectors.toSet()); + } + } + """, + """ + import java.time.LocalDate; + import java.util.ArrayList; + import java.util.Set; + import java.util.stream.Collectors; + + public class A { + void getX() { + var strDates = new ArrayList().stream() + .map(date -> date.toString()).collect(Collectors.toSet()); + } + } + """ + ), + 10 + ) + ); + } } } From 29ea116723798cc8b0bd244182ea0e574d22d51a Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 10 Oct 2025 16:39:34 +0200 Subject: [PATCH 2/3] Fix tests --- .../UseVarForGenericMethodInvocations.java | 41 ++++++++++++++++--- ...UseVarForGenericMethodInvocationsTest.java | 4 +- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocations.java b/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocations.java index 4e352df206..df1378cd34 100644 --- a/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocations.java +++ b/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocations.java @@ -23,11 +23,10 @@ import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.search.UsesJavaVersion; -import org.openrewrite.java.tree.Expression; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.JavaType; -import org.openrewrite.java.tree.TypeTree; +import org.openrewrite.java.tree.*; +import org.openrewrite.marker.Markers; +import java.util.ArrayList; import java.util.List; import static java.util.Objects.requireNonNull; @@ -97,6 +96,7 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v /** * Makes nested generic types explicit by replacing diamond operators in constructor calls * with explicit type parameters based on the variable declaration type. + * Also adds explicit type parameters to the method invocation itself when needed. */ private J.MethodInvocation makeNestedGenericsExplicit(J.MethodInvocation mi, J.VariableDeclarations vd) { // Extract type parameters from the variable declaration @@ -109,8 +109,23 @@ private J.MethodInvocation makeNestedGenericsExplicit(J.MethodInvocation mi, J.V return mi; } + // Check if method invocation needs explicit type parameters + // This happens when the method is generic and the return type's type parameter + // matches a type parameter from the declaring class + J.MethodInvocation result = mi; + if (mi.getTypeParameters() == null && mi.getMethodType() != null && containsGenericTypeVariable(mi.getMethodType().getReturnType())) { + // Create JRightPadded list from leftTypeParams + List> typeParamsList = new ArrayList<>(); + for (Expression typeParam : leftTypeParams) { + typeParamsList.add(JRightPadded.build(typeParam)); + } + result = mi.getPadding().withTypeParameters( + JContainer.build(Space.EMPTY, typeParamsList, Markers.EMPTY) + ); + } + // Visit arguments and replace diamond operators with explicit type parameters - return mi.withArguments(ListUtils.map(mi.getArguments(), arg -> { + return result.withArguments(ListUtils.map(result.getArguments(), arg -> { if (arg instanceof J.NewClass) { J.NewClass newClass = (J.NewClass) arg; // Check if using diamond operator (rightTypeParams is empty) @@ -124,6 +139,22 @@ private J.MethodInvocation makeNestedGenericsExplicit(J.MethodInvocation mi, J.V })); } + private boolean containsGenericTypeVariable(JavaType type) { + if (type instanceof JavaType.GenericTypeVariable) { + return true; + } + + if (type instanceof JavaType.Parameterized) { + for (JavaType typeParam : ((JavaType.Parameterized) type).getTypeParameters()) { + if (containsGenericTypeVariable(typeParam)) { + return true; + } + } + } + + return false; + } + private static boolean hasTypeParams(@Nullable TypeTree clazz) { if (clazz instanceof J.ParameterizedType) { List typeParameters = ((J.ParameterizedType) clazz).getTypeParameters(); diff --git a/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocationsTest.java b/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocationsTest.java index 95d3706f7a..85e4068b35 100644 --- a/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocationsTest.java +++ b/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocationsTest.java @@ -304,7 +304,7 @@ void applyVarAndAddExplicitTypeArgumentForNestedGenericFactoryMethod() { version( java( """ - public class A { + class A { void getX(final Root root) { Path x = root.get("x"); } @@ -315,7 +315,7 @@ interface Root{ interface Path { } """, """ - public class A { + class A { void getX(final Root root) { var x = root.get("x"); } From 32816b616d0a999e15deca512b220aa611fe8544 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 10 Oct 2025 16:46:16 +0200 Subject: [PATCH 3/3] Minimize implementation --- .../lang/var/UseVarForGenericMethodInvocations.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocations.java b/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocations.java index df1378cd34..460787709e 100644 --- a/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocations.java +++ b/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocations.java @@ -109,23 +109,18 @@ private J.MethodInvocation makeNestedGenericsExplicit(J.MethodInvocation mi, J.V return mi; } - // Check if method invocation needs explicit type parameters - // This happens when the method is generic and the return type's type parameter - // matches a type parameter from the declaring class - J.MethodInvocation result = mi; + // Add explicit type parameters when the method is generic and the return type's type parameter matches a type parameter from the declaring class if (mi.getTypeParameters() == null && mi.getMethodType() != null && containsGenericTypeVariable(mi.getMethodType().getReturnType())) { // Create JRightPadded list from leftTypeParams List> typeParamsList = new ArrayList<>(); for (Expression typeParam : leftTypeParams) { typeParamsList.add(JRightPadded.build(typeParam)); } - result = mi.getPadding().withTypeParameters( - JContainer.build(Space.EMPTY, typeParamsList, Markers.EMPTY) - ); + mi = mi.withTypeParameters(JContainer.build(Space.EMPTY, typeParamsList, Markers.EMPTY)); } // Visit arguments and replace diamond operators with explicit type parameters - return result.withArguments(ListUtils.map(result.getArguments(), arg -> { + return mi.withArguments(ListUtils.map(mi.getArguments(), arg -> { if (arg instanceof J.NewClass) { J.NewClass newClass = (J.NewClass) arg; // Check if using diamond operator (rightTypeParams is empty)