Skip to content

Commit 6d04813

Browse files
authored
Sweep preconditions to prepare for C# running (#319)
* Sweep preconditions to prepare for C# running
1 parent aa561cd commit 6d04813

30 files changed

Lines changed: 213 additions & 223 deletions

src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
6666
public J visit(@Nullable Tree tree, ExecutionContext ctx) {
6767
if (tree instanceof JavaSourceFile) {
6868
JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree);
69-
TabsAndIndentsStyle style = ((SourceFile) cu).getStyle(TabsAndIndentsStyle.class);
69+
TabsAndIndentsStyle style = cu.getStyle(TabsAndIndentsStyle.class);
7070
if (style == null) {
7171
style = IntelliJ.tabsAndIndents();
7272
}

src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java

Lines changed: 104 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,20 @@
1515
*/
1616
package org.openrewrite.staticanalysis;
1717

18-
import org.openrewrite.ExecutionContext;
19-
import org.openrewrite.Incubating;
20-
import org.openrewrite.Recipe;
21-
import org.openrewrite.TreeVisitor;
18+
import org.openrewrite.*;
19+
import org.openrewrite.java.AnnotationMatcher;
20+
import org.openrewrite.java.JavaIsoVisitor;
21+
import org.openrewrite.java.JavaTemplate;
22+
import org.openrewrite.java.MethodMatcher;
23+
import org.openrewrite.java.service.AnnotationService;
24+
import org.openrewrite.java.tree.J;
25+
import org.openrewrite.java.tree.JavaType;
2226

2327
import java.time.Duration;
2428
import java.util.Collections;
29+
import java.util.Comparator;
2530
import java.util.Set;
31+
import java.util.stream.Stream;
2632

2733
@Incubating(since = "7.0.0")
2834
public class CovariantEquals extends Recipe {
@@ -35,7 +41,7 @@ public String getDisplayName() {
3541
@Override
3642
public String getDescription() {
3743
return "Checks that classes and records which define a covariant `equals()` method also override method `equals(Object)`. " +
38-
"Covariant `equals()` means a method that is similar to `equals(Object)`, but with a covariant parameter type (any subtype of `Object`).";
44+
"Covariant `equals()` means a method that is similar to `equals(Object)`, but with a covariant parameter type (any subtype of `Object`).";
3945
}
4046

4147
@Override
@@ -50,6 +56,98 @@ public Duration getEstimatedEffortPerOccurrence() {
5056

5157
@Override
5258
public TreeVisitor<?, ExecutionContext> getVisitor() {
53-
return new CovariantEqualsVisitor<>();
59+
MethodMatcher objectEquals = new MethodMatcher("* equals(java.lang.Object)");
60+
return new JavaIsoVisitor<ExecutionContext>() {
61+
62+
@Override
63+
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
64+
J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
65+
Stream<J.MethodDeclaration> mds = cd.getBody().getStatements().stream()
66+
.filter(J.MethodDeclaration.class::isInstance)
67+
.map(J.MethodDeclaration.class::cast);
68+
if (cd.getKind() != J.ClassDeclaration.Kind.Type.Interface && mds.noneMatch(m -> objectEquals.matches(m, classDecl))) {
69+
cd = (J.ClassDeclaration) new ChangeCovariantEqualsMethodVisitor(cd).visit(cd, ctx, getCursor().getParentOrThrow());
70+
assert cd != null;
71+
}
72+
return cd;
73+
}
74+
75+
class ChangeCovariantEqualsMethodVisitor extends JavaIsoVisitor<ExecutionContext> {
76+
private final AnnotationMatcher OVERRIDE_ANNOTATION = new AnnotationMatcher("@java.lang.Override");
77+
78+
private final J.ClassDeclaration enclosingClass;
79+
80+
public ChangeCovariantEqualsMethodVisitor(J.ClassDeclaration enclosingClass) {
81+
this.enclosingClass = enclosingClass;
82+
}
83+
84+
@Override
85+
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
86+
J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx);
87+
updateCursor(m);
88+
89+
/*
90+
* Looking for "public boolean equals(EnclosingClassType)" as the method signature match.
91+
* We'll replace it with "public boolean equals(Object)"
92+
*/
93+
JavaType.FullyQualified type = enclosingClass.getType();
94+
if (type == null || type instanceof JavaType.Unknown) {
95+
return m;
96+
}
97+
98+
String ecfqn = type.getFullyQualifiedName();
99+
if (m.hasModifier(J.Modifier.Type.Public) &&
100+
m.getReturnTypeExpression() != null &&
101+
JavaType.Primitive.Boolean.equals(m.getReturnTypeExpression().getType()) &&
102+
new MethodMatcher(ecfqn + " equals(" + ecfqn + ")").matches(m, enclosingClass)) {
103+
104+
if (!service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION)) {
105+
m = JavaTemplate.builder("@Override").build()
106+
.apply(updateCursor(m),
107+
m.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName)));
108+
}
109+
110+
/*
111+
* Change parameter type to Object, and maybe change input parameter name representing the other object.
112+
* This is because we prepend these type-checking replacement statements to the existing "equals(..)" body.
113+
* Therefore we don't want to collide with any existing variable names.
114+
*/
115+
J.VariableDeclarations.NamedVariable oldParamName = ((J.VariableDeclarations) m.getParameters().get(0)).getVariables().get(0);
116+
String paramName = "obj".equals(oldParamName.getSimpleName()) ? "other" : "obj";
117+
m = JavaTemplate.builder("Object #{}").build()
118+
.apply(updateCursor(m),
119+
m.getCoordinates().replaceParameters(),
120+
paramName);
121+
122+
/*
123+
* We'll prepend this type-check and type-cast to the beginning of the existing
124+
* equals(..) method body statements, and let the existing equals(..) method definition continue
125+
* with the logic doing what it was doing.
126+
*/
127+
String equalsBodyPrefixTemplate = "if (#{} == this) return true;\n" +
128+
"if (#{} == null || getClass() != #{}.getClass()) return false;\n" +
129+
"#{} #{} = (#{}) #{};\n";
130+
JavaTemplate equalsBodySnippet = JavaTemplate.builder(equalsBodyPrefixTemplate).contextSensitive().build();
131+
132+
assert m.getBody() != null;
133+
Object[] params = new Object[]{
134+
paramName,
135+
paramName,
136+
paramName,
137+
enclosingClass.getSimpleName(),
138+
oldParamName.getSimpleName(),
139+
enclosingClass.getSimpleName(),
140+
paramName
141+
};
142+
143+
m = equalsBodySnippet.apply(new Cursor(getCursor().getParent(), m),
144+
m.getBody().getStatements().get(0).getCoordinates().before(),
145+
params);
146+
}
147+
148+
return m;
149+
}
150+
}
151+
};
54152
}
55153
}

