From 576c27a54f8fa6320bd603072c7d6c5ff1ee6ba8 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 16 Sep 2025 20:44:54 +0200 Subject: [PATCH 1/8] Add OpenRewrite recipe for JEP 512 instance main methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements a migration recipe to convert `public static void main(String[] args)` to instance `void main()` when the args parameter is unused, as introduced in JEP 512 (Java 21+). The recipe: - Detects main methods with unused String[] args parameter - Removes public and static modifiers - Removes the unused parameter - Preserves formatting and annotations - Only applies to Java 21+ codebases 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../lang/MigrateMainMethodToInstanceMain.java | 168 +++++++++++ .../META-INF/rewrite/java-version-25.yml | 1 + .../MigrateMainMethodToInstanceMainTest.java | 270 ++++++++++++++++++ 3 files changed, 439 insertions(+) create mode 100644 src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java create mode 100644 src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java diff --git a/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java new file mode 100644 index 0000000000..ef86e90f73 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java @@ -0,0 +1,168 @@ +/* + * 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.java.migrate.lang; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.InMemoryExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.search.UsesJavaVersion; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.Space; +import org.openrewrite.java.tree.TypeUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +import static java.util.Collections.singleton; + +public class MigrateMainMethodToInstanceMain extends Recipe { + @Override + public String getDisplayName() { + return "Migrate `public static void main(String[] args)` to instance `void main()`"; + } + + @Override + public String getDescription() { + return "Migrate `public static void main(String[] args)` method to instance `void main()` method when the `args` parameter is unused, as supported by JEP 512 in Java 21+."; + } + + @Override + public Set getTags() { + return singleton("java21"); + } + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check( + new UsesJavaVersion<>(21), + new MigrateMainMethodVisitor() + ); + } + + private static class MigrateMainMethodVisitor extends JavaIsoVisitor { + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + J.MethodDeclaration md = super.visitMethodDeclaration(method, ctx); + + // Check if this is a main method: public static void main(String[] args) + if (!"main".equals(md.getSimpleName()) || + md.getReturnTypeExpression() == null || + !md.getReturnTypeExpression().toString().equals("void") || + md.getParameters().size() != 1) { + return md; + } + + // Check modifiers - must have public and static + boolean hasPublic = false; + boolean hasStatic = false; + for (J.Modifier modifier : md.getModifiers()) { + if (modifier.getType() == J.Modifier.Type.Public) { + hasPublic = true; + } else if (modifier.getType() == J.Modifier.Type.Static) { + hasStatic = true; + } + } + + if (!hasPublic || !hasStatic) { + return md; + } + + // Check parameter type + if (!(md.getParameters().get(0) instanceof J.VariableDeclarations)) { + return md; + } + + J.VariableDeclarations param = (J.VariableDeclarations) md.getParameters().get(0); + if (param.getVariables().isEmpty()) { + return md; + } + + J.VariableDeclarations.NamedVariable paramVar = param.getVariables().get(0); + String paramName = paramVar.getSimpleName(); + + // Check if parameter is String[] type + JavaType paramType = param.getType(); + if (paramType == null || !TypeUtils.isOfClassType(paramType, "java.lang.String")) { + return md; + } + + // Ensure it's an array + if (!(paramType instanceof JavaType.Array)) { + return md; + } + + // Check if the parameter is used in the method body + if (md.getBody() == null || isParameterUsed(md.getBody(), paramName)) { + return md; + } + + // Remove public and static modifiers, preserve spacing + List newModifiers = new ArrayList<>(); + Space leadingSpace = null; + + for (int i = 0; i < md.getModifiers().size(); i++) { + J.Modifier modifier = md.getModifiers().get(i); + if (modifier.getType() != J.Modifier.Type.Public && + modifier.getType() != J.Modifier.Type.Static) { + if (!newModifiers.isEmpty() || leadingSpace == null) { + newModifiers.add(modifier); + } else { + // Apply the leading space to the first remaining modifier + newModifiers.add(modifier.withPrefix(leadingSpace)); + } + } else if (leadingSpace == null) { + // Capture the leading space from the first public/static modifier + leadingSpace = modifier.getPrefix(); + } + } + + // If no modifiers remain and we have a return type, preserve the spacing + if (newModifiers.isEmpty() && leadingSpace != null && md.getReturnTypeExpression() != null) { + md = md.withReturnTypeExpression(md.getReturnTypeExpression().withPrefix(leadingSpace)); + } + + // Remove the parameter + md = md.withModifiers(newModifiers) + .withParameters(new ArrayList<>()); + + return md; + } + + private boolean isParameterUsed(J.Block body, String paramName) { + AtomicBoolean used = new AtomicBoolean(false); + new JavaIsoVisitor() { + @Override + public J.Identifier visitIdentifier(J.Identifier identifier, ExecutionContext ctx) { + if (paramName.equals(identifier.getSimpleName())) { + // Check if this identifier is a variable declaration (not a usage) + J.VariableDeclarations.NamedVariable namedVar = getCursor().firstEnclosing(J.VariableDeclarations.NamedVariable.class); + if (namedVar == null || !paramName.equals(namedVar.getSimpleName())) { + used.set(true); + } + } + return super.visitIdentifier(identifier, ctx); + } + }.visit(body, new InMemoryExecutionContext()); + return used.get(); + } + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/rewrite/java-version-25.yml b/src/main/resources/META-INF/rewrite/java-version-25.yml index 5b3f2f4e4d..a569cccddf 100644 --- a/src/main/resources/META-INF/rewrite/java-version-25.yml +++ b/src/main/resources/META-INF/rewrite/java-version-25.yml @@ -31,6 +31,7 @@ recipeList: - org.openrewrite.github.SetupJavaUpgradeJavaVersion: minimumJavaMajorVersion: 25 - org.openrewrite.java.migrate.io.ReplaceSystemOutWithIOPrint + - org.openrewrite.java.migrate.lang.MigrateMainMethodToInstanceMain - org.openrewrite.java.migrate.lang.MigrateProcessWaitForDuration - org.openrewrite.java.migrate.lang.ReplaceUnusedVariablesWithUnderscore - org.openrewrite.java.migrate.util.MigrateInflaterDeflaterToClose diff --git a/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java b/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java new file mode 100644 index 0000000000..5d210d3c59 --- /dev/null +++ b/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java @@ -0,0 +1,270 @@ +/* + * 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.java.migrate.lang; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.java.Assertions.javaVersion; + +class MigrateMainMethodToInstanceMainTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new MigrateMainMethodToInstanceMain()) + .allSources(s -> s.markers(javaVersion(21))); + } + + @DocumentExample + @Test + void migrateMainMethodWithUnusedArgs() { + //language=java + rewriteRun( + java( + """ + class Application { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } + } + """, + """ + class Application { + void main() { + System.out.println("Hello, World!"); + } + } + """ + ) + ); + } + + @Test + void doNotMigrateWhenArgsIsUsed() { + //language=java + rewriteRun( + java( + """ + class Application { + public static void main(String[] args) { + if (args.length > 0) { + System.out.println("Args provided: " + args[0]); + } + } + } + """ + ) + ); + } + + @Test + void doNotMigrateWhenArgsIsUsedInMethodCall() { + //language=java + rewriteRun( + java( + """ + class Application { + public static void main(String[] args) { + processArgs(args); + } + + private static void processArgs(String[] args) { + // Process arguments + } + } + """ + ) + ); + } + + @Test + void migrateMainMethodWithEmptyBody() { + //language=java + rewriteRun( + java( + """ + class Application { + public static void main(String[] args) { + } + } + """, + """ + class Application { + void main() { + } + } + """ + ) + ); + } + + @Test + void doNotMigrateNonMainMethod() { + //language=java + rewriteRun( + java( + """ + class Application { + public static void notMain(String[] args) { + System.out.println("Not a main method"); + } + } + """ + ) + ); + } + + @Test + void doNotMigrateMainWithDifferentSignature() { + //language=java + rewriteRun( + java( + """ + class Application { + public static int main(String[] args) { + return 0; + } + } + """ + ) + ); + } + + @Test + void doNotMigratePrivateMain() { + //language=java + rewriteRun( + java( + """ + class Application { + private static void main(String[] args) { + System.out.println("Private main"); + } + } + """ + ) + ); + } + + @Test + void doNotMigrateInstanceMain() { + //language=java + rewriteRun( + java( + """ + class Application { + public void main(String[] args) { + System.out.println("Already instance main"); + } + } + """ + ) + ); + } + + @Test + void migrateMainWithComplexUnusedArgs() { + //language=java + rewriteRun( + java( + """ + class Application { + public static void main(String[] arguments) { + System.out.println("Starting application..."); + new Application().run(); + } + + void run() { + System.out.println("Running..."); + } + } + """, + """ + class Application { + void main() { + System.out.println("Starting application..."); + new Application().run(); + } + + void run() { + System.out.println("Running..."); + } + } + """ + ) + ); + } + + @Test + void doNotMigrateMainWithMultipleParameters() { + //language=java + rewriteRun( + java( + """ + class Application { + public static void main(String[] args, String extra) { + System.out.println("Invalid main method"); + } + } + """ + ) + ); + } + + @Test + void doNotMigrateMainWithNoParameters() { + //language=java + rewriteRun( + java( + """ + class Application { + public static void main() { + System.out.println("No parameters"); + } + } + """ + ) + ); + } + + @Test + void migrateMainWithAnnotations() { + //language=java + rewriteRun( + java( + """ + class Application { + @SuppressWarnings("unused") + public static void main(String[] args) { + System.out.println("Hello!"); + } + } + """, + """ + class Application { + @SuppressWarnings("unused") + void main() { + System.out.println("Hello!"); + } + } + """ + ) + ); + } +} \ No newline at end of file From ca6b95b5c5c2e128934a9b0e4b0c2725d7b6dca0 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 16 Sep 2025 20:46:25 +0200 Subject: [PATCH 2/8] Apply formatter --- .../lang/MigrateMainMethodToInstanceMain.java | 18 +- .../MigrateMainMethodToInstanceMainTest.java | 302 +++++++++--------- 2 files changed, 158 insertions(+), 162 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java index ef86e90f73..956cf0c403 100644 --- a/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java +++ b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java @@ -15,11 +15,7 @@ */ package org.openrewrite.java.migrate.lang; -import org.openrewrite.ExecutionContext; -import org.openrewrite.InMemoryExecutionContext; -import org.openrewrite.Preconditions; -import org.openrewrite.Recipe; -import org.openrewrite.TreeVisitor; +import org.openrewrite.*; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.search.UsesJavaVersion; import org.openrewrite.java.tree.J; @@ -65,9 +61,9 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex // Check if this is a main method: public static void main(String[] args) if (!"main".equals(md.getSimpleName()) || - md.getReturnTypeExpression() == null || - !md.getReturnTypeExpression().toString().equals("void") || - md.getParameters().size() != 1) { + md.getReturnTypeExpression() == null || + !md.getReturnTypeExpression().toString().equals("void") || + md.getParameters().size() != 1) { return md; } @@ -122,7 +118,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex for (int i = 0; i < md.getModifiers().size(); i++) { J.Modifier modifier = md.getModifiers().get(i); if (modifier.getType() != J.Modifier.Type.Public && - modifier.getType() != J.Modifier.Type.Static) { + modifier.getType() != J.Modifier.Type.Static) { if (!newModifiers.isEmpty() || leadingSpace == null) { newModifiers.add(modifier); } else { @@ -142,7 +138,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex // Remove the parameter md = md.withModifiers(newModifiers) - .withParameters(new ArrayList<>()); + .withParameters(new ArrayList<>()); return md; } @@ -165,4 +161,4 @@ public J.Identifier visitIdentifier(J.Identifier identifier, ExecutionContext ct return used.get(); } } -} \ No newline at end of file +} diff --git a/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java b/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java index 5d210d3c59..f129a5909a 100644 --- a/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java +++ b/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java @@ -28,7 +28,7 @@ class MigrateMainMethodToInstanceMainTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { spec.recipe(new MigrateMainMethodToInstanceMain()) - .allSources(s -> s.markers(javaVersion(21))); + .allSources(s -> s.markers(javaVersion(25))); } @DocumentExample @@ -36,22 +36,22 @@ public void defaults(RecipeSpec spec) { void migrateMainMethodWithUnusedArgs() { //language=java rewriteRun( - java( - """ - class Application { - public static void main(String[] args) { - System.out.println("Hello, World!"); - } - } - """, - """ - class Application { - void main() { - System.out.println("Hello, World!"); - } - } - """ - ) + java( + """ + class Application { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } + } + """, + """ + class Application { + void main() { + System.out.println("Hello, World!"); + } + } + """ + ) ); } @@ -59,17 +59,17 @@ void main() { void doNotMigrateWhenArgsIsUsed() { //language=java rewriteRun( - java( - """ - class Application { - public static void main(String[] args) { - if (args.length > 0) { - System.out.println("Args provided: " + args[0]); - } - } - } - """ - ) + java( + """ + class Application { + public static void main(String[] args) { + if (args.length > 0) { + System.out.println("Args provided: " + args[0]); + } + } + } + """ + ) ); } @@ -77,19 +77,19 @@ public static void main(String[] args) { void doNotMigrateWhenArgsIsUsedInMethodCall() { //language=java rewriteRun( - java( - """ - class Application { - public static void main(String[] args) { - processArgs(args); - } + java( + """ + class Application { + public static void main(String[] args) { + processArgs(args); + } - private static void processArgs(String[] args) { - // Process arguments - } - } - """ - ) + private static void processArgs(String[] args) { + // Process arguments + } + } + """ + ) ); } @@ -97,20 +97,20 @@ private static void processArgs(String[] args) { void migrateMainMethodWithEmptyBody() { //language=java rewriteRun( - java( - """ - class Application { - public static void main(String[] args) { - } - } - """, - """ - class Application { - void main() { - } - } - """ - ) + java( + """ + class Application { + public static void main(String[] args) { + } + } + """, + """ + class Application { + void main() { + } + } + """ + ) ); } @@ -118,15 +118,15 @@ void main() { void doNotMigrateNonMainMethod() { //language=java rewriteRun( - java( - """ - class Application { - public static void notMain(String[] args) { - System.out.println("Not a main method"); - } - } - """ - ) + java( + """ + class Application { + public static void notMain(String[] args) { + System.out.println("Not a main method"); + } + } + """ + ) ); } @@ -134,15 +134,15 @@ public static void notMain(String[] args) { void doNotMigrateMainWithDifferentSignature() { //language=java rewriteRun( - java( - """ - class Application { - public static int main(String[] args) { - return 0; - } - } - """ - ) + java( + """ + class Application { + public static int main(String[] args) { + return 0; + } + } + """ + ) ); } @@ -150,15 +150,15 @@ public static int main(String[] args) { void doNotMigratePrivateMain() { //language=java rewriteRun( - java( - """ - class Application { - private static void main(String[] args) { - System.out.println("Private main"); - } - } - """ - ) + java( + """ + class Application { + private static void main(String[] args) { + System.out.println("Private main"); + } + } + """ + ) ); } @@ -166,15 +166,15 @@ private static void main(String[] args) { void doNotMigrateInstanceMain() { //language=java rewriteRun( - java( - """ - class Application { - public void main(String[] args) { - System.out.println("Already instance main"); - } - } - """ - ) + java( + """ + class Application { + public void main(String[] args) { + System.out.println("Already instance main"); + } + } + """ + ) ); } @@ -182,32 +182,32 @@ public void main(String[] args) { void migrateMainWithComplexUnusedArgs() { //language=java rewriteRun( - java( - """ - class Application { - public static void main(String[] arguments) { - System.out.println("Starting application..."); - new Application().run(); - } + java( + """ + class Application { + public static void main(String[] arguments) { + System.out.println("Starting application..."); + new Application().run(); + } - void run() { - System.out.println("Running..."); - } - } - """, - """ - class Application { - void main() { - System.out.println("Starting application..."); - new Application().run(); - } + void run() { + System.out.println("Running..."); + } + } + """, + """ + class Application { + void main() { + System.out.println("Starting application..."); + new Application().run(); + } - void run() { - System.out.println("Running..."); - } - } - """ - ) + void run() { + System.out.println("Running..."); + } + } + """ + ) ); } @@ -215,15 +215,15 @@ void run() { void doNotMigrateMainWithMultipleParameters() { //language=java rewriteRun( - java( - """ - class Application { - public static void main(String[] args, String extra) { - System.out.println("Invalid main method"); - } - } - """ - ) + java( + """ + class Application { + public static void main(String[] args, String extra) { + System.out.println("Invalid main method"); + } + } + """ + ) ); } @@ -231,15 +231,15 @@ public static void main(String[] args, String extra) { void doNotMigrateMainWithNoParameters() { //language=java rewriteRun( - java( - """ - class Application { - public static void main() { - System.out.println("No parameters"); - } - } - """ - ) + java( + """ + class Application { + public static void main() { + System.out.println("No parameters"); + } + } + """ + ) ); } @@ -247,24 +247,24 @@ public static void main() { void migrateMainWithAnnotations() { //language=java rewriteRun( - java( - """ - class Application { - @SuppressWarnings("unused") - public static void main(String[] args) { - System.out.println("Hello!"); - } - } - """, - """ - class Application { - @SuppressWarnings("unused") - void main() { - System.out.println("Hello!"); - } - } - """ - ) + java( + """ + class Application { + @SuppressWarnings("unused") + public static void main(String[] args) { + System.out.println("Hello!"); + } + } + """, + """ + class Application { + @SuppressWarnings("unused") + void main() { + System.out.println("Hello!"); + } + } + """ + ) ); } -} \ No newline at end of file +} From 8724c171a2fa51142f6474cbb2eb3ade384d8e9b Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 16 Sep 2025 20:49:37 +0200 Subject: [PATCH 3/8] Inline named visitor --- .../lang/MigrateMainMethodToInstanceMain.java | 196 ++++++++---------- 1 file changed, 92 insertions(+), 104 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java index 956cf0c403..a57ae7c6f6 100644 --- a/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java +++ b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java @@ -25,11 +25,8 @@ import java.util.ArrayList; import java.util.List; -import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import static java.util.Collections.singleton; - public class MigrateMainMethodToInstanceMain extends Recipe { @Override public String getDisplayName() { @@ -38,127 +35,118 @@ public String getDisplayName() { @Override public String getDescription() { - return "Migrate `public static void main(String[] args)` method to instance `void main()` method when the `args` parameter is unused, as supported by JEP 512 in Java 21+."; - } - - @Override - public Set getTags() { - return singleton("java21"); + return "Migrate `public static void main(String[] args)` method to instance `void main()` method when the `args` parameter is unused, as supported by JEP 512 in Java 25+."; } @Override public TreeVisitor getVisitor() { - return Preconditions.check( - new UsesJavaVersion<>(21), - new MigrateMainMethodVisitor() - ); - } - - private static class MigrateMainMethodVisitor extends JavaIsoVisitor { - @Override - public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { - J.MethodDeclaration md = super.visitMethodDeclaration(method, ctx); - - // Check if this is a main method: public static void main(String[] args) - if (!"main".equals(md.getSimpleName()) || - md.getReturnTypeExpression() == null || - !md.getReturnTypeExpression().toString().equals("void") || - md.getParameters().size() != 1) { - return md; - } + return Preconditions.check(new UsesJavaVersion<>(25), new JavaIsoVisitor() { + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + J.MethodDeclaration md = super.visitMethodDeclaration(method, ctx); + + // Check if this is a main method: public static void main(String[] args) + if (!"main".equals(md.getSimpleName()) || + md.getReturnTypeExpression() == null || + !md.getReturnTypeExpression().toString().equals("void") || + md.getParameters().size() != 1) { + return md; + } - // Check modifiers - must have public and static - boolean hasPublic = false; - boolean hasStatic = false; - for (J.Modifier modifier : md.getModifiers()) { - if (modifier.getType() == J.Modifier.Type.Public) { - hasPublic = true; - } else if (modifier.getType() == J.Modifier.Type.Static) { - hasStatic = true; + // Check modifiers - must have public and static + boolean hasPublic = false; + boolean hasStatic = false; + for (J.Modifier modifier : md.getModifiers()) { + if (modifier.getType() == J.Modifier.Type.Public) { + hasPublic = true; + } else if (modifier.getType() == J.Modifier.Type.Static) { + hasStatic = true; + } } - } - if (!hasPublic || !hasStatic) { - return md; - } + if (!hasPublic || !hasStatic) { + return md; + } - // Check parameter type - if (!(md.getParameters().get(0) instanceof J.VariableDeclarations)) { - return md; - } + // Check parameter type + if (!(md.getParameters().get(0) instanceof J.VariableDeclarations)) { + return md; + } - J.VariableDeclarations param = (J.VariableDeclarations) md.getParameters().get(0); - if (param.getVariables().isEmpty()) { - return md; - } + J.VariableDeclarations param = (J.VariableDeclarations) md.getParameters().get(0); + if (param.getVariables().isEmpty()) { + return md; + } - J.VariableDeclarations.NamedVariable paramVar = param.getVariables().get(0); - String paramName = paramVar.getSimpleName(); + J.VariableDeclarations.NamedVariable paramVar = param.getVariables().get(0); + String paramName = paramVar.getSimpleName(); - // Check if parameter is String[] type - JavaType paramType = param.getType(); - if (paramType == null || !TypeUtils.isOfClassType(paramType, "java.lang.String")) { - return md; - } + // Check if parameter is String[] type + JavaType paramType = param.getType(); + if (paramType == null || !TypeUtils.isOfClassType(paramType, "java.lang.String")) { + return md; + } - // Ensure it's an array - if (!(paramType instanceof JavaType.Array)) { - return md; - } + // Ensure it's an array + if (!(paramType instanceof JavaType.Array)) { + return md; + } - // Check if the parameter is used in the method body - if (md.getBody() == null || isParameterUsed(md.getBody(), paramName)) { - return md; - } + // Check if the parameter is used in the method body + if (md.getBody() == null || isParameterUsed(md.getBody(), paramName)) { + return md; + } - // Remove public and static modifiers, preserve spacing - List newModifiers = new ArrayList<>(); - Space leadingSpace = null; - - for (int i = 0; i < md.getModifiers().size(); i++) { - J.Modifier modifier = md.getModifiers().get(i); - if (modifier.getType() != J.Modifier.Type.Public && - modifier.getType() != J.Modifier.Type.Static) { - if (!newModifiers.isEmpty() || leadingSpace == null) { - newModifiers.add(modifier); - } else { - // Apply the leading space to the first remaining modifier - newModifiers.add(modifier.withPrefix(leadingSpace)); + // Remove public and static modifiers, preserve spacing + List newModifiers = new ArrayList<>(); + Space leadingSpace = null; + + for (int i = 0; i < md.getModifiers().size(); i++) { + J.Modifier modifier = md.getModifiers().get(i); + if (modifier.getType() != J.Modifier.Type.Public && + modifier.getType() != J.Modifier.Type.Static) { + if (!newModifiers.isEmpty() || leadingSpace == null) { + newModifiers.add(modifier); + } else { + // Apply the leading space to the first remaining modifier + newModifiers.add(modifier.withPrefix(leadingSpace)); + } + } else if (leadingSpace == null) { + // Capture the leading space from the first public/static modifier + leadingSpace = modifier.getPrefix(); } - } else if (leadingSpace == null) { - // Capture the leading space from the first public/static modifier - leadingSpace = modifier.getPrefix(); } - } - // If no modifiers remain and we have a return type, preserve the spacing - if (newModifiers.isEmpty() && leadingSpace != null && md.getReturnTypeExpression() != null) { - md = md.withReturnTypeExpression(md.getReturnTypeExpression().withPrefix(leadingSpace)); + // If no modifiers remain and we have a return type, preserve the spacing + if (newModifiers.isEmpty() && leadingSpace != null && md.getReturnTypeExpression() != null) { + md = md.withReturnTypeExpression(md.getReturnTypeExpression().withPrefix(leadingSpace)); + } + + // Remove the parameter + md = md.withModifiers(newModifiers) + .withParameters(new ArrayList<>()); + + return md; } - // Remove the parameter - md = md.withModifiers(newModifiers) - .withParameters(new ArrayList<>()); - - return md; - } - - private boolean isParameterUsed(J.Block body, String paramName) { - AtomicBoolean used = new AtomicBoolean(false); - new JavaIsoVisitor() { - @Override - public J.Identifier visitIdentifier(J.Identifier identifier, ExecutionContext ctx) { - if (paramName.equals(identifier.getSimpleName())) { - // Check if this identifier is a variable declaration (not a usage) - J.VariableDeclarations.NamedVariable namedVar = getCursor().firstEnclosing(J.VariableDeclarations.NamedVariable.class); - if (namedVar == null || !paramName.equals(namedVar.getSimpleName())) { - used.set(true); + private boolean isParameterUsed(J.Block body, String paramName) { + AtomicBoolean used = new AtomicBoolean(false); + new JavaIsoVisitor() { + @Override + public J.Identifier visitIdentifier(J.Identifier identifier, ExecutionContext ctx) { + if (paramName.equals(identifier.getSimpleName())) { + // Check if this identifier is a variable declaration (not a usage) + J.VariableDeclarations.NamedVariable namedVar = getCursor().firstEnclosing(J.VariableDeclarations.NamedVariable.class); + if (namedVar == null || !paramName.equals(namedVar.getSimpleName())) { + used.set(true); + } } + return super.visitIdentifier(identifier, ctx); } - return super.visitIdentifier(identifier, ctx); - } - }.visit(body, new InMemoryExecutionContext()); - return used.get(); - } + }.visit(body, new InMemoryExecutionContext()); + return used.get(); + } + }); } + } From 54bd049b488f3613825ac1c4025a7e6af9bd9b27 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 16 Sep 2025 20:58:49 +0200 Subject: [PATCH 4/8] Adopt `VariableReferences.findRhsReferences` --- .../lang/MigrateMainMethodToInstanceMain.java | 44 +++++++++++-------- .../MigrateMainMethodToInstanceMainTest.java | 24 +++++++++- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java index a57ae7c6f6..a1d31709ad 100644 --- a/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java +++ b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java @@ -15,15 +15,21 @@ */ package org.openrewrite.java.migrate.lang; -import org.openrewrite.*; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.search.SemanticallyEqual; import org.openrewrite.java.search.UsesJavaVersion; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.Space; import org.openrewrite.java.tree.TypeUtils; +import org.openrewrite.staticanalysis.VariableReferences; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -93,7 +99,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex } // Check if the parameter is used in the method body - if (md.getBody() == null || isParameterUsed(md.getBody(), paramName)) { + if (md.getBody() == null) { return md; } @@ -123,28 +129,30 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex } // Remove the parameter - md = md.withModifiers(newModifiers) - .withParameters(new ArrayList<>()); + if (argumentsUnused(paramVar, md.getBody())) { + md = md.withParameters(Collections.emptyList()); + } + + // Remove the modifiers + return md.withModifiers(newModifiers); + } - return md; + private boolean argumentsUnused(J.VariableDeclarations.NamedVariable variable, J context) { + return VariableReferences.findRhsReferences(context, variable.getName()).isEmpty() && + !usedInModifyingUnary(variable.getName(), context); } - private boolean isParameterUsed(J.Block body, String paramName) { - AtomicBoolean used = new AtomicBoolean(false); - new JavaIsoVisitor() { + private boolean usedInModifyingUnary(J.Identifier identifier, J context) { + return new JavaIsoVisitor() { @Override - public J.Identifier visitIdentifier(J.Identifier identifier, ExecutionContext ctx) { - if (paramName.equals(identifier.getSimpleName())) { - // Check if this identifier is a variable declaration (not a usage) - J.VariableDeclarations.NamedVariable namedVar = getCursor().firstEnclosing(J.VariableDeclarations.NamedVariable.class); - if (namedVar == null || !paramName.equals(namedVar.getSimpleName())) { - used.set(true); - } + public J.Unary visitUnary(J.Unary unary, AtomicBoolean atomicBoolean) { + if (unary.getOperator().isModifying() && + SemanticallyEqual.areEqual(identifier, unary.getExpression())) { + atomicBoolean.set(true); } - return super.visitIdentifier(identifier, ctx); + return super.visitUnary(unary, atomicBoolean); } - }.visit(body, new InMemoryExecutionContext()); - return used.get(); + }.reduce(context, new AtomicBoolean(false)).get(); } }); } diff --git a/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java b/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java index f129a5909a..b8428709bb 100644 --- a/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java +++ b/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java @@ -56,7 +56,7 @@ void main() { } @Test - void doNotMigrateWhenArgsIsUsed() { + void retainArgsWhenUsed() { //language=java rewriteRun( java( @@ -68,13 +68,22 @@ public static void main(String[] args) { } } } + """, + """ + class Application { + void main(String[] args) { + if (args.length > 0) { + System.out.println("Args provided: " + args[0]); + } + } + } """ ) ); } @Test - void doNotMigrateWhenArgsIsUsedInMethodCall() { + void retainArgsWhenUsedInMethodCall() { //language=java rewriteRun( java( @@ -88,6 +97,17 @@ private static void processArgs(String[] args) { // Process arguments } } + """, + """ + class Application { + void main(String[] args) { + processArgs(args); + } + + private static void processArgs(String[] args) { + // Process arguments + } + } """ ) ); From b67a21929ed3e21519964d81cf70aaec8699b2f3 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 16 Sep 2025 21:02:35 +0200 Subject: [PATCH 5/8] Use ListUtils.filter instead of manual ArrayList construction Refactored the modifier filtering logic to use OpenRewrite's ListUtils.filter and ListUtils.mapFirst utilities for cleaner and more idiomatic code. --- .../lang/MigrateMainMethodToInstanceMain.java | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java index a1d31709ad..be494b6f2c 100644 --- a/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java +++ b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java @@ -19,6 +19,7 @@ import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; +import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.search.SemanticallyEqual; import org.openrewrite.java.search.UsesJavaVersion; @@ -28,11 +29,11 @@ import org.openrewrite.java.tree.TypeUtils; import org.openrewrite.staticanalysis.VariableReferences; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import static java.util.Collections.emptyList; + public class MigrateMainMethodToInstanceMain extends Recipe { @Override public String getDisplayName() { @@ -103,24 +104,16 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex return md; } - // Remove public and static modifiers, preserve spacing - List newModifiers = new ArrayList<>(); - Space leadingSpace = null; - - for (int i = 0; i < md.getModifiers().size(); i++) { - J.Modifier modifier = md.getModifiers().get(i); - if (modifier.getType() != J.Modifier.Type.Public && - modifier.getType() != J.Modifier.Type.Static) { - if (!newModifiers.isEmpty() || leadingSpace == null) { - newModifiers.add(modifier); - } else { - // Apply the leading space to the first remaining modifier - newModifiers.add(modifier.withPrefix(leadingSpace)); - } - } else if (leadingSpace == null) { - // Capture the leading space from the first public/static modifier - leadingSpace = modifier.getPrefix(); - } + // Remove public and static modifiers using ListUtils.filter, preserve spacing + Space leadingSpace = md.getModifiers().isEmpty() ? null : md.getModifiers().get(0).getPrefix(); + + List newModifiers = ListUtils.filter(md.getModifiers(), + modifier -> modifier.getType() != J.Modifier.Type.Public && + modifier.getType() != J.Modifier.Type.Static); + + // If we removed modifiers and have remaining ones, fix the leading space on the first + if (!newModifiers.isEmpty() && newModifiers.size() < md.getModifiers().size()) { + newModifiers = ListUtils.mapFirst(newModifiers, m -> m.withPrefix(leadingSpace)); } // If no modifiers remain and we have a return type, preserve the spacing @@ -130,7 +123,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex // Remove the parameter if (argumentsUnused(paramVar, md.getBody())) { - md = md.withParameters(Collections.emptyList()); + md = md.withParameters(emptyList()); } // Remove the modifiers From 07fe693f5f5a2281758aaf25b60202ffc4d767e4 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 16 Sep 2025 21:25:18 +0200 Subject: [PATCH 6/8] Condense --- .../lang/MigrateMainMethodToInstanceMain.java | 88 ++++--------------- 1 file changed, 17 insertions(+), 71 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java index be494b6f2c..1d71fb734b 100644 --- a/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java +++ b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java @@ -19,17 +19,14 @@ import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.search.SemanticallyEqual; import org.openrewrite.java.search.UsesJavaVersion; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; -import org.openrewrite.java.tree.Space; import org.openrewrite.java.tree.TypeUtils; import org.openrewrite.staticanalysis.VariableReferences; -import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import static java.util.Collections.emptyList; @@ -55,92 +52,41 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex // Check if this is a main method: public static void main(String[] args) if (!"main".equals(md.getSimpleName()) || md.getReturnTypeExpression() == null || - !md.getReturnTypeExpression().toString().equals("void") || - md.getParameters().size() != 1) { + md.getReturnTypeExpression().getType() != JavaType.Primitive.Void || + !md.hasModifier(J.Modifier.Type.Public) || + !md.hasModifier(J.Modifier.Type.Static) || + md.getParameters().size() != 1 || + !(md.getParameters().get(0) instanceof J.VariableDeclarations) || + md.getBody() == null) { return md; } - // Check modifiers - must have public and static - boolean hasPublic = false; - boolean hasStatic = false; - for (J.Modifier modifier : md.getModifiers()) { - if (modifier.getType() == J.Modifier.Type.Public) { - hasPublic = true; - } else if (modifier.getType() == J.Modifier.Type.Static) { - hasStatic = true; - } - } - - if (!hasPublic || !hasStatic) { - return md; - } - - // Check parameter type - if (!(md.getParameters().get(0) instanceof J.VariableDeclarations)) { - return md; - } - - J.VariableDeclarations param = (J.VariableDeclarations) md.getParameters().get(0); - if (param.getVariables().isEmpty()) { - return md; - } - - J.VariableDeclarations.NamedVariable paramVar = param.getVariables().get(0); - String paramName = paramVar.getSimpleName(); - // Check if parameter is String[] type + J.VariableDeclarations param = (J.VariableDeclarations) md.getParameters().get(0); JavaType paramType = param.getType(); - if (paramType == null || !TypeUtils.isOfClassType(paramType, "java.lang.String")) { - return md; - } - - // Ensure it's an array - if (!(paramType instanceof JavaType.Array)) { - return md; - } - - // Check if the parameter is used in the method body - if (md.getBody() == null) { + if (!TypeUtils.isOfClassType(paramType, "java.lang.String") || !(paramType instanceof JavaType.Array)) { return md; } - // Remove public and static modifiers using ListUtils.filter, preserve spacing - Space leadingSpace = md.getModifiers().isEmpty() ? null : md.getModifiers().get(0).getPrefix(); - - List newModifiers = ListUtils.filter(md.getModifiers(), - modifier -> modifier.getType() != J.Modifier.Type.Public && - modifier.getType() != J.Modifier.Type.Static); - - // If we removed modifiers and have remaining ones, fix the leading space on the first - if (!newModifiers.isEmpty() && newModifiers.size() < md.getModifiers().size()) { - newModifiers = ListUtils.mapFirst(newModifiers, m -> m.withPrefix(leadingSpace)); - } - - // If no modifiers remain and we have a return type, preserve the spacing - if (newModifiers.isEmpty() && leadingSpace != null && md.getReturnTypeExpression() != null) { - md = md.withReturnTypeExpression(md.getReturnTypeExpression().withPrefix(leadingSpace)); - } - - // Remove the parameter - if (argumentsUnused(paramVar, md.getBody())) { + // Remove the parameter if unused + if (argumentsUnused(param.getVariables().get(0).getName(), md.getBody())) { md = md.withParameters(emptyList()); } - - // Remove the modifiers - return md.withModifiers(newModifiers); + return md.withReturnTypeExpression(md.getReturnTypeExpression().withPrefix(md.getModifiers().get(0).getPrefix())) + .withModifiers(emptyList()); } - private boolean argumentsUnused(J.VariableDeclarations.NamedVariable variable, J context) { - return VariableReferences.findRhsReferences(context, variable.getName()).isEmpty() && - !usedInModifyingUnary(variable.getName(), context); + private boolean argumentsUnused(J.Identifier variableName, J context) { + return VariableReferences.findRhsReferences(context, variableName).isEmpty() && + !usedInModifyingUnary(variableName, context); } - private boolean usedInModifyingUnary(J.Identifier identifier, J context) { + private boolean usedInModifyingUnary(J.Identifier variableName, J context) { return new JavaIsoVisitor() { @Override public J.Unary visitUnary(J.Unary unary, AtomicBoolean atomicBoolean) { if (unary.getOperator().isModifying() && - SemanticallyEqual.areEqual(identifier, unary.getExpression())) { + SemanticallyEqual.areEqual(variableName, unary.getExpression())) { atomicBoolean.set(true); } return super.visitUnary(unary, atomicBoolean); From 49f5396084375d05ca295b7315cb2b1386713b34 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 16 Sep 2025 21:27:42 +0200 Subject: [PATCH 7/8] Suppress warnings for confusing main methods --- .../java/migrate/lang/MigrateMainMethodToInstanceMainTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java b/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java index b8428709bb..9ceb82ab0c 100644 --- a/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java +++ b/src/test/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMainTest.java @@ -23,6 +23,7 @@ import static org.openrewrite.java.Assertions.java; import static org.openrewrite.java.Assertions.javaVersion; +@SuppressWarnings("ConfusingMainMethod") class MigrateMainMethodToInstanceMainTest implements RewriteTest { @Override @@ -238,7 +239,7 @@ void doNotMigrateMainWithMultipleParameters() { java( """ class Application { - public static void main(String[] args, String extra) { + public static void main(String first, String[] args) { System.out.println("Invalid main method"); } } From 31550bc8cc3933077ea6733641ecc5b827d48456 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 16 Sep 2025 21:29:01 +0200 Subject: [PATCH 8/8] Further simplifications for String arrays --- .../lang/MigrateMainMethodToInstanceMain.java | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java index 1d71fb734b..1d0b684c03 100644 --- a/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java +++ b/src/main/java/org/openrewrite/java/migrate/lang/MigrateMainMethodToInstanceMain.java @@ -20,15 +20,12 @@ import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.search.SemanticallyEqual; import org.openrewrite.java.search.UsesJavaVersion; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.TypeUtils; import org.openrewrite.staticanalysis.VariableReferences; -import java.util.concurrent.atomic.AtomicBoolean; - import static java.util.Collections.emptyList; public class MigrateMainMethodToInstanceMain extends Recipe { @@ -69,30 +66,13 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex } // Remove the parameter if unused - if (argumentsUnused(param.getVariables().get(0).getName(), md.getBody())) { + J.Identifier variableName = param.getVariables().get(0).getName(); + if (VariableReferences.findRhsReferences(md.getBody(), variableName).isEmpty()) { md = md.withParameters(emptyList()); } return md.withReturnTypeExpression(md.getReturnTypeExpression().withPrefix(md.getModifiers().get(0).getPrefix())) .withModifiers(emptyList()); } - - private boolean argumentsUnused(J.Identifier variableName, J context) { - return VariableReferences.findRhsReferences(context, variableName).isEmpty() && - !usedInModifyingUnary(variableName, context); - } - - private boolean usedInModifyingUnary(J.Identifier variableName, J context) { - return new JavaIsoVisitor() { - @Override - public J.Unary visitUnary(J.Unary unary, AtomicBoolean atomicBoolean) { - if (unary.getOperator().isModifying() && - SemanticallyEqual.areEqual(variableName, unary.getExpression())) { - atomicBoolean.set(true); - } - return super.visitUnary(unary, atomicBoolean); - } - }.reduce(context, new AtomicBoolean(false)).get(); - } }); }