Skip to content

Commit f2bd8eb

Browse files
authored
Fix errors for Arrays and inception bounds (#263)
* add Tests for arrays as type parameter and bounds hiding in type parameters * fix arrays as type parameters * fix arrays as type parameters and bounds * replace streams with for loops
1 parent 2dde3ae commit f2bd8eb

File tree

4 files changed

+159
-32
lines changed

4 files changed

+159
-32
lines changed

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import java.util.ArrayList;
1919
import java.util.List;
20-
import java.util.stream.Collectors;
2120

2221
import org.openrewrite.*;
2322
import org.openrewrite.java.JavaIsoVisitor;
@@ -72,7 +71,7 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
7271

7372
//if no type paramters are present and no arguments we assume the type is hard to determine a needs manual action
7473
boolean hasNoTypeParams = ((J.MethodInvocation) initializer).getTypeParameters() == null;
75-
boolean argumentsEmpty = ((J.MethodInvocation) initializer).getArguments().stream().allMatch(p -> p instanceof J.Empty);
74+
boolean argumentsEmpty = allArgumentsEmpty((J.MethodInvocation) initializer);
7675
if (hasNoTypeParams && argumentsEmpty) return vd;
7776

7877
// mark imports for removal if unused
@@ -81,17 +80,26 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
8180
return transformToVar(vd, new ArrayList<>(), new ArrayList<>());
8281
}
8382

83+
private static boolean allArgumentsEmpty(J.MethodInvocation invocation){
84+
for (Expression argument : invocation.getArguments()) {
85+
if (!(argument instanceof J.Empty)) {
86+
return false;
87+
}
88+
}
89+
return true;
90+
}
91+
8492
private J.VariableDeclarations transformToVar(J.VariableDeclarations vd, List<JavaType> leftTypes, List<JavaType> rightTypes) {
8593
Expression initializer = vd.getVariables().get(0).getInitializer();
8694
String simpleName = vd.getVariables().get(0).getSimpleName();
8795

8896
// if left is defined but not right, copy types to initializer
8997
if(rightTypes.isEmpty() && !leftTypes.isEmpty()) {
9098
// we need to switch type infos from left to right here
91-
List<Expression> typeArgument = leftTypes.stream()
92-
.map(t ->
93-
new J.Identifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, ((JavaType.Class)t).getClassName(), t, null))
94-
.collect(Collectors.toList());
99+
List<Expression> typeArgument = new ArrayList<>();
100+
for (JavaType t : leftTypes) {
101+
typeArgument.add(new J.Identifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, ((JavaType.Class) t).getClassName(), t, null));
102+
}
95103
J.ParameterizedType typedInitializerClazz = ((J.ParameterizedType) ((J.NewClass) initializer).getClazz()).withTypeParameters(typeArgument);
96104
initializer = ((J.NewClass) initializer).withClazz(typedInitializerClazz);
97105
}

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

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
package org.openrewrite.java.migrate.lang.var;
1717

1818
import java.util.ArrayList;
19+
import java.util.Collections;
1920
import java.util.List;
20-
import java.util.Objects;
21-
import java.util.stream.Collectors;
2221

22+
import org.jetbrains.annotations.NotNull;
2323
import org.openrewrite.*;
24+
import org.openrewrite.internal.StringUtils;
2425
import org.openrewrite.internal.lang.Nullable;
2526
import org.openrewrite.java.JavaIsoVisitor;
2627
import org.openrewrite.java.JavaParser;
@@ -73,11 +74,11 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
7374
if (rightTypes == null || (leftTypes.isEmpty() && rightTypes.isEmpty())) return vd;
7475

7576
// skip generics with type bounds, it's not yet implemented
76-
boolean genericHasBounds = leftTypes.stream()
77-
.filter(t -> t instanceof JavaType.GenericTypeVariable)
78-
.map(t -> (JavaType.GenericTypeVariable) t)
79-
.map(t -> !t.getBounds().isEmpty())
80-
.reduce(false, Boolean::logicalOr);
77+
for (JavaType type : leftTypes) {
78+
if (hasBounds(type))
79+
return vd;
80+
}
81+
boolean genericHasBounds = anyTypeHasBounds(leftTypes);
8182
if (genericHasBounds) return vd;
8283