src/main/java/org/openrewrite/staticanalysis/CovariantEqualsVisitor.java

Lines changed: 0 additions & 125 deletions
This file was deleted.

src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ private static class DefaultComesLastFromCompilationUnitStyle extends JavaIsoVis
6161
public J visit(@Nullable Tree tree, ExecutionContext ctx) {
6262
if (tree instanceof JavaSourceFile) {
6363
JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree);
64-
DefaultComesLastStyle style = ((SourceFile) cu).getStyle(DefaultComesLastStyle.class);
64+
DefaultComesLastStyle style = cu.getStyle(DefaultComesLastStyle.class);
6565
if (style == null) {
6666
style = Checkstyle.defaultComesLast();
6767
}
@@ -70,5 +70,4 @@ public J visit(@Nullable Tree tree, ExecutionContext ctx) {
7070
return (J) tree;
7171
}
7272
}
73-
7473
}

src/main/java/org/openrewrite/staticanalysis/EmptyBlock.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ private static class EmptyBlockFromCompilationUnitStyle extends JavaIsoVisitor<E
6161
public J visit(@Nullable Tree tree, ExecutionContext ctx) {
6262
if (tree instanceof JavaSourceFile) {
6363
JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree);
64-
EmptyBlockStyle style = ((SourceFile) cu).getStyle(EmptyBlockStyle.class);
64+
EmptyBlockStyle style = cu.getStyle(EmptyBlockStyle.class);
6565
if (style == null) {
6666
style = Checkstyle.emptyBlock();
6767
}

src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ private static class EqualsAvoidsNullFromCompilationUnitStyle extends JavaIsoVis
6161
public J visit(@Nullable Tree tree, ExecutionContext ctx) {
6262
if (tree instanceof JavaSourceFile) {
6363
JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree);
64-
EqualsAvoidsNullStyle style = ((SourceFile) cu).getStyle(EqualsAvoidsNullStyle.class);
64+
EqualsAvoidsNullStyle style = cu.getStyle(EqualsAvoidsNullStyle.class);
6565
if (style == null) {
6666
style = Checkstyle.equalsAvoidsNull();
6767
}

src/main/java/org/openrewrite/staticanalysis/ExplicitInitialization.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
6363
public J visit(@Nullable Tree tree, ExecutionContext ctx) {
6464
if (tree instanceof JavaSourceFile) {
6565
JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree);
66-
ExplicitInitializationStyle style = ((SourceFile) cu).getStyle(ExplicitInitializationStyle.class);
66+
ExplicitInitializationStyle style = cu.getStyle(ExplicitInitializationStyle.class);
6767
if (style == null) {
6868
style = Checkstyle.explicitInitialization();
6969
}

src/main/java/org/openrewrite/staticanalysis/FallThrough.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ private static class FallThroughFromCompilationUnitStyle extends JavaIsoVisitor<
6060
public J visit(@Nullable Tree tree, ExecutionContext ctx) {
6161
if (tree instanceof JavaSourceFile) {
6262
JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree);
63-
FallThroughStyle style = ((SourceFile) cu).getStyle(FallThroughStyle.class);
63+
FallThroughStyle style = cu.getStyle(FallThroughStyle.class);
6464
if (style == null) {
6565
style = Checkstyle.fallThrough();
6666
}

src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public J.Case visitCase(J.Case case_, P p) {
5454
J.Switch switch_ = getCursor().dropParentUntil(J.Switch.class::isInstance).getValue();
5555
if (Boolean.TRUE.equals(style.getCheckLastCaseGroup()) || !isLastCase(case_, switch_)) {
5656
if (FindLastLineBreaksOrFallsThroughComments.find(switch_, c).isEmpty()) {
57-
c = (J.Case) new AddBreak<>(c).visit(c, p, getCursor().getParent());
57+
c = (J.Case) new AddBreak<>(c).visitNonNull(c, p, getCursor().getParentOrThrow());
5858
}
5959
}
6060
}

src/main/java/org/openrewrite/staticanalysis/HiddenField.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ private static class HiddenFieldFromCompilationUnitStyle extends JavaIsoVisitor<
6161
public J visit(@Nullable Tree tree, ExecutionContext ctx) {
6262
if (tree instanceof JavaSourceFile) {
6363
JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree);
64-
HiddenFieldStyle style = ((SourceFile) cu).getStyle(HiddenFieldStyle.class);
64+
HiddenFieldStyle style = cu.getStyle(HiddenFieldStyle.class);
6565
if (style == null) {
6666
style = Checkstyle.hiddenFieldStyle();
6767
}

0 commit comments

Comments
 (0)