|
15 | 15 | */ |
16 | 16 | package org.openrewrite.java.migrate.lang.var; |
17 | 17 |
|
| 18 | +import org.jspecify.annotations.Nullable; |
18 | 19 | import org.openrewrite.ExecutionContext; |
19 | 20 | import org.openrewrite.Preconditions; |
20 | 21 | import org.openrewrite.Recipe; |
21 | 22 | import org.openrewrite.TreeVisitor; |
| 23 | +import org.openrewrite.internal.ListUtils; |
22 | 24 | import org.openrewrite.java.JavaIsoVisitor; |
23 | 25 | import org.openrewrite.java.search.UsesJavaVersion; |
24 | 26 | import org.openrewrite.java.tree.Expression; |
25 | 27 | import org.openrewrite.java.tree.J; |
26 | 28 | import org.openrewrite.java.tree.JavaType; |
| 29 | +import org.openrewrite.java.tree.TypeTree; |
| 30 | + |
| 31 | +import java.util.ArrayList; |
| 32 | +import java.util.List; |
27 | 33 |
|
28 | 34 | public class UseVarForGenericMethodInvocations extends Recipe { |
29 | 35 | @Override |
@@ -82,16 +88,65 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v |
82 | 88 | maybeRemoveImport((JavaType.FullyQualified) vd.getType()); |
83 | 89 | } |
84 | 90 |
|
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()); |
| 91 | + // Make nested generic types explicit before converting to var |
| 92 | + J.VariableDeclarations finalVd = vd; |
| 93 | + return DeclarationCheck.transformToVar(vd, (J.MethodInvocation mi) -> makeNestedGenericsExplicit(mi, finalVd)); |
| 94 | + } |
| 95 | + |
| 96 | + /** |
| 97 | + * Makes nested generic types explicit by replacing diamond operators in constructor calls |
| 98 | + * with explicit type parameters based on the variable declaration type. |
| 99 | + */ |
| 100 | + private J.MethodInvocation makeNestedGenericsExplicit(J.MethodInvocation mi, J.VariableDeclarations vd) { |
| 101 | + // Extract type parameters from the variable declaration |
| 102 | + if (!(vd.getTypeExpression() instanceof J.ParameterizedType)) { |
| 103 | + return mi; |
| 104 | + } |
| 105 | + |
| 106 | + J.ParameterizedType leftType = (J.ParameterizedType) vd.getTypeExpression(); |
| 107 | + List<Expression> leftTypeParams = leftType.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 | + List<JavaType> rightTypeParams = extractJavaTypes(newClass); |
| 117 | + // Check if using diamond operator (rightTypeParams is empty) |
| 118 | + if (rightTypeParams != null && rightTypeParams.isEmpty() && newClass.getClazz() instanceof J.ParameterizedType) { |
| 119 | + // Copy type parameters from left side to right side |
| 120 | + J.ParameterizedType rightType = (J.ParameterizedType) newClass.getClazz(); |
| 121 | + return newClass.withClazz( |
| 122 | + rightType.withTypeParameters(leftTypeParams) |
| 123 | + ); |
| 124 | + } |
92 | 125 | } |
93 | | - return it; |
94 | | - });*/ |
| 126 | + return arg; |
| 127 | + })); |
| 128 | + } |
| 129 | + |
| 130 | + /** |
| 131 | + * Extract JavaTypes from a NewClass expression's type parameters. |
| 132 | + * Returns null if not a parameterized type, or an empty list for diamond operator. |
| 133 | + */ |
| 134 | + private @Nullable List<JavaType> extractJavaTypes(J.NewClass newClass) { |
| 135 | + TypeTree clazz = newClass.getClazz(); |
| 136 | + if (clazz instanceof J.ParameterizedType) { |
| 137 | + List<Expression> typeParameters = ((J.ParameterizedType) clazz).getTypeParameters(); |
| 138 | + List<JavaType> params = new ArrayList<>(); |
| 139 | + if (typeParameters != null) { |
| 140 | + for (Expression curType : typeParameters) { |
| 141 | + JavaType type = curType.getType(); |
| 142 | + if (type != null) { |
| 143 | + params.add(type); |
| 144 | + } |
| 145 | + } |
| 146 | + } |
| 147 | + return params; |
| 148 | + } |
| 149 | + return null; |
95 | 150 | } |
96 | 151 |
|
97 | 152 | private static boolean allArgumentsEmpty(J.MethodInvocation invocation) { |
|
0 commit comments