Skip to content

Commit a2b5986

Browse files
authored
Let all change-to-var recipes use a created var-Identifier instead of using a JavaTemplate (#793)
* Let all change-to-var recipes use a created var-Identifier instead of using a JavaTemplate * Polish * Polish * Polish * Polish
1 parent 775fcf2 commit a2b5986

File tree

6 files changed

+86
-168
lines changed

6 files changed

+86
-168
lines changed

src/main/java/org/openrewrite/java/migrate/lang/var/DeclarationCheck.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,27 @@
1515
*/
1616
package org.openrewrite.java.migrate.lang.var;
1717

18+
import lombok.experimental.UtilityClass;
1819
import org.jspecify.annotations.Nullable;
1920
import org.openrewrite.Cursor;
21+
import org.openrewrite.internal.ListUtils;
2022
import org.openrewrite.java.tree.*;
23+
import org.openrewrite.marker.Markers;
2124

25+
import java.util.List;
26+
import java.util.function.UnaryOperator;
27+
28+
import static java.util.Collections.emptyList;
29+
import static java.util.Collections.singleton;
2230
import static java.util.Objects.requireNonNull;
31+
import static org.openrewrite.Tree.randomId;
32+
import static org.openrewrite.java.tree.Space.EMPTY;
2333

34+
@UtilityClass
2435
final class DeclarationCheck {
2536

26-
private DeclarationCheck() {
27-
28-
}
29-
3037
/**
31-
* Determine if var is applicable with regard to location and decleation type.
38+
* Determine if var is applicable with regard to location and declaration type.
3239
* <p>
3340
* Var is applicable inside methods and initializer blocks for single variable definition.
3441
* Var is *not* applicable to method definitions.
@@ -119,7 +126,7 @@ public static boolean useGenerics(J.VariableDeclarations vd) {
119126
}
120127

121128
/**
122-
* Determin if the initilizer uses the ternary operator <code>Expression ? if-then : else</code>
129+
* Determine if the initializer uses the ternary operator <code>Expression ? if-then : else</code>
123130
*
124131
* @param vd variable declaration at hand
125132
* @return true iff the ternary operator is used in the initialization
@@ -225,4 +232,29 @@ public static boolean initializedByStaticMethod(@Nullable Expression initializer
225232

226233
return invocation.getMethodType().hasFlags(Flag.Static);
227234
}
235+
236+
public static J.VariableDeclarations transformToVar(J.VariableDeclarations vd) {
237+
return transformToVar(vd, it -> it);
238+
}
239+
240+
public static <T extends Expression> J.VariableDeclarations transformToVar(J.VariableDeclarations vd, UnaryOperator<T> transformerInitializer) {
241+
T initializer = (T) vd.getVariables().get(0).getInitializer();
242+
if (initializer == null) {
243+
return vd;
244+
}
245+
246+
Expression transformedInitializer = transformerInitializer.apply(initializer);
247+
248+
List<J.VariableDeclarations.NamedVariable> variables = ListUtils.mapFirst(vd.getVariables(), it -> {
249+
JavaType.Variable variableType = it.getVariableType() == null ? null : it.getVariableType().withOwner(null);
250+
return it
251+
.withName(it.getName().withType(transformedInitializer.getType()).withFieldType(variableType))
252+
.withInitializer(transformedInitializer)
253+
.withVariableType(variableType);
254+
});
255+
J.Identifier typeExpression = new J.Identifier(randomId(), vd.getTypeExpression() == null ? EMPTY : vd.getTypeExpression().getPrefix(),
256+
Markers.build(singleton(JavaVarKeyword.build())), emptyList(), "var", transformedInitializer.getType(), null);
257+
258+
return vd.withVariables(variables).withTypeExpression(typeExpression);
259+
}
228260
}

src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocations.java

Lines changed: 21 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,15 @@
1515
*/
1616
package org.openrewrite.java.migrate.lang.var;
1717

18-
import org.openrewrite.*;
18+
import org.openrewrite.ExecutionContext;
19+
import org.openrewrite.Preconditions;
20+
import org.openrewrite.Recipe;
21+
import org.openrewrite.TreeVisitor;
1922
import org.openrewrite.java.JavaIsoVisitor;
20-
import org.openrewrite.java.JavaParser;
21-
import org.openrewrite.java.JavaTemplate;
2223
import org.openrewrite.java.search.UsesJavaVersion;
23-
import org.openrewrite.java.tree.*;
24-
import org.openrewrite.marker.Markers;
25-
26-
import java.util.ArrayList;
27-
import java.util.List;
28-
29-
import static java.util.Collections.emptyList;
24+
import org.openrewrite.java.tree.Expression;
25+
import org.openrewrite.java.tree.J;
26+
import org.openrewrite.java.tree.JavaType;
3027

3128
public class UseVarForGenericMethodInvocations extends Recipe {
3229
@Override
@@ -50,9 +47,6 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
5047
}
5148

5249
static final class UseVarForGenericsVisitor extends JavaIsoVisitor<ExecutionContext> {
53-
private final JavaTemplate template = JavaTemplate.builder("var #{} = #{any()}")
54-
.javaParser(JavaParser.fromJavaVersion()).build();
55-
5650
@Override
5751
public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations vd, ExecutionContext ctx) {
5852
vd = super.visitVariableDeclarations(vd, ctx);
@@ -62,34 +56,42 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
6256
return vd;
6357
}
6458

65-
// recipe specific
59+
// Recipe specific
6660
boolean isPrimitive = DeclarationCheck.isPrimitive(vd);
6761
boolean usesNoGenerics = !DeclarationCheck.useGenerics(vd);
6862
boolean usesTernary = DeclarationCheck.initializedByTernary(vd);
6963
if (isPrimitive || usesTernary || usesNoGenerics) {
7064
return vd;
7165
}
7266

73-
//now we deal with generics, check for method invocations
67+
// Now we deal with generics, check for method invocations
7468
Expression initializer = vd.getVariables().get(0).getInitializer();
7569
boolean isMethodInvocation = initializer != null && initializer.unwrap() instanceof J.MethodInvocation;
7670
if (!isMethodInvocation) {
7771
return vd;
7872
}
7973

80-
//if no type paramters are present and no arguments we assume the type is hard to determine a needs manual action
74+
// If no type parameters and no arguments are present, we assume the type is too hard to determine
8175
boolean hasNoTypeParams = ((J.MethodInvocation) initializer).getTypeParameters() == null;
8276
boolean argumentsEmpty = allArgumentsEmpty((J.MethodInvocation) initializer);
8377
if (hasNoTypeParams && argumentsEmpty) {
8478
return vd;
8579
}
8680

87-
// mark imports for removal if unused
8881
if (vd.getType() instanceof JavaType.FullyQualified) {
89-
maybeRemoveImport( (JavaType.FullyQualified) vd.getType() );
82+
maybeRemoveImport((JavaType.FullyQualified) vd.getType());
9083
}
9184

92-
return transformToVar(vd, new ArrayList<>(), new ArrayList<>());
85+
return DeclarationCheck.transformToVar(vd);
86+
// TODO implement to support cases like `var strs = List.<String>of();`
87+
/*J.VariableDeclarations finalVd = vd;
88+
return DeclarationCheck.<J.MethodInvocation>transformToVar(vd, it -> {
89+
// If left has generics but right has not, copy types parameters
90+
if (finalVd.getTypeExpression() instanceof J.ParameterizedType && !((J.ParameterizedType) finalVd.getTypeExpression()).getTypeParameters().isEmpty() && it.getTypeParameters() == null) {
91+
return it.withTypeParameters(((J.ParameterizedType) finalVd.getTypeExpression()).getPadding().getTypeParameters());
92+
}
93+
return it;
94+
});*/
9395
}
9496

9597
private static boolean allArgumentsEmpty(J.MethodInvocation invocation) {
@@ -100,40 +102,5 @@ private static boolean allArgumentsEmpty(J.MethodInvocation invocation) {
100102
}
101103
return true;
102104
}
103-
104-
private J.VariableDeclarations transformToVar(J.VariableDeclarations vd, List<JavaType> leftTypes, List<JavaType> rightTypes) {
105-
Expression initializer = vd.getVariables().get(0).getInitializer();
106-
String simpleName = vd.getVariables().get(0).getSimpleName();
107-
108-
// if left is defined but not right, copy types to initializer
109-
if (rightTypes.isEmpty() && !leftTypes.isEmpty()) {
110-
// we need to switch type infos from left to right here
111-
List<Expression> typeArgument = new ArrayList<>();
112-
for (JavaType t : leftTypes) {
113-
typeArgument.add(new J.Identifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, emptyList(), ((JavaType.Class) t).getClassName(), t, null));
114-
}
115-
J.ParameterizedType typedInitializerClazz = ((J.ParameterizedType) ((J.NewClass) initializer).getClazz()).withTypeParameters(typeArgument);
116-
initializer = ((J.NewClass) initializer).withClazz(typedInitializerClazz);
117-
}
118-
119-
J.VariableDeclarations result = template.<J.VariableDeclarations>apply(getCursor(), vd.getCoordinates().replace(), simpleName, initializer)
120-
.withPrefix(vd.getPrefix());
121-
122-
// apply modifiers like final
123-
List<J.Modifier> modifiers = vd.getModifiers();
124-
boolean hasModifiers = !modifiers.isEmpty();
125-
if (hasModifiers) {
126-
result = result.withModifiers(modifiers);
127-
}
128-
129-
// apply prefix to type expression
130-
TypeTree resultingTypeExpression = result.getTypeExpression();
131-
boolean resultHasTypeExpression = resultingTypeExpression != null;
132-
if (resultHasTypeExpression) {
133-
result = result.withTypeExpression(resultingTypeExpression.withPrefix(vd.getTypeExpression().getPrefix()));
134-
}
135-
136-
return result;
137-
}
138105
}
139106
}

