diff --git a/rewrite-java/src/main/java/org/openrewrite/java/RelocateSuperCall.java b/rewrite-java/src/main/java/org/openrewrite/java/RelocateSuperCall.java new file mode 100644 index 0000000000..c45aec1527 --- /dev/null +++ b/rewrite-java/src/main/java/org/openrewrite/java/RelocateSuperCall.java @@ -0,0 +1,66 @@ +package org.openrewrite.java; + + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.search.UsesJavaVersion; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.Statement; + +import java.util.List; +@EqualsAndHashCode(callSuper = false) +@Value +public class RelocateSuperCall extends Recipe { + + @Override + public String getDisplayName() { + return "Move `super()` after conditionals (Java 25+)"; + } + + @Override + public String getDescription() { + return "Relocates `super()` calls to occur after conditionals inside constructors, enabled by JEP 513 in Java 25+."; + } + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check( + new UsesJavaVersion<>(25), + new RelocateSuperCallVisitor()); + } + + private static class RelocateSuperCallVisitor extends JavaIsoVisitor { + + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + if (!method.isConstructor() || method.getBody() == null) { + return method; + } + + List statements = method.getBody().getStatements(); + if (statements.size() < 2) { + return method; + } + + Statement first = statements.get(0); + if (!(first instanceof J.MethodInvocation)) { + return method; + } + J.MethodInvocation methodInvocation = (J.MethodInvocation) first; + if (!"super".equals(methodInvocation.getSimpleName())) { + return method; + } + + // Move super() to the end + List updated = new java.util.ArrayList<>(statements); + updated.remove(0); + updated.add(methodInvocation); + + return method.withBody(method.getBody().withStatements(updated)); + } + } +} diff --git a/rewrite-java/src/test/java/org/openrewrite/java/RelocateSuperCallTest.java b/rewrite-java/src/test/java/org/openrewrite/java/RelocateSuperCallTest.java new file mode 100644 index 0000000000..48b9960a61 --- /dev/null +++ b/rewrite-java/src/test/java/org/openrewrite/java/RelocateSuperCallTest.java @@ -0,0 +1,113 @@ +package org.openrewrite.java; + +import org.junit.jupiter.api.Test; +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 RelocateSuperCallTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new RelocateSuperCall()) + .allSources(src -> src.markers(javaVersion(25))); + + } + @Test + void moveSuperAfterIf() { + rewriteRun( + java( + """ + class A { + public A(String bar) { + super(); + if(bar.equals("test")) + throw new RuntimeException(); + } + } + """, + """ + class A { + public A(String bar) { + if(bar.equals("test")) + throw new RuntimeException(); + super(); + } + } + """ + ) + ); + } + + @Test + void moveSuperAfterIfStatement() { + rewriteRun( + java( + // language=java + """ + class Person { + final int age; + public Person(int age) { + if (age < 0) { + throw new IllegalArgumentException("Invalid age"); + } + this.age = age; + } + } + + class Employee extends Person { + public Employee(int age) { + super(age); + if (age < 18 || age > 67) { + throw new IllegalArgumentException("Invalid employee age"); + } + } + } + """, + // Expected output + """ + class Person { + final int age; + public Person(int age) { + if (age < 0) { + throw new IllegalArgumentException("Invalid age"); + } + this.age = age; + } + } + + class Employee extends Person { + public Employee(int age) { + if (age < 18 || age > 67) { + throw new IllegalArgumentException("Invalid employee age"); + } + super(age); + } + } + """ + ) + ); + } + + @Test + void moveSuperAfterIf_withJdkBelow25Version() { + rewriteRun( + spec -> spec.recipe(new RelocateSuperCall()) + .allSources(src -> src.markers(javaVersion(8))), + java( + """ + class A { + public A(String bar) { + super(); + if(bar.equals("test")) + throw new RuntimeException(); + } + } + """, + spec -> spec.markers(javaVersion(8)) + ) + ); + } +}