Skip to content

Commit 1185126

Browse files
Support fluent chains in RemoveMethodCallVisitor (#340)
* refactor: Pull out conditions from RemoveMethodCallVisitor * feat: Support removing method invocations from fluent chains * refactor: Use new methods to streamline visitNewClass also * Slight polish * Inline call to MethodMatcher --------- Co-authored-by: Tim te Beek <[email protected]>
1 parent 337235e commit 1185126

File tree

2 files changed

+71
-29
lines changed

2 files changed

+71
-29
lines changed

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

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,9 @@
1919
import org.jspecify.annotations.Nullable;
2020
import org.openrewrite.java.JavaIsoVisitor;
2121
import org.openrewrite.java.MethodMatcher;
22-
import org.openrewrite.java.tree.Expression;
23-
import org.openrewrite.java.tree.J;
24-
import org.openrewrite.java.tree.MethodCall;
22+
import org.openrewrite.java.tree.*;
2523

2624
import java.util.function.BiPredicate;
27-
import java.util.function.Supplier;
2825

2926
/**
3027
* Removes all {@link MethodCall} matching both the
@@ -48,33 +45,48 @@ public class RemoveMethodCallVisitor<P> extends JavaIsoVisitor<P> {
4845
@SuppressWarnings("NullableProblems")
4946
@Override
5047
public J.@Nullable NewClass visitNewClass(J.NewClass newClass, P p) {
51-
return visitMethodCall(newClass, () -> super.visitNewClass(newClass, p));
48+
if (methodMatcher.matches(newClass) && predicateMatchesAllArguments(newClass) && isStatementInParentBlock(newClass)) {
49+
if (newClass.getMethodType() != null) {
50+
maybeRemoveImport(newClass.getMethodType().getDeclaringType());
51+
}
52+
return null;
53+
}
54+
return super.visitNewClass(newClass, p);
5255
}
5356

5457
@SuppressWarnings("NullableProblems")
5558
@Override
5659
public J.@Nullable MethodInvocation visitMethodInvocation(J.MethodInvocation method, P p) {
57-
return visitMethodCall(method, () -> super.visitMethodInvocation(method, p));
58-
}
60+
// Find method invocations that match the specified method and arguments
61+
if (methodMatcher.matches(method) && predicateMatchesAllArguments(method)) {
62+
// If the method invocation is a standalone statement, remove it altogether
63+
if (isStatementInParentBlock(method)) {
64+
if (method.getMethodType() != null) {
65+
maybeRemoveImport(method.getMethodType().getDeclaringType());
66+
}
67+
return null;
68+
}
5969

60-
private <M extends MethodCall> @Nullable M visitMethodCall(M methodCall, Supplier<M> visitSuper) {
61-
if (!methodMatcher.matches(methodCall)) {
62-
return visitSuper.get();
63-
}
64-
J.Block parentBlock = getCursor().firstEnclosing(J.Block.class);
65-
//noinspection SuspiciousMethodCalls
66-
if (parentBlock != null && !parentBlock.getStatements().contains(methodCall)) {
67-
return visitSuper.get();
68-
}
69-
// Remove the method invocation when the argumentMatcherPredicate is true for all arguments
70-
for (int i = 0; i < methodCall.getArguments().size(); i++) {
71-
if (!argumentPredicate.test(i, methodCall.getArguments().get(i))) {
72-
return visitSuper.get();
70+
// If the method invocation is in a fluent chain, remove just the current invocation
71+
if (method.getSelect() instanceof J.MethodInvocation &&
72+
TypeUtils.isOfType(method.getType(), method.getSelect().getType())) {
73+
return super.visitMethodInvocation((J.MethodInvocation) method.getSelect(), p);
7374
}
7475
}
75-
if (methodCall.getMethodType() != null) {
76-
maybeRemoveImport(methodCall.getMethodType().getDeclaringType());
76+
return super.visitMethodInvocation(method, p);
77+
}
78+
79+
private boolean predicateMatchesAllArguments(MethodCall method) {
80+
for (int i = 0; i < method.getArguments().size(); i++) {
81+
if (!argumentPredicate.test(i, method.getArguments().get(i))) {
82+
return false;
83+
}
7784
}
78-
return null;
85+
return true;
86+
}
87+
88+
private boolean isStatementInParentBlock(Statement method) {
89+
J.Block parentBlock = getCursor().firstEnclosing(J.Block.class);
90+
return parentBlock == null || parentBlock.getStatements().contains(method);
7991
}
8092
}

src/test/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitorTest.java

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ void assertTrueIsRemoved() {
4343
"""
4444
abstract class Test {
4545
abstract void assertTrue(boolean condition);
46-
46+
4747
void test() {
4848
System.out.println("Hello");
4949
assertTrue(true);
@@ -53,7 +53,7 @@ void test() {
5353
""", """
5454
abstract class Test {
5555
abstract void assertTrue(boolean condition);
56-
56+
5757
void test() {
5858
System.out.println("Hello");
5959
System.out.println("World");
@@ -72,7 +72,7 @@ void assertTrueFalseIsNotRemoved() {
7272
"""
7373
abstract class Test {
7474
abstract void assertTrue(boolean condition);
75-
75+
7676
void test() {
7777
System.out.println("Hello");
7878
assertTrue(false);
@@ -95,7 +95,7 @@ void assertTrueTwoArgIsRemoved() {
9595
"""
9696
abstract class Test {
9797
abstract void assertTrue(String message, boolean condition);
98-
98+
9999
void test() {
100100
System.out.println("Hello");
101101
assertTrue("message", true);
@@ -106,7 +106,7 @@ void test() {
106106
"""
107107
abstract class Test {
108108
abstract void assertTrue(String message, boolean condition);
109-
109+
110110
void test() {
111111
System.out.println("Hello");
112112
System.out.println("World");
@@ -125,7 +125,7 @@ void doesNotRemoveAssertTrueIfReturnValueIsUsed() {
125125
"""
126126
abstract class Test {
127127
abstract int assertTrue(boolean condition);
128-
128+
129129
void test() {
130130
System.out.println("Hello");
131131
int value = assertTrue(true);
@@ -136,4 +136,34 @@ void test() {
136136
)
137137
);
138138
}
139+
140+
@Test
141+
void removeMethodCallFromFluentChain() {
142+
rewriteRun(
143+
spec -> spec.recipe(RewriteTest.toRecipe(() -> new RemoveMethodCallVisitor<>(
144+
new MethodMatcher("java.lang.StringBuilder append(..)"), (i, e) -> true))),
145+
// language=java
146+
java(
147+
"""
148+
class Main {
149+
void hello() {
150+
final String s = new StringBuilder("hello")
151+
.delete(1, 2)
152+
.append("world")
153+
.toString();
154+
}
155+
}
156+
""",
157+
"""
158+
class Main {
159+
void hello() {
160+
final String s = new StringBuilder("hello")
161+
.delete(1, 2)
162+
.toString();
163+
}
164+
}
165+
"""
166+
)
167+
);
168+
}
139169
}

0 commit comments

Comments
 (0)