src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericsConstructors.java

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,15 @@
2020
import org.openrewrite.Preconditions;
2121
import org.openrewrite.Recipe;
2222
import org.openrewrite.TreeVisitor;
23-
import org.openrewrite.internal.ListUtils;
2423
import org.openrewrite.java.JavaIsoVisitor;
2524
import org.openrewrite.java.search.UsesJavaVersion;
26-
import org.openrewrite.java.tree.*;
27-
import org.openrewrite.marker.Markers;
25+
import org.openrewrite.java.tree.Expression;
26+
import org.openrewrite.java.tree.J;
27+
import org.openrewrite.java.tree.JavaType;
28+
import org.openrewrite.java.tree.TypeTree;
2829

2930
import java.util.ArrayList;
3031
import java.util.List;
31-
import java.util.Objects;
32-
33-
import static java.util.Collections.emptyList;
34-
import static java.util.Collections.singleton;
35-
import static org.openrewrite.Tree.randomId;
3632

3733
public class UseVarForGenericsConstructors extends Recipe {
3834
@Override
@@ -87,12 +83,20 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
8783
return vd;
8884
}
8985

90-
// Mark imports for removal if unused
9186
if (vd.getType() instanceof JavaType.FullyQualified) {
9287
maybeRemoveImport((JavaType.FullyQualified) vd.getType());
9388
}
9489

