Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UseEnumSetOf should rewrite EnumSet.noneOf from empty Set.of() #570

94 changes: 54 additions & 40 deletions src/main/java/org/openrewrite/java/migrate/util/UseEnumSetOf.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

import org.jspecify.annotations.Nullable;
import org.openrewrite.*;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.search.UsesJavaVersion;
import org.openrewrite.java.search.UsesMethod;
Expand All @@ -33,6 +33,7 @@

public class UseEnumSetOf extends Recipe {
private static final MethodMatcher SET_OF = new MethodMatcher("java.util.Set of(..)", true);
private static final String METHOD_TYPE = "java.util.EnumSet";

@Override
public String getDisplayName() {
Expand All @@ -52,59 +53,72 @@ public Duration getEstimatedEffortPerOccurrence() {
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(Preconditions.and(new UsesJavaVersion<>(9),
new UsesMethod<>(SET_OF)), new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation m = super.visitMethodInvocation(method, ctx);
new UsesMethod<>(SET_OF)), new UseEnumSetOfVisitor());
}

if (SET_OF.matches(method) && method.getType() instanceof JavaType.Parameterized &&
!TypeUtils.isOfClassType(method.getType(), "java.util.EnumSet")) {
Cursor parent = getCursor().dropParentUntil(is -> is instanceof J.Assignment || is instanceof J.VariableDeclarations || is instanceof J.Block);
if (!(parent.getValue() instanceof J.Block)) {
JavaType type = parent.getValue() instanceof J.Assignment ?
((J.Assignment) parent.getValue()).getType() : ((J.VariableDeclarations) parent.getValue()).getVariables().get(0).getType();
if (isAssignmentSetOfEnum(type)) {
maybeAddImport("java.util.EnumSet");
private static class UseEnumSetOfVisitor extends JavaVisitor<ExecutionContext> {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInvocation, ExecutionContext ctx) {
J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(methodInvocation, ctx);

List<Expression> args = m.getArguments();
if (isArrayParameter(args)) {
return m;
}
if (SET_OF.matches(mi) &&
mi.getType() instanceof JavaType.Parameterized &&
!TypeUtils.isOfClassType(mi.getType(), METHOD_TYPE)) {
Cursor parent = getCursor().dropParentUntil(is -> is instanceof J.Assignment || is instanceof J.VariableDeclarations || is instanceof J.Block);
if (!(parent.getValue() instanceof J.Block)) {
JavaType type = parent.getValue() instanceof J.Assignment ?
((J.Assignment) parent.getValue()).getType() : ((J.VariableDeclarations) parent.getValue()).getVariables().get(0).getType();
if (isAssignmentSetOfEnum(type)) {
maybeAddImport(METHOD_TYPE);

StringJoiner setOf = new StringJoiner(", ", "EnumSet.of(", ")");
args.forEach(o -> setOf.add("#{any()}"));
List<Expression> args = mi.getArguments();
if (isArrayParameter(args)) {
return mi;
}

return JavaTemplate.builder(setOf.toString())
if (args.get(0) instanceof J.Empty) {
JavaType firstTypeParameter = ((JavaType.Parameterized) type).getTypeParameters().get(0);
JavaType.ShallowClass shallowClass = JavaType.ShallowClass.build(firstTypeParameter.toString());
return JavaTemplate.builder("EnumSet.noneOf(" + shallowClass.getClassName() + ".class)")
.contextSensitive()
.imports("java.util.EnumSet")
.imports(METHOD_TYPE)
.build()
.apply(updateCursor(m), m.getCoordinates().replace(), args.toArray());
.apply(updateCursor(mi), mi.getCoordinates().replace());
}

StringJoiner setOf = new StringJoiner(", ", "EnumSet.of(", ")");
args.forEach(o -> setOf.add("#{any()}"));
return JavaTemplate.builder(setOf.toString())
.contextSensitive()
.imports(METHOD_TYPE)
.build()
.apply(updateCursor(mi), mi.getCoordinates().replace(), args.toArray());
}
}
return m;
}
return mi;
}

private boolean isAssignmentSetOfEnum(@Nullable JavaType type) {
if (type instanceof JavaType.Parameterized) {
JavaType.Parameterized parameterized = (JavaType.Parameterized) type;
if (TypeUtils.isOfClassType(parameterized.getType(), "java.util.Set")) {
return ((JavaType.Parameterized) type).getTypeParameters().stream()
.filter(org.openrewrite.java.tree.JavaType.Class.class::isInstance)
.map(org.openrewrite.java.tree.JavaType.Class.class::cast)
.anyMatch(o -> o.getKind() == JavaType.FullyQualified.Kind.Enum);
}
private boolean isAssignmentSetOfEnum(@Nullable JavaType type) {
if (type instanceof JavaType.Parameterized) {
JavaType.Parameterized parameterized = (JavaType.Parameterized) type;
if (TypeUtils.isOfClassType(parameterized.getType(), "java.util.Set")) {
return ((JavaType.Parameterized) type).getTypeParameters().stream()
.filter(org.openrewrite.java.tree.JavaType.Class.class::isInstance)
.map(org.openrewrite.java.tree.JavaType.Class.class::cast)
.anyMatch(o -> o.getKind() == JavaType.FullyQualified.Kind.Enum);
}
return false;
}
return false;
}

private boolean isArrayParameter(final List<Expression> args) {
if (args.size() != 1) {
return false;
}
JavaType type = args.get(0).getType();
return TypeUtils.asArray(type) != null;
private boolean isArrayParameter(final List<Expression> args) {
if (args.size() != 1) {
return false;
}
});
JavaType type = args.get(0).getType();
return TypeUtils.asArray(type) != null;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ void changeDeclaration() {
java(
"""
import java.util.Set;

class Test {
public enum Color {
RED, GREEN, BLUE
Expand All @@ -52,7 +52,7 @@ public void method() {
"""
import java.util.EnumSet;
import java.util.Set;

class Test {
public enum Color {
RED, GREEN, BLUE
Expand All @@ -76,7 +76,7 @@ void changeAssignment() {
java(
"""
import java.util.Set;

class Test {
public enum Color {
RED, GREEN, BLUE
Expand All @@ -90,7 +90,7 @@ public void method() {
"""
import java.util.EnumSet;
import java.util.Set;

class Test {
public enum Color {
RED, GREEN, BLUE
Expand All @@ -116,7 +116,7 @@ void dontChangeVarargs() {
java(
"""
import java.util.Set;

class Test {
public enum Color {
RED, GREEN, BLUE
Expand All @@ -141,7 +141,7 @@ void dontChangeArray() {
java(
"""
import java.util.Set;

class Test {
public enum Color {
RED, GREEN, BLUE
Expand All @@ -157,4 +157,39 @@ public void method() {
)
);
}

@Test
@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/490")
void dontHaveArgs() {
//language=java
rewriteRun(
version(
java(
"""
import java.util.Set;
import java.util.concurrent.TimeUnit;
class Test {

public void method() {
Set<TimeUnit> warm = Set.of();
}
}
""",
"""
import java.util.EnumSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

class Test {

public void method() {
Set<TimeUnit> warm = EnumSet.noneOf(TimeUnit.class);
}
}
"""
),
9
)
);
}
}