Skip to content

Commit

Permalink
UseEnumSetOf should rewrite EnumSet.noneOf from empty Set.of() (#…
Browse files Browse the repository at this point in the history
…570)

* issue-490: Rewriting with EnumSet.noneOf from empty Set.of() by UseEnumSetOf

* refactoring code

* format code

* change test name

* Apply formatter

* Adopt conventions for local variable naming

* Use two separate templates for replacement

* Organize imports

---------

Co-authored-by: Tim te Beek <tim@moderne.io>
  • Loading branch information
BramliAK and timtebeek authored Oct 5, 2024
1 parent 984efb8 commit fe412ee
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 46 deletions.
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
)
);
}
}

0 comments on commit fe412ee

Please sign in to comment.