Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* 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
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* 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.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.TypeUtils;
import org.openrewrite.staticanalysis.VariableReferences;

import static java.util.Collections.emptyList;

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 25+.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(new UsesJavaVersion<>(25), new JavaIsoVisitor<ExecutionContext>() {
@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().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 if parameter is String[] type
J.VariableDeclarations param = (J.VariableDeclarations) md.getParameters().get(0);
JavaType paramType = param.getType();
if (!TypeUtils.isOfClassType(paramType, "java.lang.String") || !(paramType instanceof JavaType.Array)) {
return md;
}

// Remove the parameter if unused
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());
}
});
}

}
1 change: 1 addition & 0 deletions src/main/resources/META-INF/rewrite/java-version-25.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* 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
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* 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;

@SuppressWarnings("ConfusingMainMethod")
class MigrateMainMethodToInstanceMainTest implements RewriteTest {

@Override
public void defaults(RecipeSpec spec) {
spec.recipe(new MigrateMainMethodToInstanceMain())
.allSources(s -> s.markers(javaVersion(25)));
}

@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 retainArgsWhenUsed() {
//language=java
rewriteRun(
java(
"""
class Application {
public static void main(String[] args) {
if (args.length > 0) {
System.out.println("Args provided: " + args[0]);
}
}
}
""",
"""
class Application {
void main(String[] args) {
if (args.length > 0) {
System.out.println("Args provided: " + args[0]);
}
}
}
"""
)
);
}

@Test
void retainArgsWhenUsedInMethodCall() {
//language=java
rewriteRun(
java(
"""
class Application {
public static void main(String[] args) {
processArgs(args);
}

private static void processArgs(String[] args) {
// Process arguments
}
}
""",
"""
class Application {
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 first, String[] args) {
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!");
}
}
"""
)
);
}
}