95-
return transformToVar(vd, leftTypes, rightTypes);
90+
J.VariableDeclarations finalVd = vd;
91+
return DeclarationCheck.<J.NewClass>transformToVar(vd, it -> {
92+
// If left is defined but right is not, copy types from typeExpression to initializer
93+
if (rightTypes.isEmpty() && !leftTypes.isEmpty() && finalVd.getTypeExpression() instanceof J.ParameterizedType && it.getClazz() instanceof J.ParameterizedType) {
94+
J.ParameterizedType typedInitializerClazz = ((J.ParameterizedType) it.getClazz())
95+
.withTypeParameters(((J.ParameterizedType) finalVd.getTypeExpression()).getTypeParameters());
96+
return it.withClazz(typedInitializerClazz);
97+
}
98+
return it;
99+
});
96100
}
97101

98102
private static Boolean anyTypeHasBounds(List<JavaType> leftTypes) {
@@ -141,30 +145,5 @@ private List<JavaType> extractTypeParameters(JavaType.@Nullable Variable variabl
141145
}
142146
return new ArrayList<>();
143147
}
144-
145-
private J.VariableDeclarations transformToVar(J.VariableDeclarations vd, List<JavaType> leftTypes, List<JavaType> rightTypes) {
146-
J.NewClass initializer = Objects.requireNonNull((J.NewClass) vd.getVariables().get(0).getInitializer());
147-
148-
// If left is defined but right is not, copy types from typeExpression to initializer
149-
if (rightTypes.isEmpty() && !leftTypes.isEmpty() && vd.getTypeExpression() instanceof J.ParameterizedType && initializer.getClazz() instanceof J.ParameterizedType) {
150-
J.ParameterizedType typedInitializerClazz = ((J.ParameterizedType) initializer.getClazz())
151-
.withTypeParameters(((J.ParameterizedType) vd.getTypeExpression()).getTypeParameters());
152-
initializer = initializer.withClazz(typedInitializerClazz);
153-
}
154-
155-
// Replace actual type by `var` keyword and replace the first variable's name, initializer and type
156-
J.NewClass finalInitializer = initializer;
157-
List<J.VariableDeclarations.NamedVariable> variables = ListUtils.mapFirst(vd.getVariables(), it -> {
158-
JavaType.Variable variableType = it.getVariableType() == null ? null : it.getVariableType().withOwner(null);
159-
return it
160-
.withName(it.getName().withType(finalInitializer.getType()).withFieldType(variableType))
161-
.withInitializer(finalInitializer)
162-
.withVariableType(variableType);
163-
});
164-
J.Identifier typeExpression = new J.Identifier(randomId(), vd.getTypeExpression().getPrefix(),
165-
Markers.build(singleton(JavaVarKeyword.build())), emptyList(), "var", initializer.getType(), null);
166-
167-
return vd.withVariables(variables).withTypeExpression(typeExpression);
168-
}
169148
}
170149
}