8384
// mark imports for removal if unused
@@ -86,6 +87,25 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
8687
return transformToVar(vd, leftTypes, rightTypes);
8788
}
8889

90+
@NotNull
91+
private static Boolean anyTypeHasBounds(List<JavaType> leftTypes) {
92+
for (JavaType type : leftTypes) {
93+
if (hasBounds(type))
94+
return true;
95+
}
96+
return false;
97+
}
98+
99+
private static boolean hasBounds(JavaType type) {
100+
if (type instanceof JavaType.Parameterized) {
101+
return anyTypeHasBounds(((JavaType.Parameterized) type).getTypeParameters());
102+
}
103+
if (type instanceof JavaType.GenericTypeVariable) {
104+
return !((JavaType.GenericTypeVariable) type).getBounds().isEmpty();
105+
}
106+
return false;
107+
}
108+
89109
/**
90110
* Tries to extract the generic parameters from the expression,
91111
* if the Initializer is no new class or not of a parameterized type, returns null to signale "no info".
@@ -98,15 +118,16 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
98118
TypeTree clazz = ((J.NewClass) initializer).getClazz();
99119
if (clazz instanceof J.ParameterizedType) {
100120
List<Expression> typeParameters = ((J.ParameterizedType) clazz).getTypeParameters();
121+
List<JavaType> params = new ArrayList<>();
101122
if (typeParameters != null) {
102-
return typeParameters
103-
.stream()
104-
.map(Expression::getType)
105-
.filter(Objects::nonNull)
106-
.collect(Collectors.toList());
107-
} else {
108-
return new ArrayList<>();
123+
for (Expression curType : typeParameters) {
124+
JavaType type = curType.getType();
125+
if (type != null) {
126+
params.add(type);
127+
}
128+
}
109129
}
130+
return params;
110131
}
111132
}
112133
return null;
@@ -133,13 +154,14 @@ private J.VariableDeclarations transformToVar(J.VariableDeclarations vd, List<Ja
133154
// if left is defined but not right, copy types to initializer
134155
if(rightTypes.isEmpty() && !leftTypes.isEmpty()) {
135156
// we need to switch type infos from left to right here
136-
List<Expression> typeArgument = leftTypes.stream()
137-
.map(UseVarForGenericsConstructorsVisitor::typeToExpression)
138-
.collect(Collectors.toList());
157+
List<Expression> typeExpressions = new ArrayList<>();
158+
for (JavaType curType : leftTypes) {
159+
typeExpressions.add(typeToExpression(curType));
160+
}
139161

140162
J.ParameterizedType typedInitializerClazz = ((J.ParameterizedType) ((J.NewClass) initializer)
141163
.getClazz())
142-
.withTypeParameters(typeArgument);
164+
.withTypeParameters(typeExpressions);
143165
initializer = ((J.NewClass) initializer).withClazz(typedInitializerClazz);
144166
}
145167

@@ -169,10 +191,20 @@ private J.VariableDeclarations transformToVar(J.VariableDeclarations vd, List<Ja
169191
* @return semantically equal Expression
170192
*/
171193
private static Expression typeToExpression(JavaType type) {
172-
if (type instanceof JavaType.Class) { // easy just parse to identifier
194+
if (type instanceof JavaType.Primitive) {
195+
JavaType.Primitive primitiveType = JavaType.Primitive.fromKeyword(((JavaType.Primitive) type).getKeyword());
196+
return new J.Primitive(Tree.randomId(), Space.EMPTY, Markers.EMPTY, primitiveType);
197+
}
198+
if (type instanceof JavaType.Class) {
173199
String className = ((JavaType.Class) type).getClassName();
174200
return new J.Identifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, className, type, null);
175201
}
202+
if (type instanceof JavaType.Array){
203+
int dimensions = StringUtils.countOccurrences(type.toString(), "[]");
204+
List<JRightPadded<Space>> dimensionsDefinition = Collections.nCopies(dimensions, JRightPadded.build(Space.EMPTY));
205+
TypeTree elemType = (TypeTree) typeToExpression(((JavaType.Array) type).getElemType());
206+
return new J.ArrayType(Tree.randomId(), Space.EMPTY, Markers.EMPTY, elemType, dimensionsDefinition);
207+
}
176208
if (type instanceof JavaType.GenericTypeVariable) {
177209
String variableName = ((JavaType.GenericTypeVariable) type).getName();
178210
J.Identifier identifier = new J.Identifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, variableName, type, null);
@@ -193,13 +225,15 @@ private static Expression typeToExpression(JavaType type) {
193225
}
194226
}
195227
if (type instanceof JavaType.Parameterized) { // recursively parse
196-
List<JRightPadded<Expression>> typeParams = ((JavaType.Parameterized) type).getTypeParameters().stream()
197-
.map(UseVarForGenericsConstructorsVisitor::typeToExpression)
198-
.map(JRightPadded::build)
199-
.collect(Collectors.toList());
228+
List<JavaType> typeParameters = ((JavaType.Parameterized) type).getTypeParameters();
229+
230+
List<JRightPadded<Expression>> typeParamsExpression = new ArrayList<>(typeParameters.size());
231+
for (JavaType curType : typeParameters) {
232+
typeParamsExpression.add(JRightPadded.build(typeToExpression(curType)));
233+
}
200234

201235
NameTree clazz = new J.Identifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, ((JavaType.Parameterized) type).getClassName(),null, null);
202-
return new J.ParameterizedType(Tree.randomId(), Space.EMPTY, Markers.EMPTY, clazz, JContainer.build(typeParams), type);
236+
return new J.ParameterizedType(Tree.randomId(), Space.EMPTY, Markers.EMPTY, clazz, JContainer.build(typeParamsExpression), type);
203237
}
204238

