Skip to content

Commit 0b00944

Browse files
jevanlingentimtebeekgithub-actions[bot]
authored
Improve recipes and excluded them from C# codebase if needed (#396)
* Improve recipes and excluded them from csharp codebase if needed * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Improve recipes and excluded them from csharp codebase if needed * Update src/main/java/org/openrewrite/staticanalysis/NewStringBuilderBufferWithCharArgument.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Apply suggestions from code review * Apply suggestions from code review --------- Co-authored-by: Tim te Beek <[email protected]> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 14f7766 commit 0b00944

22 files changed

+511
-595
lines changed

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

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,18 @@
3030

3131
import java.util.Arrays;
3232
import java.util.Collections;
33-
import java.util.HashSet;
33+
import java.util.List;
3434
import java.util.Set;
3535

3636
public class AtomicPrimitiveEqualsUsesGet extends Recipe {
3737

38-
private static final Set<String> ATOMIC_PRIMITIVE_TYPES = new HashSet<>(Arrays.asList(
39-
"java.util.concurrent.atomic.AtomicBoolean",
40-
"java.util.concurrent.atomic.AtomicInteger",
41-
"java.util.concurrent.atomic.AtomicLong"
42-
));
38+
public static final String ATOMIC_ATOMIC_BOOLEAN = "java.util.concurrent.atomic.AtomicBoolean";
39+
public static final String ATOMIC_ATOMIC_INTEGER = "java.util.concurrent.atomic.AtomicInteger";
40+
public static final String ATOMIC_ATOMIC_LONG = "java.util.concurrent.atomic.AtomicLong";
41+
42+
private static final List<String> ATOMIC_PRIMITIVE_TYPES = Arrays.asList(
43+
ATOMIC_ATOMIC_BOOLEAN, ATOMIC_ATOMIC_INTEGER, ATOMIC_ATOMIC_LONG
44+
);
4345

4446
@Override
4547
public String getDisplayName() {
@@ -59,9 +61,9 @@ public Set<String> getTags() {
5961
@Override
6062
public TreeVisitor<?, ExecutionContext> getVisitor() {
6163
return Preconditions.check(Preconditions.or(
62-
new UsesType<>("java.util.concurrent.atomic.AtomicBoolean", false),
63-
new UsesType<>("java.util.concurrent.atomic.AtomicInteger", false),
64-
new UsesType<>("java.util.concurrent.atomic.AtomicLong", false)
64+
new UsesType<>(ATOMIC_ATOMIC_BOOLEAN, false),
65+
new UsesType<>(ATOMIC_ATOMIC_INTEGER, false),
66+
new UsesType<>(ATOMIC_ATOMIC_LONG, false)
6567
), new JavaVisitor<ExecutionContext>() {
6668
private final MethodMatcher aiMethodMatcher = new MethodMatcher("java.lang.Object equals(java.lang.Object)");
6769

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

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
import java.util.Set;
3232

3333
public class CaseInsensitiveComparisonsDoNotChangeCase extends Recipe {
34+
35+
private static final MethodMatcher COMPARE_IGNORE_CASE_METHOD_MATCHER = new MethodMatcher("java.lang.String equalsIgnoreCase(java.lang.String)");
36+
private static final MethodMatcher TO_LOWER_CASE_METHOD_MATCHER = new MethodMatcher("java.lang.String toLowerCase()");
37+
private static final MethodMatcher TO_UPPER_CASE_METHOD_MATCHER = new MethodMatcher("java.lang.String toUpperCase()");
38+
3439
@Override
3540
public String getDisplayName() {
3641
return "CaseInsensitive comparisons do not alter case";
@@ -53,38 +58,32 @@ public Duration getEstimatedEffortPerOccurrence() {
5358

5459
@Override
5560
public TreeVisitor<?, ExecutionContext> getVisitor() {
56-
return Preconditions.check(new UsesMethod<>("java.lang.String equalsIgnoreCase(java.lang.String)"), new CaseInsensitiveComparisonVisitor<>());
57-
}
58-
59-
private static class CaseInsensitiveComparisonVisitor<ExecutionContext> extends JavaIsoVisitor<ExecutionContext> {
60-
private static final MethodMatcher COMPARE_IGNORE_CASE_METHOD_MATCHER = new MethodMatcher("java.lang.String equalsIgnoreCase(java.lang.String)");
61-
private static final MethodMatcher TO_LOWER_CASE_METHOD_MATCHER = new MethodMatcher("java.lang.String toLowerCase()");
62-
private static final MethodMatcher TO_UPPER_CASE_METHOD_MATCHER = new MethodMatcher("java.lang.String toUpperCase()");
63-
64-
@Override
65-
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) {
66-
J.MethodInvocation mi = super.visitMethodInvocation(method, executionContext);
67-
if (COMPARE_IGNORE_CASE_METHOD_MATCHER.matches(mi)) {
68-
mi = mi.withArguments(ListUtils.map(mi.getArguments(), arg -> {
69-
if (arg instanceof J.MethodInvocation && isChangeCaseMethod(arg)) {
70-
return ((J.MethodInvocation) arg).getSelect();
61+
return Preconditions.check(new UsesMethod<>(COMPARE_IGNORE_CASE_METHOD_MATCHER), new JavaIsoVisitor<ExecutionContext>() {
62+
@Override
63+
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
64+
J.MethodInvocation mi = super.visitMethodInvocation(method, ctx);
65+
if (COMPARE_IGNORE_CASE_METHOD_MATCHER.matches(mi)) {
66+
mi = mi.withArguments(ListUtils.map(mi.getArguments(), arg -> {
67+
if (arg instanceof J.MethodInvocation && isChangeCaseMethod(arg)) {
68+
return ((J.MethodInvocation) arg).getSelect();
69+
}
70+
return arg;
71+
}));
72+
if (isChangeCaseMethod(mi.getSelect())) {
73+
J.MethodInvocation mChangeCase = (J.MethodInvocation) mi.getSelect();
74+
mi = mi.withSelect(mChangeCase.getSelect());
7175
}
72-
return arg;
73-
}));
74-
if (isChangeCaseMethod(mi.getSelect())) {
75-
J.MethodInvocation mChangeCase = (J.MethodInvocation) mi.getSelect();
76-
mi = mi.withSelect(mChangeCase.getSelect());
7776
}
77+
return mi;
7878
}
79-
return mi;
80-
}
8179

82-
private boolean isChangeCaseMethod(@Nullable J j) {
83-
if (j instanceof J.MethodInvocation) {
84-
J.MethodInvocation mi = (J.MethodInvocation) j;
85-
return TO_LOWER_CASE_METHOD_MATCHER.matches(mi) || TO_UPPER_CASE_METHOD_MATCHER.matches(mi);
80+
private boolean isChangeCaseMethod(@Nullable J j) {
81+
if (j instanceof J.MethodInvocation) {
82+
J.MethodInvocation mi = (J.MethodInvocation) j;
83+
return TO_LOWER_CASE_METHOD_MATCHER.matches(mi) || TO_UPPER_CASE_METHOD_MATCHER.matches(mi);
84+
}
85+
return false;
8686
}
87-
return false;
88-
}
87+
});
8988
}
9089
}

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

Lines changed: 67 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,23 @@
2020
import org.openrewrite.java.JavaIsoVisitor;
2121
import org.openrewrite.java.JavaTemplate;
2222
import org.openrewrite.java.MethodMatcher;
23+
import org.openrewrite.java.search.DeclaresMethod;
2324
import org.openrewrite.java.service.AnnotationService;
2425
import org.openrewrite.java.tree.J;
2526
import org.openrewrite.java.tree.JavaType;
27+
import org.openrewrite.staticanalysis.csharp.CSharpFileChecker;
2628

27-
import java.time.Duration;
2829
import java.util.Collections;
2930
import java.util.Comparator;
3031
import java.util.Set;
31-
import java.util.stream.Stream;
3232

3333
@Incubating(since = "7.0.0")
3434
public class CovariantEquals extends Recipe {
3535

36+
private static final MethodMatcher EQUALS_MATCHER = new MethodMatcher("* equals(..)");
37+
private static final MethodMatcher EQUALS_OBJECT_MATCHER = new MethodMatcher("* equals(java.lang.Object)");
38+
private static final AnnotationMatcher OVERRIDE_ANNOTATION = new AnnotationMatcher("@java.lang.Override");
39+
3640
@Override
3741
public String getDisplayName() {
3842
return "Covariant equals";
@@ -49,105 +53,79 @@ public Set<String> getTags() {
4953
return Collections.singleton("RSPEC-S2162");
5054
}
5155

52-
@Override
53-
public Duration getEstimatedEffortPerOccurrence() {
54-
return Duration.ofMinutes(5);
55-
}
56-
5756
@Override
5857
public TreeVisitor<?, ExecutionContext> getVisitor() {
59-
MethodMatcher objectEquals = new MethodMatcher("* equals(java.lang.Object)");
60-
return new JavaIsoVisitor<ExecutionContext>() {
61-
58+
TreeVisitor<?, ExecutionContext> conditions = Preconditions.and(
59+
new DeclaresMethod<>(EQUALS_MATCHER),
60+
Preconditions.not(new DeclaresMethod<>(EQUALS_OBJECT_MATCHER)),
61+
Preconditions.not(new CSharpFileChecker<>())
62+
);
63+
return Preconditions.check(conditions, Repeat.repeatUntilStable(new JavaIsoVisitor<ExecutionContext>() {
6264
@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;
65+
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
66+
J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx);
67+
J.ClassDeclaration enclosingClass = getCursor().dropParentUntil(p -> p instanceof J.ClassDeclaration).getValue();
68+
69+
/*
70+
* Looking for "public boolean equals(EnclosingClassType)" as the method signature match.
71+
* We'll replace it with "public boolean equals(Object)"
72+
*/
73+
JavaType.FullyQualified type = enclosingClass.getType();
74+
if (type == null || type instanceof JavaType.Unknown) {
75+
return m;
8276
}
8377

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 &&
78+
String ecfqn = type.getFullyQualifiedName();
79+
if (m.hasModifier(J.Modifier.Type.Public) && m.getReturnTypeExpression() != null &&
10180
JavaType.Primitive.Boolean.equals(m.getReturnTypeExpression().getType()) &&
10281
new MethodMatcher(ecfqn + " equals(" + ecfqn + ")").matches(m, enclosingClass)) {
10382

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()
83+
if (!service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION)) {
84+
m = JavaTemplate.builder("@Override").build()
11885
.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);
86+
m.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName)));
14687
}
14788

148-
return m;
89+
/*
90+
* Change parameter type to Object, and maybe change input parameter name representing the other object.
91+
* This is because we prepend these type-checking replacement statements to the existing "equals(..)" body.
92+
* Therefore we don't want to collide with any existing variable names.
93+
*/
94+
J.VariableDeclarations.NamedVariable oldParamName = ((J.VariableDeclarations) m.getParameters().get(0)).getVariables().get(0);
95+
String paramName = "obj".equals(oldParamName.getSimpleName()) ? "other" : "obj";
96+
m = JavaTemplate.builder("Object #{}").build()
97+
.apply(updateCursor(m),
98+
m.getCoordinates().replaceParameters(),
99+
paramName);
100+
101+
/*
102+
* We'll prepend this type-check and type-cast to the beginning of the existing
103+
* equals(..) method body statements, and let the existing equals(..) method definition continue
104+
* with the logic doing what it was doing.
105+
*/
106+
String equalsBodyPrefixTemplate = "if (#{} == this) return true;\n" +
107+
"if (#{} == null || getClass() != #{}.getClass()) return false;\n" +
108+
"#{} #{} = (#{}) #{};\n";
109+
JavaTemplate equalsBodySnippet = JavaTemplate.builder(equalsBodyPrefixTemplate).contextSensitive().build();
110+
111+
assert m.getBody() != null;
112+
Object[] params = new Object[]{
113+
paramName,
114+
paramName,
115+
paramName,
116+
enclosingClass.getSimpleName(),
117+
oldParamName.getSimpleName(),
118+
enclosingClass.getSimpleName(),
119+
paramName
120+
};
121+
122+
m = equalsBodySnippet.apply(new Cursor(getCursor().getParent(), m),
123+
m.getBody().getStatements().get(0).getCoordinates().before(),
124+
params);
149125
}
126+
127+
return m;
150128
}
151-
};
129+
}));
152130
}
153131
}

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

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,20 @@ public Duration getEstimatedEffortPerOccurrence() {
5353

5454
@Override
5555
public TreeVisitor<?, ExecutionContext> getVisitor() {
56-
return new EqualsAvoidsNullFromCompilationUnitStyle();
57-
}
58-
59-
private static class EqualsAvoidsNullFromCompilationUnitStyle extends JavaIsoVisitor<ExecutionContext> {
60-
@Override
61-
public J visit(@Nullable Tree tree, ExecutionContext ctx) {
62-
if (tree instanceof JavaSourceFile) {
63-
JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree);
64-
EqualsAvoidsNullStyle style = cu.getStyle(EqualsAvoidsNullStyle.class);
65-
if (style == null) {
66-
style = Checkstyle.equalsAvoidsNull();
56+
return new JavaIsoVisitor<ExecutionContext>() {
57+
@Override
58+
public J visit(@Nullable Tree tree, ExecutionContext ctx) {
59+
if (tree instanceof JavaSourceFile) {
60+
JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree);
61+
EqualsAvoidsNullStyle style = cu.getStyle(EqualsAvoidsNullStyle.class);
62+
if (style == null) {
63+
style = Checkstyle.equalsAvoidsNull();
64+
}
65+
return new EqualsAvoidsNullVisitor<>(style).visitNonNull(cu, ctx);
6766
}
68-
return new EqualsAvoidsNullVisitor<>(style).visitNonNull(cu, ctx);
67+
//noinspection DataFlowIssue
68+
return (J) tree;
6969
}
70-
//noinspection DataFlowIssue
71-
return (J) tree;
72-
}
70+
};
7371
}
7472
}

0 commit comments

Comments
 (0)