src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForObject.java

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,10 @@
2222
import org.openrewrite.Recipe;
2323
import org.openrewrite.TreeVisitor;
2424
import org.openrewrite.java.JavaIsoVisitor;
25-
import org.openrewrite.java.JavaParser;
26-
import org.openrewrite.java.JavaTemplate;
2725
import org.openrewrite.java.search.UsesJavaVersion;
2826
import org.openrewrite.java.tree.Expression;
2927
import org.openrewrite.java.tree.J;
3028
import org.openrewrite.java.tree.JavaType;
31-
import org.openrewrite.java.tree.TypeTree;
3229

3330
@EqualsAndHashCode(callSuper = false)
3431
@Value
@@ -58,11 +55,6 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
5855

5956

6057
static final class UseVarForObjectVisitor extends JavaIsoVisitor<ExecutionContext> {
61-
private final JavaTemplate template = JavaTemplate.builder("var #{} = #{any()};")
62-
.contextSensitive()
63-
.javaParser(JavaParser.fromJavaVersion()).build();
64-
65-
6658
@Override
6759
public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations vd, ExecutionContext ctx) {
6860
vd = super.visitVariableDeclarations(vd, ctx);
@@ -82,30 +74,11 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
8274
return vd;
8375
}
8476

85-
// mark imports for removal if unused
8677
if (vd.getType() instanceof JavaType.FullyQualified) {
8778
maybeRemoveImport( (JavaType.FullyQualified) vd.getType() );
8879
}
8980

90-
return transformToVar(vd);
91-
}
92-
93-
94-
private J.VariableDeclarations transformToVar(J.VariableDeclarations vd) {
95-
Expression initializer = vd.getVariables().get(0).getInitializer();
96-
String simpleName = vd.getVariables().get(0).getSimpleName();
97-
98-
if (vd.getModifiers().isEmpty()) {
99-
return template.apply(getCursor(), vd.getCoordinates().replace(), simpleName, initializer)
100-
.withPrefix(vd.getPrefix());
101-
} else {
102-
J.VariableDeclarations result = template.<J.VariableDeclarations>apply(getCursor(), vd.getCoordinates().replace(), simpleName, initializer)
103-
.withModifiers(vd.getModifiers())
104-
.withPrefix(vd.getPrefix());
105-
TypeTree typeExpression = result.getTypeExpression();
106-
//noinspection DataFlowIssue
107-
return typeExpression != null ? result.withTypeExpression(typeExpression.withPrefix(vd.getTypeExpression().getPrefix())) : vd;
108-
}
81+
return DeclarationCheck.transformToVar(vd);
10982
}
11083
}
11184
}

0 commit comments

Comments
 (0)