205239
throw new IllegalArgumentException(String.format("Unable to parse expression from JavaType %s", type));

src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocationsTest.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,36 @@ void m() {
188188
);
189189
}
190190

191-
191+
@Test
192+
void withJDKFactoryMethodsAndBounds() {
193+
//language=java
194+
rewriteRun(
195+
version(
196+
java("""
197+
package com.example.app;
198+
199+
import java.util.List;
200+
201+
class A {
202+
void m() {
203+
List<? extends String> lst = List.of("Test");
204+
}
205+
}
206+
""","""
207+
package com.example.app;
208+
209+
import java.util.List;
210+
211+
class A {
212+
void m() {
213+
var lst = List.of("Test");
214+
}
215+
}
216+
"""),
217+
10
218+
)
219+
);
220+
}
192221

193222
@Test
194223
void withOwnFactoryMethods() {

src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericsConstructorsTest.java

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ void twoParams() {
323323
)
324324
);
325325
}
326-
}
326+
}
327327

328328
@Test
329329
void ifWelldefined() {
@@ -357,6 +357,62 @@ void m() {
357357
);
358358
}
359359

360+
@Test
361+
void arrayAsType() {
362+
//language=java
363+
rewriteRun(
364+
version(
365+
java("""
366+
package com.example.app;
367+
368+
import java.util.List;
369+
import java.util.ArrayList;
370+
371+
class A {
372+
void m() {
373+
List<char[]> strs = new ArrayList<>();
374+
}
375+
}
376+
""","""
377+
package com.example.app;
378+
379+
import java.util.ArrayList;
380+
381+
class A {
382+
void m() {
383+
var strs = new ArrayList<char[]>();
384+
}
385+
}
386+
"""),
387+
10
388+
)
389+
);
390+
}
391+
392+
@Test
393+
void twoParamsWithBounds() {
394+
//language=java
395+
rewriteRun(
396+
version(
397+
java("""
398+
package com.example.app;
399+
400+
import java.util.Map;
401+
import java.util.LinkedHashMap;
402+
403+
class AbstractOAuth2Configurer {}
404+
405+
class A {
406+
void twoParams() {
407+
Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> configurers = new LinkedHashMap<>();
408+
}
409+
}
410+
"""),
411+
10
412+
)
413+
);
414+
}
415+
360416
@Test
361417
@Example
362418
void withTypeParameterInDefinitionOnly() {

0 commit comments

Comments
 (0)