From 0973eaac112fbf1e7c03feab77adc56414948707 Mon Sep 17 00:00:00 2001 From: Deepak Date: Fri, 16 May 2025 09:38:01 +0530 Subject: [PATCH 01/37] Add RemoveUnusedParams recipe --- .../staticanalysis/RemoveUnusedParams.java | 158 ++++++++++ .../RemoveUnusedParamsTest.java | 281 ++++++++++++++++++ 2 files changed, 439 insertions(+) create mode 100644 src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java create mode 100644 src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java new file mode 100644 index 0000000000..b6ee55e5ba --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -0,0 +1,158 @@ +package org.openrewrite.staticanalysis; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Repeat; +import org.openrewrite.ScanningRecipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.NoMissingTypes; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.Statement; + +import java.time.Duration; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class RemoveUnusedParams extends ScanningRecipe { + + static class Accumulator { + Set overrideSignatures = new HashSet<>(); + } + + @Override + public String getDisplayName() { + return "Remove unused method parameters"; + } + + @Override + public String getDescription() { + return "Removes parameters from methods that are declared but never used in the method body."; + } + + @Override + public Duration getEstimatedEffortPerOccurrence() { + return Duration.ofMinutes(5); + } + + @Override + public Accumulator getInitialValue(ExecutionContext ctx) { + return new Accumulator(); + } + + @Override + public TreeVisitor getScanner(Accumulator acc) { + return new JavaIsoVisitor() { + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); + for (J.Annotation ann : m.getLeadingAnnotations()) { + if ("Override".equals(ann.getSimpleName())) { + String key = m.getSimpleName() + "#" + m.getParameters().size(); + acc.overrideSignatures.add(key); + break; + } + } + return m; + } + }; + } + + @Override + public TreeVisitor getVisitor(Accumulator acc) { + return Preconditions.check(new NoMissingTypes(), + Repeat.repeatUntilStable(new JavaIsoVisitor() { + + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); + + // Skip any explicit @Override + if (m.getMethodType() != null && m.getMethodType().isOverride()) { + return m; + } + // Skip if overridden elsewhere + String signature = m.getSimpleName() + "#" + m.getParameters().size(); + if (acc.overrideSignatures.contains(signature)) { + return m; + } + // Original guards + if (m.getBody() == null || + m.hasModifier(J.Modifier.Type.Native) || + !m.getLeadingAnnotations().isEmpty()) { + return m; + } + + // Collect parameter names + Set params = m.getParameters().stream() + .filter(p -> p instanceof J.VariableDeclarations) + .flatMap(p -> ((J.VariableDeclarations) p).getVariables().stream()) + .map(J.VariableDeclarations.NamedVariable::getSimpleName) + .collect(Collectors.toSet()); + + // Find which are actually used + Set used = new HashSet<>(); + new JavaIsoVisitor>() { + Deque> shadowed = new ArrayDeque<>(); + + @Override + public J.Block visitBlock(J.Block block, Set u) { + shadowed.push(new HashSet<>()); + J.Block b = super.visitBlock(block, u); + shadowed.pop(); + return b; + } + + @Override + public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations vars, Set u) { + vars.getVariables().forEach(v -> shadowed.peek().add(v.getSimpleName())); + return super.visitVariableDeclarations(vars, u); + } + + @Override + public J.Identifier visitIdentifier(J.Identifier ident, Set u) { + for (Set scope : shadowed) { + if (scope.contains(ident.getSimpleName())) { + return ident; + } + } + if (params.contains(ident.getSimpleName())) { + u.add(ident.getSimpleName()); + } + return ident; + } + }.visit(m.getBody(), used); + + // Rebuild parameter list + List newParams = new ArrayList<>(); + for (Statement p : m.getParameters()) { + if (!(p instanceof J.VariableDeclarations)) { + newParams.add(p); + continue; + } + J.VariableDeclarations vd = (J.VariableDeclarations) p; + if (!vd.getLeadingAnnotations().isEmpty()) { + newParams.add(vd); + continue; + } + List keep = vd.getVariables().stream() + .filter(v -> used.contains(v.getSimpleName())) + .collect(Collectors.toList()); + if (!keep.isEmpty()) { + newParams.add(vd.withVariables(keep)); + } + } + + return newParams.equals(m.getParameters()) + ? m + : m.withParameters(newParams); + } + }) + ); + } +} diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java new file mode 100644 index 0000000000..1e28307c4b --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java @@ -0,0 +1,281 @@ +package org.openrewrite.staticanalysis; + +import org.junit.jupiter.api.Test; +import org.openrewrite.Issue; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class RemoveUnusedParamsTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new RemoveUnusedParams()); + } + + @Test + void removeUnusedMethodParameter() { + rewriteRun( + java( + """ + public class Test { + void method(String unused) { + System.out.println("Hello"); + } + } + """, + """ + public class Test { + void method() { + System.out.println("Hello"); + } + } + """ + ) + ); + } + + @Test + void doNotRemoveUsedParameter() { + rewriteRun( + java( + """ + public class Test { + void method(String input) { + System.out.println(input); + } + } + """ + ) + ); + } + + @Test + void doNotRemoveOverriddenMethodParameter() { + rewriteRun( + java( + """ + class Base { + void method(String param) {} + } + class Derived extends Base { + @Override + void method(String param) { + // not used but required + } + } + """ + ) + ); + } + + @Test + void doNotRemoveAnnotatedParameter() { + rewriteRun( + java( + """ + public class Test { + void method(@Deprecated String param) {} + } + """ + ) + ); + } + + @Test + void removeMultipleUnusedParams() { + rewriteRun( + java( + """ + public class Test { + void method(String a, int b, double c) { + System.out.println("Only prints this"); + } + } + """, + """ + public class Test { + void method() { + System.out.println("Only prints this"); + } + } + """ + ) + ); + } + + @Test + void preserveJavadocAndComments() { + rewriteRun( + java( + """ + public class Test { + /** + * Some doc + * @param unused this param is never used + */ + void method(String unused) { + // comment + System.out.println("used"); + } + } + """, + """ + public class Test { + /** + * Some doc + * @param unused this param is never used + */ + void method() { + // comment + System.out.println("used"); + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/559") + @Test + void shadowedParameterShouldStillBeRemoved() { + rewriteRun( + java( + """ + public class Test { + void method(String input) { + String input = "shadowed"; + System.out.println(input); + } + } + """, + """ + public class Test { + void method() { + String input = "shadowed"; + System.out.println(input); + } + } + """ + ) + ); + } + + @Test + void removeUnusedStaticMethodParam() { + rewriteRun( + java( + """ + public class Test { + static void helper(String unused) { + // no use + } + } + """, + """ + public class Test { + static void helper() { + // no use + } + } + """ + ) + ); + } + + @Test + void removeUnusedConstructorParam() { + rewriteRun( + java( + """ + public class Test { + Test(String unused) { + // ctor body + } + } + """, + """ + public class Test { + Test() { + // ctor body + } + } + """ + ) + ); + } + + @Test + void removeUnusedVarargs() { + rewriteRun( + java( + """ + public class Test { + void m(String... args) { + System.out.println("no args used"); + } + } + """, + """ + public class Test { + void m() { + System.out.println("no args used"); + } + } + """ + ) + ); + } + + @Test + void preserveAnnotatedParamButRemoveOthers() { + rewriteRun( + java( + """ + public class Test { + void method(@Deprecated String keep, int removeMe) { + System.out.println(keep); + } + } + """, + """ + public class Test { + void method(@Deprecated String keep) { + System.out.println(keep); + } + } + """ + ) + ); + } + + @Test + void doNotRemoveNativeMethodParam() { + rewriteRun( + java( + """ + public class Test { + native void nativeCall(int mustStay); + } + """ + ) + ); + + + + } + + @Test + void interfaceMethodUnchanged() { + rewriteRun( + java( + """ + interface I { + void foo(String param); + } + """ + ) + ); + } +} From d93a14cfd4e7abbcefa469edca24bc653c1510c4 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 16 May 2025 05:14:56 -0400 Subject: [PATCH 02/37] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../staticanalysis/RemoveUnusedParams.java | 21 ++++++++++++++++--- .../RemoveUnusedParamsTest.java | 15 +++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index b6ee55e5ba..05f6d9a6ad 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -1,3 +1,18 @@ +/* + * Copyright 2025 the original author or authors. + *

+ * Licensed under the Moderne Source Available License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://docs.moderne.io/licensing/moderne-source-available-license + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.openrewrite.staticanalysis; import org.openrewrite.ExecutionContext; @@ -148,9 +163,9 @@ public J.Identifier visitIdentifier(J.Identifier ident, Set u) { } } - return newParams.equals(m.getParameters()) - ? m - : m.withParameters(newParams); + return newParams.equals(m.getParameters()) ? + m : + m.withParameters(newParams); } }) ); diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java index 1e28307c4b..87578971db 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2025 the original author or authors. + *

+ * Licensed under the Moderne Source Available License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://docs.moderne.io/licensing/moderne-source-available-license + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.openrewrite.staticanalysis; import org.junit.jupiter.api.Test; From 84d8671a353305c8885c0c0f0a8358ac842e4696 Mon Sep 17 00:00:00 2001 From: Deepak Date: Fri, 16 May 2025 19:41:34 +0530 Subject: [PATCH 03/37] Updated review comments --- .../staticanalysis/RemoveUnusedParams.java | 20 +++++++++++-------- .../RemoveUnusedParamsTest.java | 5 ++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 05f6d9a6ad..2b67624553 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -83,34 +83,39 @@ public TreeVisitor getVisitor(Accumulator acc) { return Preconditions.check(new NoMissingTypes(), Repeat.repeatUntilStable(new JavaIsoVisitor() { + private boolean skipAnyExplicitOverride(J.MethodDeclaration m) { + return m.getMethodType() != null && m.getMethodType().isOverride(); + } + + private boolean skipIfOverriddenElsewhere(String signature) { + return acc.overrideSignatures.contains(signature); + } + @Override public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); - // Skip any explicit @Override - if (m.getMethodType() != null && m.getMethodType().isOverride()) { + if (skipAnyExplicitOverride(m)) { return m; } - // Skip if overridden elsewhere + String signature = m.getSimpleName() + "#" + m.getParameters().size(); - if (acc.overrideSignatures.contains(signature)) { + if (skipIfOverriddenElsewhere(signature)) { return m; } - // Original guards + if (m.getBody() == null || m.hasModifier(J.Modifier.Type.Native) || !m.getLeadingAnnotations().isEmpty()) { return m; } - // Collect parameter names Set params = m.getParameters().stream() .filter(p -> p instanceof J.VariableDeclarations) .flatMap(p -> ((J.VariableDeclarations) p).getVariables().stream()) .map(J.VariableDeclarations.NamedVariable::getSimpleName) .collect(Collectors.toSet()); - // Find which are actually used Set used = new HashSet<>(); new JavaIsoVisitor>() { Deque> shadowed = new ArrayDeque<>(); @@ -143,7 +148,6 @@ public J.Identifier visitIdentifier(J.Identifier ident, Set u) { } }.visit(m.getBody(), used); - // Rebuild parameter list List newParams = new ArrayList<>(); for (Statement p : m.getParameters()) { if (!(p instanceof J.VariableDeclarations)) { diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java index 87578971db..3a286093ec 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java @@ -16,6 +16,7 @@ package org.openrewrite.staticanalysis; import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.Issue; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -29,6 +30,7 @@ public void defaults(RecipeSpec spec) { spec.recipe(new RemoveUnusedParams()); } + @DocumentExample @Test void removeUnusedMethodParameter() { rewriteRun( @@ -276,9 +278,6 @@ public class Test { """ ) ); - - - } @Test From 6365a31ae4fa25bcdfb649ce6716ea47a0e32012 Mon Sep 17 00:00:00 2001 From: Deepak Date: Fri, 16 May 2025 21:48:14 +0530 Subject: [PATCH 04/37] Updated review comments --- .../staticanalysis/RemoveUnusedParams.java | 17 ++++++---- .../RemoveUnusedParamsTest.java | 33 +++++++++++++++++++ 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 2b67624553..a5d9ce1ef8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -37,7 +37,7 @@ public class RemoveUnusedParams extends ScanningRecipe { static class Accumulator { - Set overrideSignatures = new HashSet<>(); + final Set OVERRIDE_SIGNATURES = new HashSet<>(); } @Override @@ -60,6 +60,13 @@ public Accumulator getInitialValue(ExecutionContext ctx) { return new Accumulator(); } + private String buildSignature(J.MethodDeclaration m){ + return m.getSimpleName() + "#" + + m.getMethodType().getParameterTypes() + .stream().map(p->p.toString()) + .collect(Collectors.joining(",")); + } + @Override public TreeVisitor getScanner(Accumulator acc) { return new JavaIsoVisitor() { @@ -68,8 +75,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); for (J.Annotation ann : m.getLeadingAnnotations()) { if ("Override".equals(ann.getSimpleName())) { - String key = m.getSimpleName() + "#" + m.getParameters().size(); - acc.overrideSignatures.add(key); + acc.OVERRIDE_SIGNATURES.add(buildSignature(m)); break; } } @@ -88,7 +94,7 @@ private boolean skipAnyExplicitOverride(J.MethodDeclaration m) { } private boolean skipIfOverriddenElsewhere(String signature) { - return acc.overrideSignatures.contains(signature); + return acc.OVERRIDE_SIGNATURES.contains(signature); } @Override @@ -99,8 +105,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex return m; } - String signature = m.getSimpleName() + "#" + m.getParameters().size(); - if (skipIfOverriddenElsewhere(signature)) { + if (skipIfOverriddenElsewhere(buildSignature(m))) { return m; } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java index 3a286093ec..603af8462c 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java @@ -292,4 +292,37 @@ interface I { ) ); } + + @Test + void skipWronglyDueToSignatureCollision() { + rewriteRun( + java( + """ + class Base { + void foo(int a, String b) {} + } + class Derived extends Base { + @Override + void foo(int a, String b) {} + void foo(String a, int b) { + // no use of a or b + } + } + """, + """ + class Base { + void foo(int a, String b) {} + } + class Derived extends Base { + @Override + void foo(int a, String b) {} + void foo() { + // no use of a or b + } + } + """ + ) + ); + } + } From e71803338f97f45a7be9e3f14fe610a0a367eb89 Mon Sep 17 00:00:00 2001 From: iddeepak <87892182+iddeepak@users.noreply.github.com> Date: Sat, 17 May 2025 01:14:25 +0530 Subject: [PATCH 05/37] Update src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../org/openrewrite/staticanalysis/RemoveUnusedParams.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index a5d9ce1ef8..45798fe87a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -61,8 +61,8 @@ public Accumulator getInitialValue(ExecutionContext ctx) { } private String buildSignature(J.MethodDeclaration m){ - return m.getSimpleName() + "#" - + m.getMethodType().getParameterTypes() + return m.getSimpleName() + "#" + + m.getMethodType().getParameterTypes() .stream().map(p->p.toString()) .collect(Collectors.joining(",")); } From c735b6ed6fabecb9654fa6cd4cd8fd9e41fc5b5a Mon Sep 17 00:00:00 2001 From: Deepak Date: Sat, 17 May 2025 05:04:21 +0530 Subject: [PATCH 06/37] Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments Updated review comments --- .../staticanalysis/RemoveUnusedParams.java | 237 ++++++++++-------- .../RemoveUnusedParamsTest.java | 18 ++ 2 files changed, 151 insertions(+), 104 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 45798fe87a..59b6be4089 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -16,167 +16,196 @@ package org.openrewrite.staticanalysis; import org.openrewrite.ExecutionContext; -import org.openrewrite.Preconditions; -import org.openrewrite.Repeat; -import org.openrewrite.ScanningRecipe; -import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.NoMissingTypes; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.Statement; +import org.openrewrite.Preconditions; +import org.openrewrite.Repeat; +import org.openrewrite.ScanningRecipe; +import org.openrewrite.TreeVisitor; -import java.time.Duration; import java.util.ArrayDeque; -import java.util.ArrayList; import java.util.Deque; +import java.time.Duration; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; public class RemoveUnusedParams extends ScanningRecipe { - static class Accumulator { - final Set OVERRIDE_SIGNATURES = new HashSet<>(); + /** + * Signatures of all methods that override or implement a supertype method. + * Each entry is a string of the form + * "fully.qualified.ClassName#methodName(paramType1,paramType2,...)". + * Parameters of these methods are considered part of the public API + * and will not be removed even if they appear unused. + */ + private final Set overrideSignatures = new HashSet<>(); + + void add(final String signature) { + overrideSignatures.add(signature); + } + + boolean contains(final String signature) { + return overrideSignatures.contains(signature); + } } + private static final String OVERRIDE_ANNOTATION = "Override"; + private static final int MAX_ATTEMPTS = 5; + @Override public String getDisplayName() { - return "Remove unused method parameters"; + return "Remove obsolete constructor and method parameters"; } @Override public String getDescription() { - return "Removes parameters from methods that are declared but never used in the method body."; + return "Removes obsolete method parameters from signature, not used in body."; } @Override public Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(5); + return Duration.ofMinutes(MAX_ATTEMPTS); } @Override - public Accumulator getInitialValue(ExecutionContext ctx) { + public Accumulator getInitialValue(final ExecutionContext ctx) { return new Accumulator(); } - private String buildSignature(J.MethodDeclaration m){ - return m.getSimpleName() + "#" + - m.getMethodType().getParameterTypes() - .stream().map(p->p.toString()) - .collect(Collectors.joining(",")); - } - @Override - public TreeVisitor getScanner(Accumulator acc) { + public TreeVisitor getScanner(final Accumulator acc) { return new JavaIsoVisitor() { @Override - public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + public J.MethodDeclaration visitMethodDeclaration(final J.MethodDeclaration method, final ExecutionContext ctx) { J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); - for (J.Annotation ann : m.getLeadingAnnotations()) { - if ("Override".equals(ann.getSimpleName())) { - acc.OVERRIDE_SIGNATURES.add(buildSignature(m)); - break; - } - } + collectOverrideSignature(m, acc); return m; } }; } + private void collectOverrideSignature(final J.MethodDeclaration m, final Accumulator acc) { + m.getLeadingAnnotations().stream() + .map(J.Annotation::getSimpleName) + .filter(OVERRIDE_ANNOTATION::equals) + .findAny() + .ifPresent(annotationName -> acc.add(buildSignature(m))); + } + @Override - public TreeVisitor getVisitor(Accumulator acc) { + public TreeVisitor getVisitor(final Accumulator acc) { return Preconditions.check(new NoMissingTypes(), Repeat.repeatUntilStable(new JavaIsoVisitor() { - private boolean skipAnyExplicitOverride(J.MethodDeclaration m) { - return m.getMethodType() != null && m.getMethodType().isOverride(); + @Override + public J.MethodDeclaration visitMethodDeclaration(final J.MethodDeclaration method, final ExecutionContext ctx) { + J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); + if (shouldPruneParameters(m, acc)) { + Set usedParams = collectUsedParameters(m); + List prunedParams = filterUnusedParameters(m, usedParams); + if (!prunedParams.equals(m.getParameters())) { + return m.withParameters(prunedParams); + } + } + return m; } + }) + ); + } - private boolean skipIfOverriddenElsewhere(String signature) { - return acc.OVERRIDE_SIGNATURES.contains(signature); - } + private boolean shouldPruneParameters(final J.MethodDeclaration m, final Accumulator acc) { + return m.getBody() != null + && !m.hasModifier(J.Modifier.Type.Native) + && m.getLeadingAnnotations().isEmpty() + && !acc.contains(buildSignature(m)); + } - @Override - public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { - J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); + private String buildSignature(final J.MethodDeclaration m) { + return m.getSimpleName() + "#" + + m.getMethodType().getParameterTypes().stream() + .map(Object::toString) + .collect(Collectors.joining(",")); + } - if (skipAnyExplicitOverride(m)) { - return m; - } + private Set collectUsedParameters(final J.MethodDeclaration m) { + Set used = new HashSet<>(); + Deque> shadowStack = new ArrayDeque<>(); - if (skipIfOverriddenElsewhere(buildSignature(m))) { - return m; - } + new JavaIsoVisitor>() { + @Override + public J.Block visitBlock(final J.Block block, final Set u) { + shadowStack.push(new HashSet<>()); + try { + return super.visitBlock(block, u); + } finally { + shadowStack.pop(); + } + } - if (m.getBody() == null || - m.hasModifier(J.Modifier.Type.Native) || - !m.getLeadingAnnotations().isEmpty()) { - return m; - } + @Override + public J.VariableDeclarations visitVariableDeclarations(final J.VariableDeclarations decl, final Set u) { + decl.getVariables().forEach(v -> shadowStack.peek().add(v.getSimpleName())); + return super.visitVariableDeclarations(decl, u); + } - Set params = m.getParameters().stream() - .filter(p -> p instanceof J.VariableDeclarations) - .flatMap(p -> ((J.VariableDeclarations) p).getVariables().stream()) - .map(J.VariableDeclarations.NamedVariable::getSimpleName) - .collect(Collectors.toSet()); - - Set used = new HashSet<>(); - new JavaIsoVisitor>() { - Deque> shadowed = new ArrayDeque<>(); - - @Override - public J.Block visitBlock(J.Block block, Set u) { - shadowed.push(new HashSet<>()); - J.Block b = super.visitBlock(block, u); - shadowed.pop(); - return b; - } + @Override + public J.Identifier visitIdentifier(final J.Identifier id, final Set u) { + if (isVisibleParameter(id, m, shadowStack)) { + u.add(id.getSimpleName()); + } + return id; + } + }.visit(m.getBody(), used); - @Override - public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations vars, Set u) { - vars.getVariables().forEach(v -> shadowed.peek().add(v.getSimpleName())); - return super.visitVariableDeclarations(vars, u); - } + return used; + } - @Override - public J.Identifier visitIdentifier(J.Identifier ident, Set u) { - for (Set scope : shadowed) { - if (scope.contains(ident.getSimpleName())) { - return ident; - } - } - if (params.contains(ident.getSimpleName())) { - u.add(ident.getSimpleName()); - } - return ident; - } - }.visit(m.getBody(), used); + private boolean isVisibleParameter(final J.Identifier id, final J.MethodDeclaration m, final Deque> shadowStack) { + return !isShadowed(id.getSimpleName(), shadowStack) + && isDeclaredAsParameter(id.getSimpleName(), m); + } - List newParams = new ArrayList<>(); - for (Statement p : m.getParameters()) { - if (!(p instanceof J.VariableDeclarations)) { - newParams.add(p); - continue; - } - J.VariableDeclarations vd = (J.VariableDeclarations) p; - if (!vd.getLeadingAnnotations().isEmpty()) { - newParams.add(vd); - continue; - } - List keep = vd.getVariables().stream() - .filter(v -> used.contains(v.getSimpleName())) - .collect(Collectors.toList()); - if (!keep.isEmpty()) { - newParams.add(vd.withVariables(keep)); - } - } + private boolean isShadowed(final String name, final Deque> shadowStack) { + return shadowStack.stream().anyMatch(scope -> scope.contains(name)); + } - return newParams.equals(m.getParameters()) ? - m : - m.withParameters(newParams); + private boolean isDeclaredAsParameter(final String name, final J.MethodDeclaration m) { + return m.getParameters().stream() + .filter(p -> p instanceof J.VariableDeclarations) + .flatMap(p -> ((J.VariableDeclarations) p).getVariables().stream()) + .anyMatch(v -> v.getSimpleName().equals(name)); + } + + private List filterUnusedParameters(final J.MethodDeclaration m, final Set usedParams) { + return m.getParameters().stream() + .flatMap(param -> { + if (param instanceof J.VariableDeclarations) { + J.VariableDeclarations decl = (J.VariableDeclarations) param; + return filterDeclaration(decl, usedParams); } + return Stream.of(param); }) - ); + .collect(Collectors.toList()); } + + + private Stream filterDeclaration(final J.VariableDeclarations decl, final Set usedParams) { + if (!decl.getLeadingAnnotations().isEmpty()) { + return Stream.of(decl); + } + List kept = + decl.getVariables().stream() + .filter(v -> usedParams.contains(v.getSimpleName())) + .collect(Collectors.toList()); + + return kept.isEmpty() + ? Stream.empty() + : Stream.of(decl.withVariables(kept)); + } + } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java index 603af8462c..35fac55dd8 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java @@ -325,4 +325,22 @@ void foo() { ); } + @Test + void removeUnusedBaseConstructorParams() { + rewriteRun( + java( + """ + class Base { + public Base(int a, String b) {} + } + """, + """ + class Base { + public Base() {} + } + """ + ) + ); + } + } From e6a633b3231ff402dbb64069270c351a7fad022a Mon Sep 17 00:00:00 2001 From: iddeepak <87892182+iddeepak@users.noreply.github.com> Date: Sat, 17 May 2025 22:34:07 +0530 Subject: [PATCH 07/37] Update src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../org/openrewrite/staticanalysis/RemoveUnusedParams.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 59b6be4089..0d026533eb 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -166,8 +166,8 @@ public J.Identifier visitIdentifier(final J.Identifier id, final Set u) } private boolean isVisibleParameter(final J.Identifier id, final J.MethodDeclaration m, final Deque> shadowStack) { - return !isShadowed(id.getSimpleName(), shadowStack) - && isDeclaredAsParameter(id.getSimpleName(), m); + return !isShadowed(id.getSimpleName(), shadowStack) && + isDeclaredAsParameter(id.getSimpleName(), m); } private boolean isShadowed(final String name, final Deque> shadowStack) { From 674a46572e46902cd881a3bc5c5b9380c661d374 Mon Sep 17 00:00:00 2001 From: iddeepak <87892182+iddeepak@users.noreply.github.com> Date: Sat, 17 May 2025 22:34:22 +0530 Subject: [PATCH 08/37] Update src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../org/openrewrite/staticanalysis/RemoveUnusedParams.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 0d026533eb..9c368e1550 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -203,9 +203,9 @@ private Stream filterDeclaration(final J.VariableDeclarations decl, f .filter(v -> usedParams.contains(v.getSimpleName())) .collect(Collectors.toList()); - return kept.isEmpty() - ? Stream.empty() - : Stream.of(decl.withVariables(kept)); + return kept.isEmpty() ? + Stream.empty() : + Stream.of(decl.withVariables(kept)); } } From e656fa2c8b651628045ef4616883a455e7654951 Mon Sep 17 00:00:00 2001 From: Deepak Date: Sun, 18 May 2025 20:04:48 +0530 Subject: [PATCH 09/37] Updated review comments --- .../staticanalysis/RemoveUnusedParams.java | 28 +++++++------------ .../RemoveUnusedParamsTest.java | 19 ------------- 2 files changed, 10 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 9c368e1550..77d8e1e519 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -82,19 +82,18 @@ public TreeVisitor getScanner(final Accumulator acc) { return new JavaIsoVisitor() { @Override public J.MethodDeclaration visitMethodDeclaration(final J.MethodDeclaration method, final ExecutionContext ctx) { - J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); - collectOverrideSignature(m, acc); - return m; + return collectOverrideSignature(super.visitMethodDeclaration(method, ctx), acc); } }; } - private void collectOverrideSignature(final J.MethodDeclaration m, final Accumulator acc) { + private J.MethodDeclaration collectOverrideSignature(final J.MethodDeclaration m, final Accumulator acc) { m.getLeadingAnnotations().stream() .map(J.Annotation::getSimpleName) .filter(OVERRIDE_ANNOTATION::equals) .findAny() .ifPresent(annotationName -> acc.add(buildSignature(m))); + return m; } @Override @@ -106,11 +105,8 @@ public TreeVisitor getVisitor(final Accumulator acc) { public J.MethodDeclaration visitMethodDeclaration(final J.MethodDeclaration method, final ExecutionContext ctx) { J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); if (shouldPruneParameters(m, acc)) { - Set usedParams = collectUsedParameters(m); - List prunedParams = filterUnusedParameters(m, usedParams); - if (!prunedParams.equals(m.getParameters())) { - return m.withParameters(prunedParams); - } + List prunedParams = filterUnusedParameters(m, collectUsedParameters(m)); + return prunedParams.equals(m.getParameters()) ? m : m.withParameters(prunedParams); } return m; } @@ -133,8 +129,8 @@ private String buildSignature(final J.MethodDeclaration m) { } private Set collectUsedParameters(final J.MethodDeclaration m) { - Set used = new HashSet<>(); - Deque> shadowStack = new ArrayDeque<>(); + final Set used = new HashSet<>(); + final Deque> shadowStack = new ArrayDeque<>(); new JavaIsoVisitor>() { @Override @@ -183,13 +179,9 @@ private boolean isDeclaredAsParameter(final String name, final J.MethodDeclarati private List filterUnusedParameters(final J.MethodDeclaration m, final Set usedParams) { return m.getParameters().stream() - .flatMap(param -> { - if (param instanceof J.VariableDeclarations) { - J.VariableDeclarations decl = (J.VariableDeclarations) param; - return filterDeclaration(decl, usedParams); - } - return Stream.of(param); - }) + .flatMap(param -> param instanceof J.VariableDeclarations ? + filterDeclaration((J.VariableDeclarations) param, usedParams) + : Stream.of(param)) .collect(Collectors.toList()); } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java index 35fac55dd8..d4a431faee 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java @@ -324,23 +324,4 @@ void foo() { ) ); } - - @Test - void removeUnusedBaseConstructorParams() { - rewriteRun( - java( - """ - class Base { - public Base(int a, String b) {} - } - """, - """ - class Base { - public Base() {} - } - """ - ) - ); - } - } From 11ef2ac929582b862eadf226010e77b42b57cb21 Mon Sep 17 00:00:00 2001 From: Deepak Date: Sun, 18 May 2025 20:21:19 +0530 Subject: [PATCH 10/37] Updated review comments --- .../staticanalysis/RemoveUnusedParams.java | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 77d8e1e519..fdca59eaf3 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -30,6 +30,7 @@ import java.time.Duration; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -162,8 +163,7 @@ public J.Identifier visitIdentifier(final J.Identifier id, final Set u) } private boolean isVisibleParameter(final J.Identifier id, final J.MethodDeclaration m, final Deque> shadowStack) { - return !isShadowed(id.getSimpleName(), shadowStack) && - isDeclaredAsParameter(id.getSimpleName(), m); + return !isShadowed(id.getSimpleName(), shadowStack) && isDeclaredAsParameter(id.getSimpleName(), m); } private boolean isShadowed(final String name, final Deque> shadowStack) { @@ -185,19 +185,23 @@ private List filterUnusedParameters(final J.MethodDeclaration m, fina .collect(Collectors.toList()); } + private Stream filterDeclaration(J.VariableDeclarations decl, Set usedParams) { + return Optional.of(decl) + .filter(d -> !d.getLeadingAnnotations().isEmpty()) + .map(d -> Stream.of(d)) + .orElseGet(() -> pruneByUsage(decl, usedParams)); + } - private Stream filterDeclaration(final J.VariableDeclarations decl, final Set usedParams) { - if (!decl.getLeadingAnnotations().isEmpty()) { - return Stream.of(decl); - } - List kept = - decl.getVariables().stream() - .filter(v -> usedParams.contains(v.getSimpleName())) - .collect(Collectors.toList()); - - return kept.isEmpty() ? - Stream.empty() : - Stream.of(decl.withVariables(kept)); + private Stream pruneByUsage(J.VariableDeclarations decl, Set usedParams) { + return Optional.of(collectUsedParameters(decl, usedParams)) + .filter(kept -> !kept.isEmpty()) + .map(kept -> Stream.of(decl.withVariables(kept))) + .orElseGet(Stream::empty); } + private List collectUsedParameters(J.VariableDeclarations decl, Set usedParams) { + return decl.getVariables().stream() + .filter(v -> usedParams.contains(v.getSimpleName())) + .collect(Collectors.toList()); + } } From f88dd779045b31990b322c85814d112f24a27480 Mon Sep 17 00:00:00 2001 From: Deepak Date: Sun, 18 May 2025 20:23:08 +0530 Subject: [PATCH 11/37] Updated review comments --- .../org/openrewrite/staticanalysis/RemoveUnusedParams.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index fdca59eaf3..3e045173ca 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -185,21 +185,21 @@ private List filterUnusedParameters(final J.MethodDeclaration m, fina .collect(Collectors.toList()); } - private Stream filterDeclaration(J.VariableDeclarations decl, Set usedParams) { + private Stream filterDeclaration(final J.VariableDeclarations decl, final Set usedParams) { return Optional.of(decl) .filter(d -> !d.getLeadingAnnotations().isEmpty()) .map(d -> Stream.of(d)) .orElseGet(() -> pruneByUsage(decl, usedParams)); } - private Stream pruneByUsage(J.VariableDeclarations decl, Set usedParams) { + private Stream pruneByUsage(final J.VariableDeclarations decl, final Set usedParams) { return Optional.of(collectUsedParameters(decl, usedParams)) .filter(kept -> !kept.isEmpty()) .map(kept -> Stream.of(decl.withVariables(kept))) .orElseGet(Stream::empty); } - private List collectUsedParameters(J.VariableDeclarations decl, Set usedParams) { + private List collectUsedParameters(final J.VariableDeclarations decl, final Set usedParams) { return decl.getVariables().stream() .filter(v -> usedParams.contains(v.getSimpleName())) .collect(Collectors.toList()); From 6d989cc745e20d837d124aaadf0bb934ed9d7f58 Mon Sep 17 00:00:00 2001 From: Deepak Date: Tue, 20 May 2025 00:28:17 +0530 Subject: [PATCH 12/37] Updated review comments --- .../staticanalysis/RemoveUnusedParams.java | 95 +++++++++++-------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 3e045173ca..daefc70f9e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -16,24 +16,23 @@ package org.openrewrite.staticanalysis; import org.openrewrite.ExecutionContext; -import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.NoMissingTypes; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.Statement; import org.openrewrite.Preconditions; import org.openrewrite.Repeat; import org.openrewrite.ScanningRecipe; import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.NoMissingTypes; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.Statement; +import java.time.Duration; import java.util.ArrayDeque; +import java.util.ArrayList; import java.util.Deque; -import java.time.Duration; import java.util.HashSet; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; public class RemoveUnusedParams extends ScanningRecipe { static class Accumulator { @@ -55,9 +54,6 @@ boolean contains(final String signature) { } } - private static final String OVERRIDE_ANNOTATION = "Override"; - private static final int MAX_ATTEMPTS = 5; - @Override public String getDisplayName() { return "Remove obsolete constructor and method parameters"; @@ -70,7 +66,7 @@ public String getDescription() { @Override public Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(MAX_ATTEMPTS); + return Duration.ofMinutes(5); } @Override @@ -89,11 +85,9 @@ public J.MethodDeclaration visitMethodDeclaration(final J.MethodDeclaration meth } private J.MethodDeclaration collectOverrideSignature(final J.MethodDeclaration m, final Accumulator acc) { - m.getLeadingAnnotations().stream() - .map(J.Annotation::getSimpleName) - .filter(OVERRIDE_ANNOTATION::equals) - .findAny() - .ifPresent(annotationName -> acc.add(buildSignature(m))); + if (m.getMethodType() != null && m.getMethodType().isOverride()) { + acc.add(buildSignature(m)); + } return m; } @@ -117,6 +111,7 @@ public J.MethodDeclaration visitMethodDeclaration(final J.MethodDeclaration meth private boolean shouldPruneParameters(final J.MethodDeclaration m, final Accumulator acc) { return m.getBody() != null + && m.getMethodType() != null && !m.hasModifier(J.Modifier.Type.Native) && m.getLeadingAnnotations().isEmpty() && !acc.contains(buildSignature(m)); @@ -167,41 +162,57 @@ private boolean isVisibleParameter(final J.Identifier id, final J.MethodDeclarat } private boolean isShadowed(final String name, final Deque> shadowStack) { - return shadowStack.stream().anyMatch(scope -> scope.contains(name)); + for (Set scope : shadowStack) { + if (scope.contains(name)) { + return true; + } + } + return false; } private boolean isDeclaredAsParameter(final String name, final J.MethodDeclaration m) { - return m.getParameters().stream() - .filter(p -> p instanceof J.VariableDeclarations) - .flatMap(p -> ((J.VariableDeclarations) p).getVariables().stream()) - .anyMatch(v -> v.getSimpleName().equals(name)); + for (Statement p : m.getParameters()) { + if (p instanceof J.VariableDeclarations) { + for (J.VariableDeclarations.NamedVariable v : ((J.VariableDeclarations) p).getVariables()) { + if (v.getSimpleName().equals(name)) { + return true; + } + } + } + } + return false; } - private List filterUnusedParameters(final J.MethodDeclaration m, final Set usedParams) { - return m.getParameters().stream() - .flatMap(param -> param instanceof J.VariableDeclarations ? - filterDeclaration((J.VariableDeclarations) param, usedParams) - : Stream.of(param)) - .collect(Collectors.toList()); + private List filterUnusedParameters(final J.MethodDeclaration method, final Set usedParams) { + final List result = new ArrayList<>(method.getParameters().size()); + for (Statement param : method.getParameters()) { + if (param instanceof J.VariableDeclarations) { + processVariableDeclaration((J.VariableDeclarations) param, usedParams, result); + } else { + result.add(param); + } + } + return result; } - private Stream filterDeclaration(final J.VariableDeclarations decl, final Set usedParams) { - return Optional.of(decl) - .filter(d -> !d.getLeadingAnnotations().isEmpty()) - .map(d -> Stream.of(d)) - .orElseGet(() -> pruneByUsage(decl, usedParams)); - } + private void processVariableDeclaration(final J.VariableDeclarations decl, final Set usedParams, final List result) { + final List kept = keepUsedVariables(decl, usedParams); - private Stream pruneByUsage(final J.VariableDeclarations decl, final Set usedParams) { - return Optional.of(collectUsedParameters(decl, usedParams)) - .filter(kept -> !kept.isEmpty()) - .map(kept -> Stream.of(decl.withVariables(kept))) - .orElseGet(Stream::empty); + if (!kept.isEmpty()) { + result.add(decl.withVariables(kept)); + } else if (!decl.getLeadingAnnotations().isEmpty()) { + result.add(decl); + } } - private List collectUsedParameters(final J.VariableDeclarations decl, final Set usedParams) { - return decl.getVariables().stream() - .filter(v -> usedParams.contains(v.getSimpleName())) - .collect(Collectors.toList()); + private List keepUsedVariables(final J.VariableDeclarations decl, final Set usedParams) { + List kept = new ArrayList<>(decl.getVariables().size()); + for (J.VariableDeclarations.NamedVariable v : decl.getVariables()) { + if (usedParams.contains(v.getSimpleName())) { + kept.add(v); + } + } + return kept; } + } From 80b57e848d9d0ea344505eac7219a24330b72e14 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 19 May 2025 21:16:28 +0200 Subject: [PATCH 13/37] Apply suggestions from code review --- .../org/openrewrite/staticanalysis/RemoveUnusedParams.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index daefc70f9e..0a37487152 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -64,11 +64,6 @@ public String getDescription() { return "Removes obsolete method parameters from signature, not used in body."; } - @Override - public Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(5); - } - @Override public Accumulator getInitialValue(final ExecutionContext ctx) { return new Accumulator(); From 95628c346df7fa3bfc7f487478aae85a37600105 Mon Sep 17 00:00:00 2001 From: Deepak Date: Tue, 20 May 2025 03:29:13 +0530 Subject: [PATCH 14/37] Updated review comments --- .../staticanalysis/RemoveUnusedParams.java | 16 +++++++--------- .../staticanalysis/RemoveUnusedParamsTest.java | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 0a37487152..aa56ba1b19 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -21,18 +21,18 @@ import org.openrewrite.ScanningRecipe; import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.NoMissingTypes; import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.Statement; -import java.time.Duration; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; public class RemoveUnusedParams extends ScanningRecipe { static class Accumulator { @@ -81,7 +81,8 @@ public J.MethodDeclaration visitMethodDeclaration(final J.MethodDeclaration meth private J.MethodDeclaration collectOverrideSignature(final J.MethodDeclaration m, final Accumulator acc) { if (m.getMethodType() != null && m.getMethodType().isOverride()) { - acc.add(buildSignature(m)); + acc.add(buildSignature(m.getMethodType())); + acc.add(buildSignature(m.getMethodType().getOverride())); } return m; } @@ -109,14 +110,11 @@ private boolean shouldPruneParameters(final J.MethodDeclaration m, final Accumul && m.getMethodType() != null && !m.hasModifier(J.Modifier.Type.Native) && m.getLeadingAnnotations().isEmpty() - && !acc.contains(buildSignature(m)); + && !acc.contains(buildSignature(m.getMethodType())); } - private String buildSignature(final J.MethodDeclaration m) { - return m.getSimpleName() + "#" - + m.getMethodType().getParameterTypes().stream() - .map(Object::toString) - .collect(Collectors.joining(",")); + private String buildSignature(final JavaType.Method m) { + return MethodMatcher.methodPattern(m); } private Set collectUsedParameters(final J.MethodDeclaration m) { diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java index d4a431faee..565fc84237 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java @@ -73,6 +73,23 @@ void doNotRemoveOverriddenMethodParameter() { rewriteRun( java( """ + class Test { + void method(String param) {} + } + class Base { + void method(String param) {} + } + class Derived extends Base { + @Override + void method(String param) { + // not used but required + } + } + """, + """ + class Test { + void method() {} + } class Base { void method(String param) {} } From a6acdef7fdeba8539433168e0705a765f8ad1e25 Mon Sep 17 00:00:00 2001 From: Deepak Date: Tue, 20 May 2025 03:57:03 +0530 Subject: [PATCH 15/37] Added Test for nested inheritance override chain --- .../staticanalysis/RemoveUnusedParams.java | 9 +++-- .../RemoveUnusedParamsTest.java | 33 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index aa56ba1b19..44a84aaaf3 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -80,9 +80,12 @@ public J.MethodDeclaration visitMethodDeclaration(final J.MethodDeclaration meth } private J.MethodDeclaration collectOverrideSignature(final J.MethodDeclaration m, final Accumulator acc) { - if (m.getMethodType() != null && m.getMethodType().isOverride()) { - acc.add(buildSignature(m.getMethodType())); - acc.add(buildSignature(m.getMethodType().getOverride())); + JavaType.Method mt = m.getMethodType(); + if (mt != null && mt.isOverride()) { + while (mt != null) { + acc.add(buildSignature(mt)); + mt = mt.getOverride(); + } } return m; } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java index 565fc84237..76027d1ba2 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java @@ -341,4 +341,37 @@ void foo() { ) ); } + + @Test + void nestedInheritanceOverrideChain() { + rewriteRun( + java( + """ + class Lone { void solo(String x) {} } + + class Grandparent { void greet(String msg) {} } + class Parent extends Grandparent { } + class Child extends Parent { + @Override + void greet(String msg) { + // required override + } + } + """, + """ + class Lone { void solo() {} } + + class Grandparent { void greet(String msg) {} } + class Parent extends Grandparent { } + class Child extends Parent { + @Override + void greet(String msg) { + // required override + } + } + """ + ) + ); + } + } From a4e7e3dada9a7c170eb38e405fcb3136706f4a79 Mon Sep 17 00:00:00 2001 From: iddeepak <87892182+iddeepak@users.noreply.github.com> Date: Tue, 20 May 2025 04:06:36 +0530 Subject: [PATCH 16/37] Update src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../openrewrite/staticanalysis/RemoveUnusedParams.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 44a84aaaf3..20c8dc78fc 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -109,11 +109,11 @@ public J.MethodDeclaration visitMethodDeclaration(final J.MethodDeclaration meth } private boolean shouldPruneParameters(final J.MethodDeclaration m, final Accumulator acc) { - return m.getBody() != null - && m.getMethodType() != null - && !m.hasModifier(J.Modifier.Type.Native) - && m.getLeadingAnnotations().isEmpty() - && !acc.contains(buildSignature(m.getMethodType())); + return m.getBody() != null && + m.getMethodType() != null && + !m.hasModifier(J.Modifier.Type.Native) && + m.getLeadingAnnotations().isEmpty() && + !acc.contains(buildSignature(m.getMethodType())); } private String buildSignature(final JavaType.Method m) { From 8e8953ffa73f362aa19b75c715461d23bf6fc53d Mon Sep 17 00:00:00 2001 From: iddeepak <87892182+iddeepak@users.noreply.github.com> Date: Tue, 20 May 2025 04:09:45 +0530 Subject: [PATCH 17/37] Update src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../org/openrewrite/staticanalysis/RemoveUnusedParams.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 20c8dc78fc..ddbedd95b1 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -27,12 +27,7 @@ import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.Statement; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; public class RemoveUnusedParams extends ScanningRecipe { static class Accumulator { From 298ee1f3c89267893e6557b67841282839bcc796 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 15 Jun 2025 00:32:08 +0200 Subject: [PATCH 18/37] Show two cases missed with conflicts after removal --- .../RemoveUnusedParamsTest.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java index 76027d1ba2..2ca6df99d7 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java @@ -374,4 +374,45 @@ void greet(String msg) { ); } + @Test + void avoidDirectConflict() { + rewriteRun( + java( + """ + public class Test { + void method() { + System.out.println("Hello"); + } + void method(String unused) { + System.out.println("Hello"); + } + } + """ + ) + ); + } + + @Test + void avoidInheritedConflict() { + rewriteRun( + java( + """ + public class A { + String method() { + return "A String"; + } + } + """ + ), + java( + """ + public class B extends A { + Long method(String unused) { + return 42L; + } + } + """ + ) + ); + } } From 9f689ab98ad63b77935dfe08d6f154d2041d8171 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 15 Jun 2025 00:38:43 +0200 Subject: [PATCH 19/37] Show another case missed: callers should be updated too --- .../RemoveUnusedParamsTest.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java index 2ca6df99d7..cc920906d7 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java @@ -415,4 +415,38 @@ Long method(String unused) { ) ); } + + @Test + void cascadeRemoveUnusedArguments() { + rewriteRun( + java( + """ + public class Test { + void method1(String unused) { + method2(unused); + } + void method2(String unused) { + method3(unused); + } + void method3(String unused) { + System.out.println("Hello"); + } + } + """, + """ + public class Test { + void method1() { + method2(); + } + void method2() { + method3(); + } + void method3() { + System.out.println("Hello"); + } + } + """ + ) + ); + } } From 28beff4b823729796a8160a3a21c666f04152639 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 15 Jun 2025 00:39:06 +0200 Subject: [PATCH 20/37] Remove unnecessary `final` to make intentional ones stand out --- .../staticanalysis/RemoveUnusedParams.java | 75 ++++++++----------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index ddbedd95b1..a47c6c83e8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -15,11 +15,7 @@ */ package org.openrewrite.staticanalysis; -import org.openrewrite.ExecutionContext; -import org.openrewrite.Preconditions; -import org.openrewrite.Repeat; -import org.openrewrite.ScanningRecipe; -import org.openrewrite.TreeVisitor; +import org.openrewrite.*; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.NoMissingTypes; @@ -30,7 +26,7 @@ import java.util.*; public class RemoveUnusedParams extends ScanningRecipe { - static class Accumulator { + public static class Accumulator { /** * Signatures of all methods that override or implement a supertype method. * Each entry is a string of the form @@ -39,14 +35,6 @@ static class Accumulator { * and will not be removed even if they appear unused. */ private final Set overrideSignatures = new HashSet<>(); - - void add(final String signature) { - overrideSignatures.add(signature); - } - - boolean contains(final String signature) { - return overrideSignatures.contains(signature); - } } @Override @@ -60,25 +48,26 @@ public String getDescription() { } @Override - public Accumulator getInitialValue(final ExecutionContext ctx) { + public Accumulator getInitialValue(ExecutionContext ctx) { return new Accumulator(); } @Override - public TreeVisitor getScanner(final Accumulator acc) { + public TreeVisitor getScanner(Accumulator acc) { return new JavaIsoVisitor() { @Override - public J.MethodDeclaration visitMethodDeclaration(final J.MethodDeclaration method, final ExecutionContext ctx) { + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { return collectOverrideSignature(super.visitMethodDeclaration(method, ctx), acc); } }; } - private J.MethodDeclaration collectOverrideSignature(final J.MethodDeclaration m, final Accumulator acc) { + private J.MethodDeclaration collectOverrideSignature(J.MethodDeclaration m, Accumulator acc) { JavaType.Method mt = m.getMethodType(); if (mt != null && mt.isOverride()) { while (mt != null) { - acc.add(buildSignature(mt)); + String signature = buildSignature(mt); + acc.overrideSignatures.add(signature); mt = mt.getOverride(); } } @@ -86,12 +75,11 @@ private J.MethodDeclaration collectOverrideSignature(final J.MethodDeclaration m } @Override - public TreeVisitor getVisitor(final Accumulator acc) { + public TreeVisitor getVisitor(Accumulator acc) { return Preconditions.check(new NoMissingTypes(), Repeat.repeatUntilStable(new JavaIsoVisitor() { - @Override - public J.MethodDeclaration visitMethodDeclaration(final J.MethodDeclaration method, final ExecutionContext ctx) { + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); if (shouldPruneParameters(m, acc)) { List prunedParams = filterUnusedParameters(m, collectUsedParameters(m)); @@ -103,25 +91,26 @@ public J.MethodDeclaration visitMethodDeclaration(final J.MethodDeclaration meth ); } - private boolean shouldPruneParameters(final J.MethodDeclaration m, final Accumulator acc) { - return m.getBody() != null && - m.getMethodType() != null && - !m.hasModifier(J.Modifier.Type.Native) && - m.getLeadingAnnotations().isEmpty() && - !acc.contains(buildSignature(m.getMethodType())); + private boolean shouldPruneParameters(J.MethodDeclaration m, Accumulator acc) { + if (m.getBody() == null || + m.getMethodType() == null || + m.hasModifier(J.Modifier.Type.Native) || + !m.getLeadingAnnotations().isEmpty()) return false; + String signature = buildSignature(m.getMethodType()); + return !acc.overrideSignatures.contains(signature); } - private String buildSignature(final JavaType.Method m) { + private String buildSignature(JavaType.Method m) { return MethodMatcher.methodPattern(m); } - private Set collectUsedParameters(final J.MethodDeclaration m) { - final Set used = new HashSet<>(); - final Deque> shadowStack = new ArrayDeque<>(); + private Set collectUsedParameters(J.MethodDeclaration m) { + Set used = new HashSet<>(); + Deque> shadowStack = new ArrayDeque<>(); new JavaIsoVisitor>() { @Override - public J.Block visitBlock(final J.Block block, final Set u) { + public J.Block visitBlock(J.Block block, Set u) { shadowStack.push(new HashSet<>()); try { return super.visitBlock(block, u); @@ -131,13 +120,13 @@ public J.Block visitBlock(final J.Block block, final Set u) { } @Override - public J.VariableDeclarations visitVariableDeclarations(final J.VariableDeclarations decl, final Set u) { + public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations decl, Set u) { decl.getVariables().forEach(v -> shadowStack.peek().add(v.getSimpleName())); return super.visitVariableDeclarations(decl, u); } @Override - public J.Identifier visitIdentifier(final J.Identifier id, final Set u) { + public J.Identifier visitIdentifier(J.Identifier id, Set u) { if (isVisibleParameter(id, m, shadowStack)) { u.add(id.getSimpleName()); } @@ -148,11 +137,11 @@ public J.Identifier visitIdentifier(final J.Identifier id, final Set u) return used; } - private boolean isVisibleParameter(final J.Identifier id, final J.MethodDeclaration m, final Deque> shadowStack) { + private boolean isVisibleParameter(J.Identifier id, J.MethodDeclaration m, Deque> shadowStack) { return !isShadowed(id.getSimpleName(), shadowStack) && isDeclaredAsParameter(id.getSimpleName(), m); } - private boolean isShadowed(final String name, final Deque> shadowStack) { + private boolean isShadowed(String name, Deque> shadowStack) { for (Set scope : shadowStack) { if (scope.contains(name)) { return true; @@ -161,7 +150,7 @@ private boolean isShadowed(final String name, final Deque> shadowSta return false; } - private boolean isDeclaredAsParameter(final String name, final J.MethodDeclaration m) { + private boolean isDeclaredAsParameter(String name, J.MethodDeclaration m) { for (Statement p : m.getParameters()) { if (p instanceof J.VariableDeclarations) { for (J.VariableDeclarations.NamedVariable v : ((J.VariableDeclarations) p).getVariables()) { @@ -174,8 +163,8 @@ private boolean isDeclaredAsParameter(final String name, final J.MethodDeclarati return false; } - private List filterUnusedParameters(final J.MethodDeclaration method, final Set usedParams) { - final List result = new ArrayList<>(method.getParameters().size()); + private List filterUnusedParameters(J.MethodDeclaration method, Set usedParams) { + List result = new ArrayList<>(method.getParameters().size()); for (Statement param : method.getParameters()) { if (param instanceof J.VariableDeclarations) { processVariableDeclaration((J.VariableDeclarations) param, usedParams, result); @@ -186,8 +175,8 @@ private List filterUnusedParameters(final J.MethodDeclaration method, return result; } - private void processVariableDeclaration(final J.VariableDeclarations decl, final Set usedParams, final List result) { - final List kept = keepUsedVariables(decl, usedParams); + private void processVariableDeclaration(J.VariableDeclarations decl, Set usedParams, List result) { + List kept = keepUsedVariables(decl, usedParams); if (!kept.isEmpty()) { result.add(decl.withVariables(kept)); @@ -196,7 +185,7 @@ private void processVariableDeclaration(final J.VariableDeclarations decl, final } } - private List keepUsedVariables(final J.VariableDeclarations decl, final Set usedParams) { + private List keepUsedVariables(J.VariableDeclarations decl, Set usedParams) { List kept = new ArrayList<>(decl.getVariables().size()); for (J.VariableDeclarations.NamedVariable v : decl.getVariables()) { if (usedParams.contains(v.getSimpleName())) { From 0b77aa9ea333a00f3e4c950bf32ac59e205ad4df Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 15 Jun 2025 00:45:28 +0200 Subject: [PATCH 21/37] Inline `collectOverrideSignature` only used once --- .../staticanalysis/RemoveUnusedParams.java | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index a47c6c83e8..1c02f37408 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -57,23 +57,20 @@ public TreeVisitor getScanner(Accumulator acc) { return new JavaIsoVisitor() { @Override public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { - return collectOverrideSignature(super.visitMethodDeclaration(method, ctx), acc); + J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); + JavaType.Method mt = m.getMethodType(); + if (mt != null && mt.isOverride()) { + while (mt != null) { + String signature = buildSignature(mt); + acc.overrideSignatures.add(signature); + mt = mt.getOverride(); + } + } + return m; } }; } - private J.MethodDeclaration collectOverrideSignature(J.MethodDeclaration m, Accumulator acc) { - JavaType.Method mt = m.getMethodType(); - if (mt != null && mt.isOverride()) { - while (mt != null) { - String signature = buildSignature(mt); - acc.overrideSignatures.add(signature); - mt = mt.getOverride(); - } - } - return m; - } - @Override public TreeVisitor getVisitor(Accumulator acc) { return Preconditions.check(new NoMissingTypes(), From c9c21caaec3df54ff9e579e2b2d2d420be0c02cb Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 15 Jun 2025 00:46:59 +0200 Subject: [PATCH 22/37] Move methods into named visitor for logical grouping --- .../staticanalysis/RemoveUnusedParams.java | 184 +++++++++--------- 1 file changed, 93 insertions(+), 91 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 1c02f37408..1e63d1f2a5 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -15,6 +15,7 @@ */ package org.openrewrite.staticanalysis; +import lombok.RequiredArgsConstructor; import org.openrewrite.*; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.MethodMatcher; @@ -61,7 +62,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex JavaType.Method mt = m.getMethodType(); if (mt != null && mt.isOverride()) { while (mt != null) { - String signature = buildSignature(mt); + String signature = MethodMatcher.methodPattern(mt); acc.overrideSignatures.add(signature); mt = mt.getOverride(); } @@ -73,123 +74,124 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex @Override public TreeVisitor getVisitor(Accumulator acc) { - return Preconditions.check(new NoMissingTypes(), - Repeat.repeatUntilStable(new JavaIsoVisitor() { - @Override - public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { - J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); - if (shouldPruneParameters(m, acc)) { - List prunedParams = filterUnusedParameters(m, collectUsedParameters(m)); - return prunedParams.equals(m.getParameters()) ? m : m.withParameters(prunedParams); - } - return m; - } - }) - ); + return Preconditions.check( + new NoMissingTypes(), + Repeat.repeatUntilStable(new RemoveUnusedParametersVisitor(acc))); } - private boolean shouldPruneParameters(J.MethodDeclaration m, Accumulator acc) { - if (m.getBody() == null || - m.getMethodType() == null || - m.hasModifier(J.Modifier.Type.Native) || - !m.getLeadingAnnotations().isEmpty()) return false; - String signature = buildSignature(m.getMethodType()); - return !acc.overrideSignatures.contains(signature); - } + @RequiredArgsConstructor + private static class RemoveUnusedParametersVisitor extends JavaIsoVisitor { + private final Accumulator acc; - private String buildSignature(JavaType.Method m) { - return MethodMatcher.methodPattern(m); - } + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); + if (shouldPruneParameters(m, acc)) { + List prunedParams = filterUnusedParameters(m, collectUsedParameters(m)); + return prunedParams.equals(m.getParameters()) ? m : m.withParameters(prunedParams); + } + return m; + } - private Set collectUsedParameters(J.MethodDeclaration m) { - Set used = new HashSet<>(); - Deque> shadowStack = new ArrayDeque<>(); - new JavaIsoVisitor>() { - @Override - public J.Block visitBlock(J.Block block, Set u) { - shadowStack.push(new HashSet<>()); - try { - return super.visitBlock(block, u); - } finally { - shadowStack.pop(); + private boolean shouldPruneParameters(J.MethodDeclaration m, Accumulator acc) { + if (m.getBody() == null || + m.getMethodType() == null || + m.hasModifier(J.Modifier.Type.Native) || + !m.getLeadingAnnotations().isEmpty()) return false; + String signature = MethodMatcher.methodPattern(m.getMethodType()); + return !acc.overrideSignatures.contains(signature); + } + + private Set collectUsedParameters(J.MethodDeclaration m) { + Set used = new HashSet<>(); + Deque> shadowStack = new ArrayDeque<>(); + + new JavaIsoVisitor>() { + @Override + public J.Block visitBlock(J.Block block, Set u) { + shadowStack.push(new HashSet<>()); + try { + return super.visitBlock(block, u); + } finally { + shadowStack.pop(); + } } - } - @Override - public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations decl, Set u) { - decl.getVariables().forEach(v -> shadowStack.peek().add(v.getSimpleName())); - return super.visitVariableDeclarations(decl, u); - } + @Override + public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations decl, Set u) { + decl.getVariables().forEach(v -> shadowStack.peek().add(v.getSimpleName())); + return super.visitVariableDeclarations(decl, u); + } - @Override - public J.Identifier visitIdentifier(J.Identifier id, Set u) { - if (isVisibleParameter(id, m, shadowStack)) { - u.add(id.getSimpleName()); + @Override + public J.Identifier visitIdentifier(J.Identifier id, Set u) { + if (isVisibleParameter(id, m, shadowStack)) { + u.add(id.getSimpleName()); + } + return id; } - return id; - } - }.visit(m.getBody(), used); + }.visit(m.getBody(), used); - return used; - } + return used; + } - private boolean isVisibleParameter(J.Identifier id, J.MethodDeclaration m, Deque> shadowStack) { - return !isShadowed(id.getSimpleName(), shadowStack) && isDeclaredAsParameter(id.getSimpleName(), m); - } + private boolean isVisibleParameter(J.Identifier id, J.MethodDeclaration m, Deque> shadowStack) { + return !isShadowed(id.getSimpleName(), shadowStack) && isDeclaredAsParameter(id.getSimpleName(), m); + } - private boolean isShadowed(String name, Deque> shadowStack) { - for (Set scope : shadowStack) { - if (scope.contains(name)) { - return true; + private boolean isShadowed(String name, Deque> shadowStack) { + for (Set scope : shadowStack) { + if (scope.contains(name)) { + return true; + } } + return false; } - return false; - } - private boolean isDeclaredAsParameter(String name, J.MethodDeclaration m) { - for (Statement p : m.getParameters()) { - if (p instanceof J.VariableDeclarations) { - for (J.VariableDeclarations.NamedVariable v : ((J.VariableDeclarations) p).getVariables()) { - if (v.getSimpleName().equals(name)) { - return true; + private boolean isDeclaredAsParameter(String name, J.MethodDeclaration m) { + for (Statement p : m.getParameters()) { + if (p instanceof J.VariableDeclarations) { + for (J.VariableDeclarations.NamedVariable v : ((J.VariableDeclarations) p).getVariables()) { + if (v.getSimpleName().equals(name)) { + return true; + } } } } + return false; } - return false; - } - private List filterUnusedParameters(J.MethodDeclaration method, Set usedParams) { - List result = new ArrayList<>(method.getParameters().size()); - for (Statement param : method.getParameters()) { - if (param instanceof J.VariableDeclarations) { - processVariableDeclaration((J.VariableDeclarations) param, usedParams, result); - } else { - result.add(param); + private List filterUnusedParameters(J.MethodDeclaration method, Set usedParams) { + List result = new ArrayList<>(method.getParameters().size()); + for (Statement param : method.getParameters()) { + if (param instanceof J.VariableDeclarations) { + processVariableDeclaration((J.VariableDeclarations) param, usedParams, result); + } else { + result.add(param); + } } + return result; } - return result; - } - private void processVariableDeclaration(J.VariableDeclarations decl, Set usedParams, List result) { - List kept = keepUsedVariables(decl, usedParams); + private void processVariableDeclaration(J.VariableDeclarations decl, Set usedParams, List result) { + List kept = keepUsedVariables(decl, usedParams); - if (!kept.isEmpty()) { - result.add(decl.withVariables(kept)); - } else if (!decl.getLeadingAnnotations().isEmpty()) { - result.add(decl); + if (!kept.isEmpty()) { + result.add(decl.withVariables(kept)); + } else if (!decl.getLeadingAnnotations().isEmpty()) { + result.add(decl); + } } - } - private List keepUsedVariables(J.VariableDeclarations decl, Set usedParams) { - List kept = new ArrayList<>(decl.getVariables().size()); - for (J.VariableDeclarations.NamedVariable v : decl.getVariables()) { - if (usedParams.contains(v.getSimpleName())) { - kept.add(v); + private List keepUsedVariables(J.VariableDeclarations decl, Set usedParams) { + List kept = new ArrayList<>(decl.getVariables().size()); + for (J.VariableDeclarations.NamedVariable v : decl.getVariables()) { + if (usedParams.contains(v.getSimpleName())) { + kept.add(v); + } } + return kept; } - return kept; } - } From d1bf138c679a453ee8ec2de5de5ec1571074db7e Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 15 Jun 2025 00:47:37 +0200 Subject: [PATCH 23/37] Add missing braces --- .../org/openrewrite/staticanalysis/RemoveUnusedParams.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 1e63d1f2a5..d2b1fc24df 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -93,12 +93,13 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex return m; } - private boolean shouldPruneParameters(J.MethodDeclaration m, Accumulator acc) { if (m.getBody() == null || m.getMethodType() == null || m.hasModifier(J.Modifier.Type.Native) || - !m.getLeadingAnnotations().isEmpty()) return false; + !m.getLeadingAnnotations().isEmpty()) { + return false; + } String signature = MethodMatcher.methodPattern(m.getMethodType()); return !acc.overrideSignatures.contains(signature); } From 25ece103b920a4e9ee3ac46834f54abf62abc105 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 15 Jun 2025 00:48:33 +0200 Subject: [PATCH 24/37] Use `reduce` in `collectUsedParameters` --- .../openrewrite/staticanalysis/RemoveUnusedParams.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index d2b1fc24df..7abaad792f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -105,10 +105,8 @@ private boolean shouldPruneParameters(J.MethodDeclaration m, Accumulator acc) { } private Set collectUsedParameters(J.MethodDeclaration m) { - Set used = new HashSet<>(); Deque> shadowStack = new ArrayDeque<>(); - - new JavaIsoVisitor>() { + return new JavaIsoVisitor>() { @Override public J.Block visitBlock(J.Block block, Set u) { shadowStack.push(new HashSet<>()); @@ -132,9 +130,7 @@ public J.Identifier visitIdentifier(J.Identifier id, Set u) { } return id; } - }.visit(m.getBody(), used); - - return used; + }.reduce(m.getBody(), new HashSet<>()); } private boolean isVisibleParameter(J.Identifier id, J.MethodDeclaration m, Deque> shadowStack) { From 6bd040507e13a2bff9c5bffddfc2275c3cb56a28 Mon Sep 17 00:00:00 2001 From: Deepak Date: Tue, 17 Jun 2025 17:28:06 +0530 Subject: [PATCH 25/37] use ListUtils for referential comparison --- .../staticanalysis/RemoveUnusedParams.java | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 7abaad792f..21f894e556 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -17,6 +17,7 @@ import lombok.RequiredArgsConstructor; import org.openrewrite.*; +import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.NoMissingTypes; @@ -86,14 +87,14 @@ private static class RemoveUnusedParametersVisitor extends JavaIsoVisitor prunedParams = filterUnusedParameters(m, collectUsedParameters(m)); - return prunedParams.equals(m.getParameters()) ? m : m.withParameters(prunedParams); + if (shouldPruneParameters(m)) { + List prunedParams = filterUnusedParameters(m); + return prunedParams == m.getParameters() ? m : m.withParameters(prunedParams); } return m; } - private boolean shouldPruneParameters(J.MethodDeclaration m, Accumulator acc) { + private boolean shouldPruneParameters(J.MethodDeclaration m) { if (m.getBody() == null || m.getMethodType() == null || m.hasModifier(J.Modifier.Type.Native) || @@ -159,26 +160,28 @@ private boolean isDeclaredAsParameter(String name, J.MethodDeclaration m) { return false; } - private List filterUnusedParameters(J.MethodDeclaration method, Set usedParams) { - List result = new ArrayList<>(method.getParameters().size()); - for (Statement param : method.getParameters()) { - if (param instanceof J.VariableDeclarations) { - processVariableDeclaration((J.VariableDeclarations) param, usedParams, result); - } else { - result.add(param); - } - } - return result; + private List filterUnusedParameters(J.MethodDeclaration m) { + return ListUtils.map( + m.getParameters(), + p -> { + if (!(p instanceof J.VariableDeclarations)) { + return p; + } + return processVariableDeclaration((J.VariableDeclarations) p, collectUsedParameters(m)); + } + ); } - private void processVariableDeclaration(J.VariableDeclarations decl, Set usedParams, List result) { + private Statement processVariableDeclaration(J.VariableDeclarations decl, Set usedParams) { + // exactly the same code you had in the lambda: List kept = keepUsedVariables(decl, usedParams); if (!kept.isEmpty()) { - result.add(decl.withVariables(kept)); + return decl.withVariables(kept); } else if (!decl.getLeadingAnnotations().isEmpty()) { - result.add(decl); + return decl; } + return null; } private List keepUsedVariables(J.VariableDeclarations decl, Set usedParams) { From 459d3276a6dfa2cb2ac7dfc1680c057cef7105f9 Mon Sep 17 00:00:00 2001 From: Deepak Date: Wed, 18 Jun 2025 02:02:52 +0530 Subject: [PATCH 26/37] use SemanticallyEqual comparison --- .../staticanalysis/RemoveUnusedParams.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 21f894e556..576ffb83e5 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -21,6 +21,7 @@ import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.NoMissingTypes; +import org.openrewrite.java.search.SemanticallyEqual; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.Statement; @@ -106,7 +107,7 @@ private boolean shouldPruneParameters(J.MethodDeclaration m) { } private Set collectUsedParameters(J.MethodDeclaration m) { - Deque> shadowStack = new ArrayDeque<>(); + Deque> shadowStack = new ArrayDeque<>(); return new JavaIsoVisitor>() { @Override public J.Block visitBlock(J.Block block, Set u) { @@ -120,7 +121,7 @@ public J.Block visitBlock(J.Block block, Set u) { @Override public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations decl, Set u) { - decl.getVariables().forEach(v -> shadowStack.peek().add(v.getSimpleName())); + decl.getVariables().forEach(v -> shadowStack.peek().add(v.getName())); return super.visitVariableDeclarations(decl, u); } @@ -134,24 +135,26 @@ public J.Identifier visitIdentifier(J.Identifier id, Set u) { }.reduce(m.getBody(), new HashSet<>()); } - private boolean isVisibleParameter(J.Identifier id, J.MethodDeclaration m, Deque> shadowStack) { - return !isShadowed(id.getSimpleName(), shadowStack) && isDeclaredAsParameter(id.getSimpleName(), m); + private boolean isVisibleParameter(J.Identifier id, J.MethodDeclaration m, Deque> shadowStack) { + return !isShadowed(id, shadowStack) && isDeclaredAsParameter(id, m); } - private boolean isShadowed(String name, Deque> shadowStack) { - for (Set scope : shadowStack) { - if (scope.contains(name)) { - return true; + private boolean isShadowed(J.Identifier id, Deque> shadowStack) { + for (Set scope : shadowStack) { + for (J.Identifier local : scope) { + if (SemanticallyEqual.areEqual(id, local)) { + return true; + } } } return false; } - private boolean isDeclaredAsParameter(String name, J.MethodDeclaration m) { + private boolean isDeclaredAsParameter(J.Identifier id, J.MethodDeclaration m) { for (Statement p : m.getParameters()) { if (p instanceof J.VariableDeclarations) { for (J.VariableDeclarations.NamedVariable v : ((J.VariableDeclarations) p).getVariables()) { - if (v.getSimpleName().equals(name)) { + if (v.getSimpleName().equals(id.getSimpleName())) { return true; } } @@ -173,7 +176,6 @@ private List filterUnusedParameters(J.MethodDeclaration m) { } private Statement processVariableDeclaration(J.VariableDeclarations decl, Set usedParams) { - // exactly the same code you had in the lambda: List kept = keepUsedVariables(decl, usedParams); if (!kept.isEmpty()) { From 12ccb6c06709252eeb9d1ea2e61ee642db4d3a02 Mon Sep 17 00:00:00 2001 From: Deepak Date: Wed, 18 Jun 2025 08:09:40 +0530 Subject: [PATCH 27/37] fix avoidDirectConflict and avoidInheritedConflict --- .../staticanalysis/RemoveUnusedParams.java | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 576ffb83e5..5c0fa9dc57 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -38,6 +38,9 @@ public static class Accumulator { * and will not be removed even if they appear unused. */ private final Set overrideSignatures = new HashSet<>(); + + /** All original signatures (including same-class overloads) **/ + private final Set originalSignatures = new HashSet<>(); } @Override @@ -62,6 +65,7 @@ public TreeVisitor getScanner(Accumulator acc) { public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); JavaType.Method mt = m.getMethodType(); + acc.originalSignatures.add(MethodMatcher.methodPattern(mt)); if (mt != null && mt.isOverride()) { while (mt != null) { String signature = MethodMatcher.methodPattern(mt); @@ -90,7 +94,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); if (shouldPruneParameters(m)) { List prunedParams = filterUnusedParameters(m); - return prunedParams == m.getParameters() ? m : m.withParameters(prunedParams); + return prunedParams == m.getParameters() ? m : applyPrunedSignature(m, prunedParams); } return m; } @@ -177,7 +181,6 @@ private List filterUnusedParameters(J.MethodDeclaration m) { private Statement processVariableDeclaration(J.VariableDeclarations decl, Set usedParams) { List kept = keepUsedVariables(decl, usedParams); - if (!kept.isEmpty()) { return decl.withVariables(kept); } else if (!decl.getLeadingAnnotations().isEmpty()) { @@ -195,5 +198,44 @@ private List keepUsedVariables(J.VariableD } return kept; } + + private J.MethodDeclaration applyPrunedSignature(J.MethodDeclaration original, List pruned) { + J.MethodDeclaration candidate = original.withParameters(pruned) + .withMethodType( + original.getMethodType() + .withParameterTypes(collectParameterTypes(pruned)) + ); + + String fullSig = MethodMatcher.methodPattern(candidate); + String tail = fullSig.substring(fullSig.indexOf(' ') + 1); + + for (String existing : acc.overrideSignatures) { + if (existing.endsWith(tail)) { + return original; + } + } + for (String existing : acc.originalSignatures) { + if (existing.endsWith(tail)) { + return original; + } + } + return candidate; + } + + private static List collectParameterTypes(List prunedParams) { + List newParamTypes = new ArrayList<>(); + for (Statement stmt : prunedParams) { + if (stmt instanceof J.VariableDeclarations) { + J.VariableDeclarations decl = (J.VariableDeclarations) stmt; + for (J.VariableDeclarations.NamedVariable v : decl.getVariables()) { + JavaType t = v.getType(); + if (t != null) { + newParamTypes.add(t); + } + } + } + } + return newParamTypes; + } } } From c5275064540b8d914f689f3adddae6f571c82f83 Mon Sep 17 00:00:00 2001 From: Deepak Date: Wed, 18 Jun 2025 20:57:41 +0530 Subject: [PATCH 28/37] fix avoidDirectConflict --- .../staticanalysis/RemoveUnusedParams.java | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 5c0fa9dc57..369a9450eb 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -39,8 +39,7 @@ public static class Accumulator { */ private final Set overrideSignatures = new HashSet<>(); - /** All original signatures (including same-class overloads) **/ - private final Set originalSignatures = new HashSet<>(); + private final Map> originalSignatures = new HashMap<>(); } @Override @@ -65,11 +64,12 @@ public TreeVisitor getScanner(Accumulator acc) { public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); JavaType.Method mt = m.getMethodType(); - acc.originalSignatures.add(MethodMatcher.methodPattern(mt)); + String className = mt.getDeclaringType().toString(); + acc.originalSignatures.computeIfAbsent(className, k -> new HashSet<>()) + .add(MethodMatcher.methodPattern(mt)); if (mt != null && mt.isOverride()) { while (mt != null) { - String signature = MethodMatcher.methodPattern(mt); - acc.overrideSignatures.add(signature); + acc.overrideSignatures.add(MethodMatcher.methodPattern(mt)); mt = mt.getOverride(); } } @@ -93,7 +93,7 @@ private static class RemoveUnusedParametersVisitor extends JavaIsoVisitor prunedParams = filterUnusedParameters(m); + List prunedParams = filterUnusedParameters(m, collectUsedParameters(m)); return prunedParams == m.getParameters() ? m : applyPrunedSignature(m, prunedParams); } return m; @@ -106,8 +106,7 @@ private boolean shouldPruneParameters(J.MethodDeclaration m) { !m.getLeadingAnnotations().isEmpty()) { return false; } - String signature = MethodMatcher.methodPattern(m.getMethodType()); - return !acc.overrideSignatures.contains(signature); + return !acc.overrideSignatures.contains(MethodMatcher.methodPattern(m.getMethodType())); } private Set collectUsedParameters(J.MethodDeclaration m) { @@ -167,14 +166,14 @@ private boolean isDeclaredAsParameter(J.Identifier id, J.MethodDeclaration m) { return false; } - private List filterUnusedParameters(J.MethodDeclaration m) { + private List filterUnusedParameters(J.MethodDeclaration m, Set usedParams) { return ListUtils.map( m.getParameters(), p -> { if (!(p instanceof J.VariableDeclarations)) { return p; } - return processVariableDeclaration((J.VariableDeclarations) p, collectUsedParameters(m)); + return processVariableDeclaration((J.VariableDeclarations) p, usedParams); } ); } @@ -207,18 +206,12 @@ private J.MethodDeclaration applyPrunedSignature(J.MethodDeclaration original, L ); String fullSig = MethodMatcher.methodPattern(candidate); - String tail = fullSig.substring(fullSig.indexOf(' ') + 1); - for (String existing : acc.overrideSignatures) { - if (existing.endsWith(tail)) { - return original; - } - } - for (String existing : acc.originalSignatures) { - if (existing.endsWith(tail)) { - return original; - } + if (acc.overrideSignatures.contains(fullSig) + || acc.originalSignatures.get(fullSig.substring(0,fullSig.indexOf(' '))).contains(fullSig)) { + return original; } + return candidate; } From dd23526f69a585510cd83215ca5aecfabc08e887 Mon Sep 17 00:00:00 2001 From: Deepak Date: Wed, 18 Jun 2025 21:41:03 +0530 Subject: [PATCH 29/37] fix avoidInheritiedConflict --- .../staticanalysis/RemoveUnusedParams.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 369a9450eb..fded9078e8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -206,12 +206,28 @@ private J.MethodDeclaration applyPrunedSignature(J.MethodDeclaration original, L ); String fullSig = MethodMatcher.methodPattern(candidate); + String tail = fullSig.substring(fullSig.indexOf(' ') + 1); if (acc.overrideSignatures.contains(fullSig) || acc.originalSignatures.get(fullSig.substring(0,fullSig.indexOf(' '))).contains(fullSig)) { return original; } + JavaType.Method mt = candidate.getMethodType(); + if (mt != null && mt.getDeclaringType() instanceof JavaType.Class) { + JavaType.Class cls = (JavaType.Class) mt.getDeclaringType(); + JavaType.Class superCls = (JavaType.Class) cls.getSupertype(); + if (superCls != null) { + String superKey = superCls.getFullyQualifiedName() + " " + tail; + Set superSigs = acc.originalSignatures + .getOrDefault(superCls.getFullyQualifiedName(), + Collections.emptySet()); + if (superSigs.contains(superKey)) { + return original; + } + } + } + return candidate; } From 03056a524e9895536dcae85a7dd16079eab388f3 Mon Sep 17 00:00:00 2001 From: Deepak Date: Thu, 19 Jun 2025 01:35:58 +0530 Subject: [PATCH 30/37] clean up --- .../staticanalysis/RemoveUnusedParams.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index fded9078e8..d01e94e8f9 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -16,6 +16,7 @@ package org.openrewrite.staticanalysis; import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; @@ -206,13 +207,18 @@ private J.MethodDeclaration applyPrunedSignature(J.MethodDeclaration original, L ); String fullSig = MethodMatcher.methodPattern(candidate); - String tail = fullSig.substring(fullSig.indexOf(' ') + 1); - if (acc.overrideSignatures.contains(fullSig) - || acc.originalSignatures.get(fullSig.substring(0,fullSig.indexOf(' '))).contains(fullSig)) { + || acc.originalSignatures.get(fullSig.substring(0,fullSig.indexOf(' '))).contains(fullSig) + || conflictsWithSuperClassMethods(original, candidate, + fullSig.substring(fullSig.indexOf(' ') + 1)) != null) { return original; } + return candidate; + } + @Nullable + private J.MethodDeclaration conflictsWithSuperClassMethods(J.MethodDeclaration original, + J.MethodDeclaration candidate, String tail) { JavaType.Method mt = candidate.getMethodType(); if (mt != null && mt.getDeclaringType() instanceof JavaType.Class) { JavaType.Class cls = (JavaType.Class) mt.getDeclaringType(); @@ -227,8 +233,7 @@ private J.MethodDeclaration applyPrunedSignature(J.MethodDeclaration original, L } } } - - return candidate; + return null; } private static List collectParameterTypes(List prunedParams) { From f4a0ac00affc5e8c3dbe0c21535285001f485fc7 Mon Sep 17 00:00:00 2001 From: Deepak Date: Thu, 19 Jun 2025 02:07:10 +0530 Subject: [PATCH 31/37] clean up --- .../java/org/openrewrite/staticanalysis/RemoveUnusedParams.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index d01e94e8f9..9242c22213 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -16,7 +16,6 @@ package org.openrewrite.staticanalysis; import lombok.RequiredArgsConstructor; -import org.jetbrains.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; @@ -216,7 +215,6 @@ private J.MethodDeclaration applyPrunedSignature(J.MethodDeclaration original, L return candidate; } - @Nullable private J.MethodDeclaration conflictsWithSuperClassMethods(J.MethodDeclaration original, J.MethodDeclaration candidate, String tail) { JavaType.Method mt = candidate.getMethodType(); From e1f414a5cb7fdf6bd4dac145db913e5b266992ef Mon Sep 17 00:00:00 2001 From: iddeepak <87892182+iddeepak@users.noreply.github.com> Date: Thu, 19 Jun 2025 02:14:18 +0530 Subject: [PATCH 32/37] Update src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../org/openrewrite/staticanalysis/RemoveUnusedParams.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 9242c22213..eb94e3a9c3 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -206,9 +206,9 @@ private J.MethodDeclaration applyPrunedSignature(J.MethodDeclaration original, L ); String fullSig = MethodMatcher.methodPattern(candidate); - if (acc.overrideSignatures.contains(fullSig) - || acc.originalSignatures.get(fullSig.substring(0,fullSig.indexOf(' '))).contains(fullSig) - || conflictsWithSuperClassMethods(original, candidate, + if (acc.overrideSignatures.contains(fullSig) || + acc.originalSignatures.get(fullSig.substring(0,fullSig.indexOf(' '))).contains(fullSig) || + conflictsWithSuperClassMethods(original, candidate, fullSig.substring(fullSig.indexOf(' ') + 1)) != null) { return original; } From 2cdeff6b3bc196e4c371ec6a018249fa44945548 Mon Sep 17 00:00:00 2001 From: Deepak Date: Thu, 19 Jun 2025 03:00:15 +0530 Subject: [PATCH 33/37] fix cascadeRemove --- .../staticanalysis/RemoveUnusedParams.java | 79 ++++++++++++++++--- 1 file changed, 67 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index eb94e3a9c3..e621c0e790 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -22,6 +22,7 @@ import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.NoMissingTypes; import org.openrewrite.java.search.SemanticallyEqual; +import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.Statement; @@ -198,20 +199,74 @@ private List keepUsedVariables(J.VariableD return kept; } - private J.MethodDeclaration applyPrunedSignature(J.MethodDeclaration original, List pruned) { - J.MethodDeclaration candidate = original.withParameters(pruned) - .withMethodType( - original.getMethodType() - .withParameterTypes(collectParameterTypes(pruned)) - ); - - String fullSig = MethodMatcher.methodPattern(candidate); - if (acc.overrideSignatures.contains(fullSig) || - acc.originalSignatures.get(fullSig.substring(0,fullSig.indexOf(' '))).contains(fullSig) || - conflictsWithSuperClassMethods(original, candidate, - fullSig.substring(fullSig.indexOf(' ') + 1)) != null) { + private J.MethodDeclaration applyPrunedSignature(J.MethodDeclaration original, + List pruned) { + // Identify exactly which parameter positions were removed + List originalParams = original.getParameters(); + Set removedIndexes = new HashSet<>(); + for (int i = 0; i < originalParams.size(); i++) { + if (!pruned.contains(originalParams.get(i))) { + removedIndexes.add(i); + } + } + + // Build the pruned method declaration + JavaType.Method originalType = original.getMethodType(); + List prunedParamTypes = collectParameterTypes(pruned); + J.MethodDeclaration candidate = original + .withParameters(pruned) + .withMethodType(originalType.withParameterTypes(prunedParamTypes)); + + // Do override/original‐signature/superclass conflict checks + String fullSignature = MethodMatcher.methodPattern(candidate); + int split = fullSignature.indexOf(' '); + String qualifier = fullSignature.substring(0, split); + String signatureTail = fullSignature.substring(split + 1); + + if (acc.overrideSignatures.contains(fullSignature) + || acc.originalSignatures + .getOrDefault(qualifier, Collections.emptySet()) + .contains(fullSignature) + || conflictsWithSuperClassMethods(original, candidate, signatureTail) != null) { return original; } + + // Schedule a one‐off visitor to prune matching call‐site arguments + String oldSignature = MethodMatcher.methodPattern(originalType); + doAfterVisit(new JavaIsoVisitor() { + private final MethodMatcher matcher = new MethodMatcher(oldSignature); + + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation invocation, + ExecutionContext ctx) { + J.MethodInvocation m = super.visitMethodInvocation(invocation, ctx); + if (matcher.matches(m) && m.getArguments().size() != prunedParamTypes.size()) { + // Trim the argument list + List keptArgs = new ArrayList<>(); + for (int i = 0; i < m.getArguments().size(); i++) { + if (!removedIndexes.contains(i)) { + keptArgs.add(m.getArguments().get(i)); + } + } + // Trim the MethodType parameter list + JavaType.Method mt = m.getMethodType(); + List keptTypes = new ArrayList<>(); + for (int i = 0; i < mt.getParameterTypes().size(); i++) { + if (!removedIndexes.contains(i)) { + keptTypes.add(mt.getParameterTypes().get(i)); + } + } + JavaType.Method updatedType = mt.withParameterTypes(keptTypes); + // Update the name identifier to carry the same type instance + J.Identifier newName = m.getName().withType(updatedType); + return m.withArguments(keptArgs) + .withMethodType(updatedType) + .withName(newName); + } + return m; + } + }); + return candidate; } From 4f6b3465f9f4d31017979bbe7e862497406d6de3 Mon Sep 17 00:00:00 2001 From: iddeepak <87892182+iddeepak@users.noreply.github.com> Date: Thu, 19 Jun 2025 03:07:35 +0530 Subject: [PATCH 34/37] Update src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../openrewrite/staticanalysis/RemoveUnusedParams.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index e621c0e790..8745c85bb9 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -223,11 +223,10 @@ private J.MethodDeclaration applyPrunedSignature(J.MethodDeclaration original, String qualifier = fullSignature.substring(0, split); String signatureTail = fullSignature.substring(split + 1); - if (acc.overrideSignatures.contains(fullSignature) - || acc.originalSignatures - .getOrDefault(qualifier, Collections.emptySet()) - .contains(fullSignature) - || conflictsWithSuperClassMethods(original, candidate, signatureTail) != null) { + if (acc.overrideSignatures.contains(fullSignature) || + acc.originalSignatures + .contains(fullSignature) || + conflictsWithSuperClassMethods(original, candidate, signatureTail) != null) { return original; } From ec388d6bf015cba768bd3a7e9934c6ee05357026 Mon Sep 17 00:00:00 2001 From: Deepak Date: Thu, 19 Jun 2025 03:09:49 +0530 Subject: [PATCH 35/37] fix build --- .../java/org/openrewrite/staticanalysis/RemoveUnusedParams.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 8745c85bb9..0b0fd63d36 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -225,6 +225,7 @@ private J.MethodDeclaration applyPrunedSignature(J.MethodDeclaration original, if (acc.overrideSignatures.contains(fullSignature) || acc.originalSignatures + .getOrDefault(qualifier, Collections.emptySet()) .contains(fullSignature) || conflictsWithSuperClassMethods(original, candidate, signatureTail) != null) { return original; From 5be9333ea62f22018ecd6b14f8c0e3497047bde9 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 28 Jul 2025 01:18:19 +0200 Subject: [PATCH 36/37] Update src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../org/openrewrite/staticanalysis/RemoveUnusedParams.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 0b0fd63d36..52befe7d51 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -183,7 +183,8 @@ private Statement processVariableDeclaration(J.VariableDeclarations decl, Set kept = keepUsedVariables(decl, usedParams); if (!kept.isEmpty()) { return decl.withVariables(kept); - } else if (!decl.getLeadingAnnotations().isEmpty()) { + } + if (!decl.getLeadingAnnotations().isEmpty()) { return decl; } return null; From 1ee3249c93a5142485a93d8469cdbfbf4936a3fe Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 4 Aug 2025 18:38:33 +0200 Subject: [PATCH 37/37] Reduce warnings --- .../staticanalysis/RemoveUnusedParams.java | 32 +++---- .../RemoveUnusedParamsTest.java | 84 +++++++++---------- 2 files changed, 59 insertions(+), 57 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java index 52befe7d51..c20ddcaae1 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedParams.java @@ -16,6 +16,7 @@ package org.openrewrite.staticanalysis; import lombok.RequiredArgsConstructor; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; @@ -29,6 +30,9 @@ import java.util.*; +import static java.util.Collections.emptySet; +import static java.util.Objects.requireNonNull; + public class RemoveUnusedParams extends ScanningRecipe { public static class Accumulator { /** @@ -40,7 +44,7 @@ public static class Accumulator { */ private final Set overrideSignatures = new HashSet<>(); - private final Map> originalSignatures = new HashMap<>(); + private final Map> originalSignatures = new HashMap<>(); } @Override @@ -64,11 +68,11 @@ public TreeVisitor getScanner(Accumulator acc) { @Override public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); - JavaType.Method mt = m.getMethodType(); + JavaType.Method mt = requireNonNull(m.getMethodType()); String className = mt.getDeclaringType().toString(); acc.originalSignatures.computeIfAbsent(className, k -> new HashSet<>()) .add(MethodMatcher.methodPattern(mt)); - if (mt != null && mt.isOverride()) { + if (mt.isOverride()) { while (mt != null) { acc.overrideSignatures.add(MethodMatcher.methodPattern(mt)); mt = mt.getOverride(); @@ -158,7 +162,7 @@ private boolean isDeclaredAsParameter(J.Identifier id, J.MethodDeclaration m) { for (Statement p : m.getParameters()) { if (p instanceof J.VariableDeclarations) { for (J.VariableDeclarations.NamedVariable v : ((J.VariableDeclarations) p).getVariables()) { - if (v.getSimpleName().equals(id.getSimpleName())) { + if (SemanticallyEqual.areEqual(v.getName(), id)) { return true; } } @@ -179,7 +183,7 @@ private List filterUnusedParameters(J.MethodDeclaration m, Set usedParams) { + private @Nullable Statement processVariableDeclaration(J.VariableDeclarations decl, Set usedParams) { List kept = keepUsedVariables(decl, usedParams); if (!kept.isEmpty()) { return decl.withVariables(kept); @@ -221,13 +225,13 @@ private J.MethodDeclaration applyPrunedSignature(J.MethodDeclaration original, // Do override/original‐signature/superclass conflict checks String fullSignature = MethodMatcher.methodPattern(candidate); int split = fullSignature.indexOf(' '); - String qualifier = fullSignature.substring(0, split); + String qualifier = fullSignature.substring(0, split); String signatureTail = fullSignature.substring(split + 1); if (acc.overrideSignatures.contains(fullSignature) || acc.originalSignatures - .getOrDefault(qualifier, Collections.emptySet()) - .contains(fullSignature) || + .getOrDefault(qualifier, emptySet()) + .contains(fullSignature) || conflictsWithSuperClassMethods(original, candidate, signatureTail) != null) { return original; } @@ -271,17 +275,15 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation invocation, return candidate; } - private J.MethodDeclaration conflictsWithSuperClassMethods(J.MethodDeclaration original, - J.MethodDeclaration candidate, String tail) { + private J.@Nullable MethodDeclaration conflictsWithSuperClassMethods( + J.MethodDeclaration original, J.MethodDeclaration candidate, String tail) { JavaType.Method mt = candidate.getMethodType(); if (mt != null && mt.getDeclaringType() instanceof JavaType.Class) { - JavaType.Class cls = (JavaType.Class) mt.getDeclaringType(); - JavaType.Class superCls = (JavaType.Class) cls.getSupertype(); + JavaType.Class cls = (JavaType.Class) mt.getDeclaringType(); + JavaType.Class superCls = (JavaType.Class) cls.getSupertype(); if (superCls != null) { String superKey = superCls.getFullyQualifiedName() + " " + tail; - Set superSigs = acc.originalSignatures - .getOrDefault(superCls.getFullyQualifiedName(), - Collections.emptySet()); + Set superSigs = acc.originalSignatures.getOrDefault(superCls.getFullyQualifiedName(), emptySet()); if (superSigs.contains(superKey)) { return original; } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java index cc920906d7..f8d452eaa9 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedParamsTest.java @@ -315,29 +315,29 @@ void skipWronglyDueToSignatureCollision() { rewriteRun( java( """ - class Base { - void foo(int a, String b) {} - } - class Derived extends Base { - @Override - void foo(int a, String b) {} - void foo(String a, int b) { - // no use of a or b - } - } - """, - """ - class Base { - void foo(int a, String b) {} - } - class Derived extends Base { - @Override - void foo(int a, String b) {} - void foo() { - // no use of a or b - } - } + class Base { + void foo(int a, String b) {} + } + class Derived extends Base { + @Override + void foo(int a, String b) {} + void foo(String a, int b) { + // no use of a or b + } + } + """, """ + class Base { + void foo(int a, String b) {} + } + class Derived extends Base { + @Override + void foo(int a, String b) {} + void foo() { + // no use of a or b + } + } + """ ) ); } @@ -347,29 +347,29 @@ void nestedInheritanceOverrideChain() { rewriteRun( java( """ - class Lone { void solo(String x) {} } + class Lone { void solo(String x) {} } - class Grandparent { void greet(String msg) {} } - class Parent extends Grandparent { } - class Child extends Parent { - @Override - void greet(String msg) { - // required override - } - } - """, + class Grandparent { void greet(String msg) {} } + class Parent extends Grandparent { } + class Child extends Parent { + @Override + void greet(String msg) { + // required override + } + } + """, """ - class Lone { void solo() {} } + class Lone { void solo() {} } - class Grandparent { void greet(String msg) {} } - class Parent extends Grandparent { } - class Child extends Parent { - @Override - void greet(String msg) { - // required override - } - } - """ + class Grandparent { void greet(String msg) {} } + class Parent extends Grandparent { } + class Child extends Parent { + @Override + void greet(String msg) { + // required override + } + } + """ ) ); }