Skip to content

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

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

Merged
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
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
@@ -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;
@@ -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() {
@@ -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
@@ -39,7 +39,7 @@ void changeDeclaration() {
java(
"""
import java.util.Set;

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

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

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

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

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

class Test {
public enum Color {
RED, GREEN, BLUE
@@ -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
)
);
}
}