Skip to content

Commit 0890b40

Browse files
committed
[HotFix] NoGuavaJava21 causing invalid code since v2.28.0
1 parent 1ddc755 commit 0890b40

File tree

5 files changed

+932
-1101
lines changed

5 files changed

+932
-1101
lines changed

src/main/java/org/openrewrite/java/migrate/guava/AbstractNoGuavaImmutableOf.java

Lines changed: 126 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -17,199 +17,145 @@
1717

1818
import org.jspecify.annotations.Nullable;
1919
import org.openrewrite.*;
20-
import org.openrewrite.internal.ListUtils;
2120
import org.openrewrite.java.JavaTemplate;
2221
import org.openrewrite.java.JavaVisitor;
2322
import org.openrewrite.java.MethodMatcher;
2423
import org.openrewrite.java.search.UsesJavaVersion;
2524
import org.openrewrite.java.search.UsesType;
2625
import org.openrewrite.java.tree.*;
27-
import org.openrewrite.marker.Markers;
2826

2927
import java.time.Duration;
30-
import java.util.ArrayList;
3128
import java.util.List;
32-
33-
import static java.util.Collections.emptyList;
34-
3529
abstract class AbstractNoGuavaImmutableOf extends Recipe {
3630

37-
private final String guavaType;
38-
private final String javaType;
39-
40-
AbstractNoGuavaImmutableOf(String guavaType, String javaType) {
41-
this.guavaType = guavaType;
42-
this.javaType = javaType;
43-
}
44-
45-
private String getShortType(String fullyQualifiedType) {
46-
return fullyQualifiedType.substring(javaType.lastIndexOf(".") + 1);
47-
}
48-
49-
@Override
50-
public String getDisplayName() {
51-
return "Prefer `" + getShortType(javaType) + ".of(..)` in Java 9 or higher";
52-
}
53-
54-
@Override
55-
public String getDescription() {
56-
return "Replaces `" + getShortType(guavaType) + ".of(..)` if the returned type is immediately down-cast.";
57-
}
58-
59-
@Override
60-
public Duration getEstimatedEffortPerOccurrence() {
61-
return Duration.ofMinutes(10);
62-
}
63-
64-
@Override
65-
public TreeVisitor<?, ExecutionContext> getVisitor() {
66-
TreeVisitor<?, ExecutionContext> check = Preconditions.and(new UsesJavaVersion<>(9),
67-
new UsesType<>(guavaType, false));
68-
final MethodMatcher IMMUTABLE_MATCHER = new MethodMatcher(guavaType + " of(..)");
69-
return Preconditions.check(check, new JavaVisitor<ExecutionContext>() {
70-
@Override
71-
public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
72-
J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(method, ctx);
73-
if (!IMMUTABLE_MATCHER.matches(mi) || !isParentTypeDownCast(mi)) {
74-
return mi;
75-
}
76-
maybeRemoveImport(guavaType);
77-
maybeAddImport(javaType);
78-
79-
String template;
80-
Object[] templateArguments;
81-
List<Expression> methodArguments = mi.getArguments();
82-
if (methodArguments.isEmpty() || methodArguments.get(0) instanceof J.Empty) {
83-
template = getShortType(javaType) + ".of()";
84-
templateArguments = new Object[]{};
85-
} else if ("com.google.common.collect.ImmutableMap".equals(guavaType)) {
86-
template = getShortType(javaType) + ".of(#{any()}, #{any()})";
87-
templateArguments = new Object[]{methodArguments.get(0), methodArguments.get(1)};
88-
} else {
89-
template = getShortType(javaType) + ".of(#{any()})";
90-
templateArguments = new Object[]{methodArguments.get(0)};
91-
}
92-
93-
J.MethodInvocation m = JavaTemplate.builder(template)
94-
.imports(javaType)
95-
.build()
96-
.apply(getCursor(), mi.getCoordinates().replace(), templateArguments);
97-
m = m.getPadding().withArguments(mi.getPadding().getArguments());
98-
JavaType.Method newType = (JavaType.Method) visitType(mi.getMethodType(), ctx);
99-
m = m.withMethodType(newType).withName(m.getName().withType(newType));
100-
return super.visitMethodInvocation(m, ctx);
31+
private final String guavaType;
32+
private final String javaType;
33+
34+
AbstractNoGuavaImmutableOf(String guavaType, String javaType) {
35+
this.guavaType = guavaType;
36+
this.javaType = javaType;
37+
}
38+
39+
private String getShortType(String fullyQualifiedType) {
40+
return fullyQualifiedType.substring(javaType.lastIndexOf(".") + 1);
41+
}
42+
43+
@Override
44+
public String getDisplayName() {
45+
return "Prefer `" + getShortType(javaType) + ".of(..)` in Java 9 or higher";
46+
}
47+
48+
@Override
49+
public String getDescription() {
50+
return "Replaces `" + getShortType(guavaType) + ".of(..)` if the returned type is immediately down-cast.";
51+
}
52+
53+
@Override
54+
public Duration getEstimatedEffortPerOccurrence() {
55+
return Duration.ofMinutes(10);
56+
}
57+
58+
@Override
59+
public TreeVisitor<?, ExecutionContext> getVisitor() {
60+
TreeVisitor<?, ExecutionContext> check = Preconditions.and(new UsesJavaVersion<>(9),
61+
new UsesType<>(guavaType, false));
62+
final MethodMatcher IMMUTABLE_MATCHER = new MethodMatcher(guavaType + " of(..)");
63+
return Preconditions.check(check, new JavaVisitor<ExecutionContext>() {
64+
@Override
65+
public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
66+
J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(method, ctx);
67+
if (!IMMUTABLE_MATCHER.matches(mi) || !isParentTypeDownCast(mi)) {
68+
return mi;
69+
}
70+
maybeRemoveImport(guavaType);
71+
maybeAddImport(javaType);
72+
73+
String template;
74+
Object[] templateArguments;
75+
List<Expression> methodArguments = mi.getArguments();
76+
if (methodArguments.isEmpty() || methodArguments.get(0) instanceof J.Empty) {
77+
template = getShortType(javaType) + ".of()";
78+
templateArguments = new Object[]{};
79+
} else if ("com.google.common.collect.ImmutableMap".equals(guavaType)) {
80+
template = getShortType(javaType) + ".of(#{any()}, #{any()})";
81+
templateArguments = new Object[]{methodArguments.get(0), methodArguments.get(1)};
82+
} else {
83+
template = getShortType(javaType) + ".of(#{any()})";
84+
templateArguments = new Object[]{methodArguments.get(0)};
85+
}
86+
87+
J.MethodInvocation m = JavaTemplate.builder(template)
88+
.imports(javaType)
89+
.build()
90+
.apply(getCursor(), mi.getCoordinates().replace(), templateArguments);
91+
m = m.getPadding().withArguments(mi.getPadding().getArguments());
92+
JavaType.Method newType = (JavaType.Method) visitType(mi.getMethodType(), ctx);
93+
m = m.withMethodType(newType).withName(m.getName().withType(newType));
94+
return super.visitMethodInvocation(m, ctx);
95+
}
96+
97+
private boolean isParentTypeDownCast(MethodCall immutableMethod) {
98+
J parent = getCursor().dropParentUntil(J.class::isInstance).getValue();
99+
boolean isParentTypeDownCast = false;
100+
if (parent instanceof J.VariableDeclarations.NamedVariable) {
101+
isParentTypeDownCast = isParentTypeMatched(((J.VariableDeclarations.NamedVariable) parent).getType());
102+
} else if (parent instanceof J.Assignment) {
103+
J.Assignment a = (J.Assignment) parent;
104+
if (a.getVariable() instanceof J.Identifier && ((J.Identifier) a.getVariable()).getFieldType() != null) {
105+
isParentTypeDownCast = isParentTypeMatched(((J.Identifier) a.getVariable()).getFieldType().getType());
106+
} else if (a.getVariable() instanceof J.FieldAccess) {
107+
isParentTypeDownCast = isParentTypeMatched(a.getVariable().getType());
108+
}
109+
} else if (parent instanceof J.Return) {
110+
// Does not currently support returns in lambda expressions.
111+
J j = getCursor().dropParentUntil(is -> is instanceof J.MethodDeclaration || is instanceof J.CompilationUnit).getValue();
112+
if (j instanceof J.MethodDeclaration) {
113+
TypeTree returnType = ((J.MethodDeclaration) j).getReturnTypeExpression();
114+
if (returnType != null) {
115+
isParentTypeDownCast = isParentTypeMatched(returnType.getType());
101116
}
102-
103-
@Override
104-
public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
105-
J.VariableDeclarations mv = (J.VariableDeclarations) super.visitVariableDeclarations(multiVariable, ctx);
106-
107-
if (multiVariable != mv && TypeUtils.isOfClassType(mv.getType(), guavaType)) {
108-
JavaType newType = JavaType.buildType(javaType);
109-
mv = mv.withTypeExpression(mv.getTypeExpression() == null ?
110-
null : createNewTypeExpression(mv.getTypeExpression(), newType));
111-
112-
mv = mv.withVariables(ListUtils.map(mv.getVariables(), variable -> {
113-
JavaType.FullyQualified varType = TypeUtils.asFullyQualified(variable.getType());
114-
if (varType != null && !varType.equals(newType)) {
115-
return variable.withType(newType).withName(variable.getName().withType(newType));
116-
}
117-
return variable;
118-
}));
119-
}
120-
121-
return mv;
122-
}
123-
124-
private TypeTree createNewTypeExpression(TypeTree typeTree, JavaType newType) {
125-
if (typeTree instanceof J.ParameterizedType) {
126-
J.ParameterizedType parameterizedType = (J.ParameterizedType) typeTree;
127-
List<JRightPadded<Expression>> jRightPaddedList = new ArrayList<>();
128-
parameterizedType.getTypeParameters().forEach(
129-
expression -> {
130-
if (expression instanceof J.ParameterizedType && TypeUtils.isOfClassType(expression.getType(), guavaType)) {
131-
jRightPaddedList.add(JRightPadded.build(((J.ParameterizedType) createNewTypeExpression((TypeTree) expression, newType))));
132-
} else {
133-
jRightPaddedList.add(JRightPadded.build(expression));
134-
}
135-
});
136-
NameTree clazz = new J.Identifier(
137-
Tree.randomId(), Space.EMPTY, Markers.EMPTY, emptyList(), getShortType(javaType), null, null);
138-
return parameterizedType.withClazz(clazz).withType(newType).getPadding().withTypeParameters(JContainer.build(jRightPaddedList));
139-
}
140-
return new J.Identifier(
141-
typeTree.getId(),
142-
typeTree.getPrefix(),
143-
Markers.EMPTY,
144-
emptyList(),
145-
getShortType(javaType),
146-
newType,
147-
null
148-
);
117+
}
118+
} else if (parent instanceof J.MethodInvocation) {
119+
J.MethodInvocation m = (J.MethodInvocation) parent;
120+
int index = m.getArguments().indexOf(immutableMethod);
121+
if (m.getMethodType() != null) {
122+
if(index != -1 && !m.getMethodType().getParameterTypes().isEmpty()) {
123+
isParentTypeDownCast = isParentTypeMatched(m.getMethodType().getParameterTypes().get(index));
124+
} else {
125+
isParentTypeDownCast = !TypeUtils.isOfClassType(m.getMethodType().getReturnType() , guavaType);
149126
}
150-
151-
152-
private boolean isParentTypeDownCast(MethodCall immutableMethod) {
153-
J parent = getCursor().dropParentUntil(J.class::isInstance).getValue();
154-
boolean isParentTypeDownCast = false;
155-
if (parent instanceof J.VariableDeclarations.NamedVariable) {
156-
isParentTypeDownCast = isParentTypeMatched(((J.VariableDeclarations.NamedVariable) parent).getType());
157-
} else if (parent instanceof J.Assignment) {
158-
J.Assignment a = (J.Assignment) parent;
159-
if (a.getVariable() instanceof J.Identifier && ((J.Identifier) a.getVariable()).getFieldType() != null) {
160-
isParentTypeDownCast = isParentTypeMatched(((J.Identifier) a.getVariable()).getFieldType().getType());
161-
} else if (a.getVariable() instanceof J.FieldAccess) {
162-
isParentTypeDownCast = isParentTypeMatched(a.getVariable().getType());
163-
}
164-
} else if (parent instanceof J.Return) {
165-
// Does not currently support returns in lambda expressions.
166-
J j = getCursor().dropParentUntil(is -> is instanceof J.MethodDeclaration || is instanceof J.CompilationUnit).getValue();
167-
if (j instanceof J.MethodDeclaration) {
168-
TypeTree returnType = ((J.MethodDeclaration) j).getReturnTypeExpression();
169-
if (returnType != null) {
170-
isParentTypeDownCast = isParentTypeMatched(returnType.getType());
171-
}
172-
}
173-
} else if (parent instanceof J.MethodInvocation) {
174-
J.MethodInvocation m = (J.MethodInvocation) parent;
175-
int index = m.getArguments().indexOf(immutableMethod);
176-
if (m.getMethodType() != null && index != -1 && !m.getMethodType().getParameterTypes().isEmpty()) {
177-
isParentTypeDownCast = isParentTypeMatched(m.getMethodType().getParameterTypes().get(index));
178-
} else {
179-
isParentTypeDownCast = true;
180-
}
181-
} else if (parent instanceof J.NewClass) {
182-
J.NewClass c = (J.NewClass) parent;
183-
int index = 0;
184-
if (c.getConstructorType() != null) {
185-
for (Expression argument : c.getArguments()) {
186-
if (IMMUTABLE_MATCHER.matches(argument)) {
187-
break;
188-
}
189-
index++;
190-
}
191-
if (c.getConstructorType() != null) {
192-
isParentTypeDownCast = isParentTypeMatched(c.getConstructorType().getParameterTypes().get(index));
193-
}
194-
}
195-
} else if (parent instanceof J.NewArray) {
196-
J.NewArray a = (J.NewArray) parent;
197-
JavaType arrayType = a.getType();
198-
while (arrayType instanceof JavaType.Array) {
199-
arrayType = ((JavaType.Array) arrayType).getElemType();
200-
}
201-
202-
isParentTypeDownCast = isParentTypeMatched(arrayType);
203-
}
204-
return isParentTypeDownCast;
127+
}
128+
} else if (parent instanceof J.NewClass) {
129+
J.NewClass c = (J.NewClass) parent;
130+
int index = 0;
131+
if (c.getConstructorType() != null) {
132+
for (Expression argument : c.getArguments()) {
133+
if (IMMUTABLE_MATCHER.matches(argument)) {
134+
break;
135+
}
136+
index++;
205137
}
206-
207-
private boolean isParentTypeMatched(@Nullable JavaType type) {
208-
JavaType.FullyQualified fq = TypeUtils.asFullyQualified(type);
209-
return TypeUtils.isOfClassType(fq, javaType) ||
210-
TypeUtils.isOfClassType(fq, "java.lang.Object") ||
211-
TypeUtils.isOfClassType(fq, guavaType);
138+
if (c.getConstructorType() != null) {
139+
isParentTypeDownCast = isParentTypeMatched(c.getConstructorType().getParameterTypes().get(index));
212140
}
213-
});
214-
}
141+
}
142+
} else if (parent instanceof J.NewArray) {
143+
J.NewArray a = (J.NewArray) parent;
144+
JavaType arrayType = a.getType();
145+
while (arrayType instanceof JavaType.Array) {
146+
arrayType = ((JavaType.Array) arrayType).getElemType();
147+
}
148+
149+
isParentTypeDownCast = isParentTypeMatched(arrayType);
150+
}
151+
return isParentTypeDownCast;
152+
}
153+
154+
private boolean isParentTypeMatched(@Nullable JavaType type) {
155+
JavaType.FullyQualified fq = TypeUtils.asFullyQualified(type);
156+
return TypeUtils.isOfClassType(fq, javaType) ||
157+
TypeUtils.isOfClassType(fq, "java.lang.Object");
158+
}
159+
});
160+
}
215161
}

0 commit comments

Comments
 (0)