Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,22 @@
*/
package org.openrewrite.java.migrate.lang.var;

import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
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 java.util.List;

import static java.util.Objects.requireNonNull;

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

@Override
Expand Down Expand Up @@ -82,16 +89,53 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
maybeRemoveImport((JavaType.FullyQualified) vd.getType());
}

return DeclarationCheck.transformToVar(vd);
// TODO implement to support cases like `var strs = List.<String>of();`
/*J.VariableDeclarations finalVd = vd;
return DeclarationCheck.<J.MethodInvocation>transformToVar(vd, it -> {
// If left has generics but right has not, copy types parameters
if (finalVd.getTypeExpression() instanceof J.ParameterizedType && !((J.ParameterizedType) finalVd.getTypeExpression()).getTypeParameters().isEmpty() && it.getTypeParameters() == null) {
return it.withTypeParameters(((J.ParameterizedType) finalVd.getTypeExpression()).getPadding().getTypeParameters());
// Make nested generic types explicit before converting to var
J.VariableDeclarations finalVd = vd;
return DeclarationCheck.transformToVar(vd, (J.MethodInvocation mi) -> makeNestedGenericsExplicit(mi, finalVd));
}

/**
* Makes nested generic types explicit by replacing diamond operators in constructor calls
* with explicit type parameters based on the variable declaration type.
*/
private J.MethodInvocation makeNestedGenericsExplicit(J.MethodInvocation mi, J.VariableDeclarations vd) {
// Extract type parameters from the variable declaration
if (!(vd.getTypeExpression() instanceof J.ParameterizedType)) {
return mi;
}

List<Expression> leftTypeParams = ((J.ParameterizedType) vd.getTypeExpression()).getTypeParameters();
if (leftTypeParams == null || leftTypeParams.isEmpty()) {
return mi;
}

// Visit arguments and replace diamond operators with explicit type parameters
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)
if (!hasTypeParams(newClass.getClazz())) {
// Copy type parameters from left side to right side
J.ParameterizedType rightType = (J.ParameterizedType) newClass.getClazz();
return newClass.withClazz(requireNonNull(rightType).withTypeParameters(leftTypeParams));
}
}
return arg;
}));
}

private static boolean hasTypeParams(@Nullable TypeTree clazz) {
if (clazz instanceof J.ParameterizedType) {
List<Expression> typeParameters = ((J.ParameterizedType) clazz).getTypeParameters();
if (typeParameters != null) {
for (Expression curType : typeParameters) {
if (curType.getType() != null) {
return true;
}
}
}
return it;
});*/
}
return false;
}

private static boolean allArgumentsEmpty(J.MethodInvocation invocation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.Issue;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

Expand Down Expand Up @@ -259,5 +260,40 @@ void m() {
)
);
}

@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/862")
@Test
void nestedGenericsSimple() {
//language=java
rewriteRun(
version(
java(
"""
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class A {
void dostuff() {
List<String> strs = Collections.synchronizedList(new ArrayList<>());
}
}
""",
"""
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class A {
void dostuff() {
var strs = Collections.synchronizedList(new ArrayList<String>());
}
}
"""
),
10
)
);
}
}
}