Skip to content

Commit 506797a

Browse files
authored
Merge branch 'main' into recipe-for-InlineMe-annotation
2 parents e9ac12f + a2b5986 commit 506797a

15 files changed

+480
-195
lines changed

src/main/java/org/openrewrite/java/migrate/lang/NullCheckAsSwitchCase.java

Lines changed: 102 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@
2323
import org.openrewrite.java.JavaTemplate;
2424
import org.openrewrite.java.JavaVisitor;
2525
import org.openrewrite.java.search.SemanticallyEqual;
26-
import org.openrewrite.java.tree.Expression;
27-
import org.openrewrite.java.tree.J;
28-
import org.openrewrite.java.tree.Space;
29-
import org.openrewrite.java.tree.Statement;
26+
import org.openrewrite.java.search.UsesJavaVersion;
27+
import org.openrewrite.java.tree.*;
28+
import org.openrewrite.staticanalysis.groovy.GroovyFileChecker;
3029
import org.openrewrite.staticanalysis.kotlin.KotlinFileChecker;
3130

3231
import java.time.Duration;
32+
import java.util.ArrayList;
33+
import java.util.List;
34+
import java.util.Objects;
3335
import java.util.Optional;
3436
import java.util.concurrent.atomic.AtomicReference;
3537

@@ -58,7 +60,13 @@ public Duration getEstimatedEffortPerOccurrence() {
5860

5961
@Override
6062
public TreeVisitor<?, ExecutionContext> getVisitor() {
61-
return Preconditions.check(Preconditions.not(new KotlinFileChecker<>()), new JavaVisitor<ExecutionContext>() {
63+
TreeVisitor<?, ExecutionContext> preconditions = Preconditions.and(
64+
new UsesJavaVersion<>(21),
65+
Preconditions.not(new KotlinFileChecker<>()),
66+
Preconditions.not(new GroovyFileChecker<>())
67+
);
68+
69+
return Preconditions.check(preconditions, new JavaVisitor<ExecutionContext>() {
6270
@Override
6371
public J visitBlock(J.Block block, ExecutionContext ctx) {
6472
AtomicReference<@Nullable NullCheck> nullCheck = new AtomicReference<>();
@@ -68,13 +76,18 @@ public J visitBlock(J.Block block, ExecutionContext ctx) {
6876
if (nullCheckOpt.isPresent()) {
6977
NullCheck check = nullCheckOpt.get();
7078
J nextStatement = index + 1 < block.getStatements().size() ? block.getStatements().get(index + 1) : null;
71-
if (!(nextStatement instanceof J.Switch) ||
72-
hasNullCase((J.Switch) nextStatement) ||
73-
!SemanticallyEqual.areEqual(((J.Switch) nextStatement).getSelector().getTree(), check.getNullCheckedParameter()) ||
74-
check.returns() ||
75-
check.couldModifyNullCheckedValue()) {
79+
if (!(nextStatement instanceof J.Switch) || check.returns() || check.couldModifyNullCheckedValue()) {
80+
return statement;
81+
}
82+
J.Switch nextSwitch = (J.Switch) nextStatement;
83+
// Only if the switch does not have a null case and switches on the same value as the null check, we can remove the null check
84+
// It must have all possible input values covered
85+
if (hasNullCase(nextSwitch) ||
86+
!SemanticallyEqual.areEqual(nextSwitch.getSelector().getTree(), check.getNullCheckedParameter()) ||
87+
!coversAllPossibleValues(nextSwitch)) {
7688
return statement;
7789
}
90+
7891
nullCheck.set(check);
7992
return null;
8093
}
@@ -106,6 +119,16 @@ private boolean hasNullCase(J.Switch switch_) {
106119
}
107120

108121
private J.Case createNullCase(J.Switch aSwitch, Statement whenNull) {
122+
J.Case currentFirstCase = aSwitch.getCases().getStatements().isEmpty() ||
123+
!(aSwitch.getCases().getStatements().get(0) instanceof J.Case) ?
124+
null : (J.Case) aSwitch.getCases().getStatements().get(0);
125+
if (currentFirstCase == null || J.Case.Type.Rule == currentFirstCase.getType()) {
126+
return createCaseRule(aSwitch, whenNull);
127+
}
128+
return createCaseStatement(aSwitch, whenNull, currentFirstCase);
129+
}
130+
131+
private J.Case createCaseRule(J.Switch aSwitch, Statement whenNull) {
109132
if (whenNull instanceof J.Block && ((J.Block) whenNull).getStatements().size() == 1) {
110133
Statement firstStatement = ((J.Block) whenNull).getStatements().get(0);
111134
if (firstStatement instanceof Expression || firstStatement instanceof J.Throw) {
@@ -122,6 +145,75 @@ private J.Case createNullCase(J.Switch aSwitch, Statement whenNull) {
122145
J.Case nullCase = (J.Case) switchWithNullCase.getCases().getStatements().get(0);
123146
return nullCase.withBody(requireNonNull(nullCase.getBody()).withPrefix(Space.SINGLE_SPACE));
124147
}
148+
149+
private J.Case createCaseStatement(J.Switch aSwitch, Statement whenNull, J.Case currentFirstCase) {
150+
List<J> statements = new ArrayList<>();
151+
statements.add(aSwitch.getSelector().getTree());
152+
if (whenNull instanceof J.Block) {
153+
statements.addAll(((J.Block) whenNull).getStatements());
154+
} else {
155+
statements.add(whenNull);
156+
}
157+
158+
// Check if the last statement is a throw statement
159+
Statement lastStatement = null;
160+
if (whenNull instanceof J.Block) {
161+
List<Statement> blockStatements = ((J.Block) whenNull).getStatements();
162+
if (!blockStatements.isEmpty()) {
163+
lastStatement = blockStatements.get(blockStatements.size() - 1);
164+
}
165+
} else {
166+
lastStatement = whenNull;
167+
}
168+
169+
StringBuilder template = new StringBuilder("switch(#{any()}) {\ncase null:");
170+
for (int i = 1; i < statements.size(); i++) {
171+
template.append("\n#{any()};");
172+
}
173+
if (!(lastStatement instanceof J.Throw)) {
174+
template.append("\nbreak;");
175+
}
176+
template.append("\n}");
177+
J.Switch switchWithNullCase = JavaTemplate.apply(
178+
template.toString(),
179+
new Cursor(getCursor(), aSwitch),
180+
aSwitch.getCoordinates().replace(),
181+
statements.toArray());
182+
J.Case nullCase = (J.Case) switchWithNullCase.getCases().getStatements().get(0);
183+
Space currentFirstCaseIndentation = currentFirstCase.getStatements().stream().map(J::getPrefix).findFirst().orElse(Space.SINGLE_SPACE);
184+
185+
return nullCase.withStatements(ListUtils.mapFirst(nullCase.getStatements(), s -> s == null ? null : s.withPrefix(currentFirstCaseIndentation)));
186+
}
187+
188+
private boolean coversAllPossibleValues(J.Switch switch_) {
189+
List<J> labels = new ArrayList<>();
190+
for (Statement statement : switch_.getCases().getStatements()) {
191+
for (J j : ((J.Case) statement).getCaseLabels()) {
192+
if (j instanceof J.Identifier && "default".equals(((J.Identifier) j).getSimpleName())) {
193+
return true;
194+
}
195+
labels.add(j);
196+
}
197+
}
198+
JavaType javaType = switch_.getSelector().getTree().getType();
199+
if (javaType instanceof JavaType.Class && ((JavaType.Class) javaType).getKind() == JavaType.FullyQualified.Kind.Enum) {
200+
// Every enum value must be present in the switch
201+
return ((JavaType.Class) javaType).getMembers().stream().allMatch(variable ->
202+
labels.stream().anyMatch(label -> {
203+
if (!(label instanceof TypeTree && TypeUtils.isOfType(((TypeTree) label).getType(), javaType))) {
204+
return false;
205+
}
206+
J.Identifier enumName = null;
207+
if (label instanceof J.Identifier) {
208+
enumName = (J.Identifier) label;
209+
} else if (label instanceof J.FieldAccess) {
210+
enumName = ((J.FieldAccess) label).getName();
211+
}
212+
return enumName != null && Objects.equals(variable.getName(), enumName.getSimpleName());
213+
}));
214+
}
215+
return false;
216+
}
125217
});
126218
}
127219
}

src/main/java/org/openrewrite/java/migrate/lang/RefineSwitchCases.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
import org.openrewrite.*;
2121
import org.openrewrite.internal.ListUtils;
2222
import org.openrewrite.java.JavaIsoVisitor;
23+
import org.openrewrite.java.search.UsesJavaVersion;
2324
import org.openrewrite.java.tree.Expression;
2425
import org.openrewrite.java.tree.J;
2526
import org.openrewrite.java.tree.Space;
2627
import org.openrewrite.java.tree.Statement;
28+
import org.openrewrite.staticanalysis.groovy.GroovyFileChecker;
2729
import org.openrewrite.staticanalysis.kotlin.KotlinFileChecker;
2830

2931
import java.util.ArrayList;
@@ -50,7 +52,13 @@ public String getDescription() {
5052

5153
@Override
5254
public TreeVisitor<?, ExecutionContext> getVisitor() {
53-
return Preconditions.check(Preconditions.not(new KotlinFileChecker<>()), new JavaIsoVisitor<ExecutionContext>() {
55+
TreeVisitor<?, ExecutionContext> preconditions = Preconditions.and(
56+
new UsesJavaVersion<>(21),
57+
Preconditions.not(new KotlinFileChecker<>()),
58+
Preconditions.not(new GroovyFileChecker<>())
59+
);
60+
61+
return Preconditions.check(preconditions, new JavaIsoVisitor<ExecutionContext>() {
5462
@Override
5563
public J.Switch visitSwitch(J.Switch sw, ExecutionContext ctx) {
5664
J.Switch switch_ = super.visitSwitch(sw, ctx);

src/main/java/org/openrewrite/java/migrate/lang/SwitchCaseEnumGuardToLabel.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
2424
import org.openrewrite.TreeVisitor;
2525
import org.openrewrite.java.JavaIsoVisitor;
2626
import org.openrewrite.java.JavaVisitor;
27+
import org.openrewrite.java.search.UsesJavaVersion;
2728
import org.openrewrite.java.tree.Expression;
2829
import org.openrewrite.java.tree.J;
2930
import org.openrewrite.java.tree.JavaType;
3031
import org.openrewrite.java.tree.TypeUtils;
32+
import org.openrewrite.staticanalysis.groovy.GroovyFileChecker;
3133
import org.openrewrite.staticanalysis.kotlin.KotlinFileChecker;
3234

3335
import static java.util.Collections.singletonList;
@@ -47,7 +49,13 @@ public String getDescription() {
4749

4850
@Override
4951
public TreeVisitor<?, ExecutionContext> getVisitor() {
50-
return Preconditions.check(Preconditions.not(new KotlinFileChecker<>()), new JavaIsoVisitor<ExecutionContext>() {
52+
TreeVisitor<?, ExecutionContext> preconditions = Preconditions.and(
53+
new UsesJavaVersion<>(21),
54+
Preconditions.not(new KotlinFileChecker<>()),
55+
Preconditions.not(new GroovyFileChecker<>())
56+
);
57+
58+
return Preconditions.check(preconditions, new JavaIsoVisitor<ExecutionContext>() {
5159

5260
@Override
5361
public J.Case visitCase(J.Case case_, ExecutionContext ctx) {

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
}

0 commit comments

Comments
 (0)