From ab38eaaaa77ad312b81d73293d625b3715ebf217 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 29 Jun 2026 13:44:16 +0200 Subject: [PATCH] Fix RemoveRedundantTypeCast removing cast that gives a var lambda its target type The lambda/member-reference guard only matched when the cast operand was the direct expression. When the operand is wrapped in parentheses (e.g. `(Function) (i -> ...)`), the operand is a J.ControlParentheses/J.Parentheses, so the guard missed it and the cast was removed. For a `var` local the cast is the sole source of the target type, so removal breaks compilation. Unwrap parentheses before checking for a lambda or method reference. Fixes #924 --- .../RemoveRedundantTypeCast.java | 10 ++++- .../RemoveRedundantTypeCastTest.java | 45 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java index 2865a8d6a..f19fbeab1 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java @@ -160,8 +160,16 @@ public J visitTypeCast(J.TypeCast typeCast, ExecutionContext ctx) { if ((targetType instanceof JavaType.Primitive || castType instanceof JavaType.Primitive) && castType != expressionType) { return visitedTypeCast; } - if (typeCast.getExpression() instanceof J.Lambda || typeCast.getExpression() instanceof J.MemberReference) { + J castExpression = typeCast.getExpression(); + while (castExpression instanceof J.Parentheses || castExpression instanceof J.ControlParentheses) { + castExpression = castExpression instanceof J.Parentheses ? + ((J.Parentheses) castExpression).getTree() : + ((J.ControlParentheses) castExpression).getTree(); + } + if (castExpression instanceof J.Lambda || castExpression instanceof J.MemberReference) { // Not currently supported, this will be more accurate with dataflow analysis. + // The cast supplies the target type for the lambda or method reference; removing it + // can break compilation, e.g. when assigned to a `var` local. return visitedTypeCast; } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java index 52b3938e4..08b8513a5 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java @@ -23,6 +23,7 @@ import org.openrewrite.test.RewriteTest; import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.java.Assertions.javaVersion; @SuppressWarnings("ALL") class RemoveRedundantTypeCastTest implements RewriteTest { @@ -489,6 +490,50 @@ public MapDropdownChoice(Supplier> choiceMap) { ); } + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/924") + void keepCastForLambdaAssignedToVar() { + rewriteRun( + //language=java + java( + """ + import java.util.function.Function; + + class Example { + void run() { + var converter = (Function) (i -> Integer.toString(i)); + } + } + """, + spec -> spec.markers(javaVersion(25)) + ) + ); + } + + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/924") + void keepCastForMethodReferenceAssignedToVar() { + rewriteRun( + //language=java + java( + """ + import java.util.function.Supplier; + + class Example { + static String make() { + return ""; + } + + void run() { + var supplier = (Supplier) Example::make; + } + } + """, + spec -> spec.markers(javaVersion(25)) + ) + ); + } + @Test void returnPrimitiveIntToWrapperLong() { rewriteRun(