From 3b967901248d5049072ffbe19ba9fe86a35fa3c0 Mon Sep 17 00:00:00 2001 From: sullis Date: Wed, 2 Sep 2026 16:31:26 -0400 Subject: [PATCH] Preserve casts when converting `lombok.val` to `final var` --- .../migrate/lombok/LombokValToFinalVar.java | 43 ++++++++------- .../java/migrate/UpgradeToJava25Test.java | 2 +- .../lombok/LombokValToFinalVarTest.java | 54 +++++++++++++++++++ 3 files changed, 80 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokValToFinalVar.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokValToFinalVar.java index 2ba6a51c4b..821f9c2f94 100644 --- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokValToFinalVar.java +++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokValToFinalVar.java @@ -19,17 +19,19 @@ import org.openrewrite.*; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.search.MaybeUsesImport; import org.openrewrite.java.search.UsesType; import org.openrewrite.java.service.AnnotationService; import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.Space; import org.openrewrite.java.tree.TypeTree; import org.openrewrite.java.tree.TypeUtils; +import org.openrewrite.marker.Markers; import java.time.Duration; import java.util.Set; +import static java.util.Collections.emptyList; import static java.util.Collections.singleton; public class LombokValToFinalVar extends Recipe { @@ -79,27 +81,32 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations m (varDecls.getTypeExpression() instanceof J.Identifier && "val".equals(((J.Identifier) varDecls.getTypeExpression()).getSimpleName()))) { maybeRemoveImport(LOMBOK_VAL); - J.VariableDeclarations.NamedVariable nv = mv.getVariables().get(0); + // Manually transform to `var`, rather than using a JavaTemplate, so that the initializer + // is carried over untouched; reprinting it can lose type information, such as the type + // variable in a cast like `(T) o`. + J.VariableDeclarations.NamedVariable nv = varDecls.getVariables().get(0); + TypeTree typeExpression = varDecls.getTypeExpression(); + J.Identifier varType = new J.Identifier(Tree.randomId(), + typeExpression.getPrefix(), + typeExpression.getMarkers(), + service(AnnotationService.class).getAllAnnotations(getCursor()), + "var", + nv.getType(), + null); if (nv.getInitializer() == null) { - // manually transform to var, as val in this case has no sufficient type information - // and the java template parsing would fail, see https://github.com/openrewrite/rewrite/pull/5637 - TypeTree typeExpression = varDecls.getTypeExpression(); - J.Identifier varType = new J.Identifier(Tree.randomId(), - typeExpression.getPrefix(), - typeExpression.getMarkers(), - service(AnnotationService.class).getAllAnnotations(getCursor()), - "var", - nv.getType(), - null); + // `val` in this case has no sufficient type information, so it becomes a plain `var`, + // see https://github.com/openrewrite/rewrite/pull/5637 return varDecls.withTypeExpression(varType); } - varDecls = JavaTemplate.builder("final var #{} = #{any()};") - .contextSensitive() - .build() - .apply(updateCursor(varDecls), varDecls.getCoordinates().replace(), nv.getSimpleName(), nv.getInitializer()); - varDecls = varDecls.withVariables(ListUtils.map(varDecls.getVariables(), namedVar -> namedVar - .withInitializer(namedVar.getInitializer().withPrefix(nv.getInitializer().getPrefix())))); + if (varDecls.getModifiers().stream().noneMatch(m -> m.getType() == J.Modifier.Type.Final)) { + varDecls = varDecls.withModifiers(ListUtils.concat( + new J.Modifier(Tree.randomId(), typeExpression.getPrefix(), Markers.EMPTY, + null, J.Modifier.Type.Final, emptyList()), + varDecls.getModifiers())); + varType = varType.withPrefix(Space.SINGLE_SPACE); + } + varDecls = varDecls.withTypeExpression(varType); } return varDecls; } diff --git a/src/test/java/org/openrewrite/java/migrate/UpgradeToJava25Test.java b/src/test/java/org/openrewrite/java/migrate/UpgradeToJava25Test.java index c48df08575..6b7fd95cd0 100644 --- a/src/test/java/org/openrewrite/java/migrate/UpgradeToJava25Test.java +++ b/src/test/java/org/openrewrite/java/migrate/UpgradeToJava25Test.java @@ -115,7 +115,7 @@ void upgradesMavenPluginsForJava25() { spec -> spec.after(actual -> assertThat(actual) .contains("25") - .containsPattern("maven-compiler-plugin\\s*3\\.15\\.") + .containsPattern("maven-compiler-plugin\\s*3\\.(1[5-9]|[2-9]\\d)\\.") .containsPattern("maven-surefire-plugin\\s*3\\.5\\.") .containsPattern("maven-failsafe-plugin\\s*3\\.5\\.") .contains("-javaagent:${org.mockito:mockito-core:jar}") diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/LombokValToFinalVarTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/LombokValToFinalVarTest.java index bd4c93226f..2a215d0316 100755 --- a/src/test/java/org/openrewrite/java/migrate/lombok/LombokValToFinalVarTest.java +++ b/src/test/java/org/openrewrite/java/migrate/lombok/LombokValToFinalVarTest.java @@ -511,4 +511,58 @@ void bar() { ) ); } + + @Test + void preserveCast() { + //language=java + rewriteRun( + version( + java( + """ + import lombok.val; + class CastDemo { + void bar() { + val foo = (String) "foo"; + } + } + """, + """ + class CastDemo { + void bar() { + final var foo = (String) "foo"; + } + } + """ + ), + 17 + ) + ); + } + + @Test + void preserveGenericCast() { + //language=java + rewriteRun( + version( + java( + """ + import lombok.val; + class GenericCastDemo { + void bar(Object o) { + val foo = (T) o; + } + } + """, + """ + class GenericCastDemo { + void bar(Object o) { + final var foo = (T) o; + } + } + """ + ), + 17 + ) + ); + } }