From ceaccfa61324c4d89ca352f18fe9372d6c0baea4 Mon Sep 17 00:00:00 2001 From: lingenj Date: Fri, 15 Aug 2025 10:49:30 +0200 Subject: [PATCH 01/10] Add exact equals type check for RemoveRedundantTypeCast --- .../RemoveRedundantTypeCast.java | 29 +++++++++--- .../RemoveRedundantTypeCastTest.java | 44 +++++++++++++++++++ 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java index ceb2a5d4b2..9f0f6b50f7 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java @@ -28,6 +28,8 @@ @Incubating(since = "7.23.0") public class RemoveRedundantTypeCast extends Recipe { + private static final String REMOVE_UNNECESSARY_PARENTHESES = "removeUnnecessaryParentheses"; + @Override public String getDisplayName() { return "Remove redundant casts"; @@ -35,7 +37,7 @@ public String getDisplayName() { @Override public String getDescription() { - return "Removes unnecessary type casts. Does not currently check casts in lambdas, class constructors, and method invocations."; + return "Removes unnecessary type casts. Does not currently check casts in lambdas and class constructors."; } @Override @@ -68,8 +70,14 @@ public J visitTypeCast(J.TypeCast typeCast, ExecutionContext ctx) { J parentValue = parent.getValue(); + J.TypeCast visitedTypeCast = (J.TypeCast) visited; + JavaType expressionType = visitedTypeCast.getExpression().getType(); + JavaType castType = visitedTypeCast.getType(); + JavaType targetType = null; - if (parentValue instanceof J.VariableDeclarations) { + if (castType.equals(expressionType)) { + targetType = castType; + } else if (parentValue instanceof J.VariableDeclarations) { targetType = ((J.VariableDeclarations) parentValue).getVariables().get(0).getType(); } else if (parentValue instanceof MethodCall) { MethodCall methodCall = (MethodCall) parentValue; @@ -98,10 +106,6 @@ public J visitTypeCast(J.TypeCast typeCast, ExecutionContext ctx) { } } - J.TypeCast visitedTypeCast = (J.TypeCast) visited; - JavaType expressionType = visitedTypeCast.getExpression().getType(); - JavaType castType = visitedTypeCast.getType(); - if (targetType == null) { return visitedTypeCast; } @@ -135,11 +139,24 @@ public J visitTypeCast(J.TypeCast typeCast, ExecutionContext ctx) { if (fullyQualified != null) { maybeRemoveImport(fullyQualified.getFullyQualifiedName()); } + Cursor directParent = getCursor().getParent(); + if (directParent != null && directParent.getParent() != null && directParent.getParent().getValue() instanceof J.Parentheses) { + directParent.getParent().putMessage(REMOVE_UNNECESSARY_PARENTHESES, true); + } return visitedTypeCast.getExpression().withPrefix(visitedTypeCast.getPrefix()); } return visitedTypeCast; } + @Override + public J visitParentheses(J.Parentheses parens, ExecutionContext executionContext) { + J.Parentheses parentheses = (J.Parentheses) super.visitParentheses(parens, executionContext); + if (getCursor().getMessage(REMOVE_UNNECESSARY_PARENTHESES, false)) { + return parentheses.getTree(); + } + return parentheses; + } + private JavaType getParameterType(JavaType.Method method, int arg) { List parameterTypes = method.getParameterTypes(); if (parameterTypes.size() > arg) { diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java index 87c69c0218..fecf219235 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java @@ -556,4 +556,48 @@ public Bar baz() { ) ); } + + @Test + void chainedMethods() { + // language=java + rewriteRun( + java( + """ + class Bar { + String getName() { + return "The bar"; + } + } + """ + ), + java( + """ + class Foo { + public void getBarName() { + String.format(((Bar) getBar()).getName()); + ((Bar) getBar()).getName(); + (((Bar) getBar())).getName(); + } + + private Bar getBar() { + return new Bar(); + } + } + """, + """ + class Foo { + public void getBarName() { + String.format(getBar().getName()); + getBar().getName(); + (getBar()).getName(); + } + + private Bar getBar() { + return new Bar(); + } + } + """ + ) + ); + } } From ef34e2a64f5fd3e75f66ab747a04ce4a90591023 Mon Sep 17 00:00:00 2001 From: Jacob van Lingen Date: Fri, 15 Aug 2025 10:54:04 +0200 Subject: [PATCH 02/10] Apply suggestion from @github-actions[bot] Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../openrewrite/staticanalysis/RemoveRedundantTypeCast.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java index 9f0f6b50f7..358424e3ac 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java @@ -149,8 +149,8 @@ public J visitTypeCast(J.TypeCast typeCast, ExecutionContext ctx) { } @Override - public J visitParentheses(J.Parentheses parens, ExecutionContext executionContext) { - J.Parentheses parentheses = (J.Parentheses) super.visitParentheses(parens, executionContext); + public J visitParentheses(J.Parentheses parens, ExecutionContext ctx) { + J.Parentheses parentheses = (J.Parentheses) super.visitParentheses(parens, ctx); if (getCursor().getMessage(REMOVE_UNNECESSARY_PARENTHESES, false)) { return parentheses.getTree(); } From bf9515176cc09f5aff2bd00599bd471c68a0d515 Mon Sep 17 00:00:00 2001 From: lingenj Date: Fri, 15 Aug 2025 11:21:09 +0200 Subject: [PATCH 03/10] Support child classes --- .../staticanalysis/RemoveRedundantTypeCast.java | 3 +++ .../RemoveRedundantTypeCastTest.java | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java index 358424e3ac..2fa65fa172 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java @@ -95,6 +95,9 @@ public J visitTypeCast(J.TypeCast typeCast, ExecutionContext ctx) { } } } + if (TypeUtils.isAssignableTo(castType, expressionType)) { + targetType = castType; + } } else if (parentValue instanceof J.Return && ((J.Return) parentValue).getExpression() == typeCast) { parent = parent.dropParentUntil(is -> is instanceof J.Lambda || is instanceof J.MethodDeclaration || diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java index fecf219235..6ef204f972 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java @@ -568,6 +568,7 @@ String getName() { return "The bar"; } } + class ChildBar extends Bar {} """ ), java( @@ -576,12 +577,17 @@ class Foo { public void getBarName() { String.format(((Bar) getBar()).getName()); ((Bar) getBar()).getName(); - (((Bar) getBar())).getName(); + ((Bar) getChildBar()).getName(); + ((((Bar) getBar()))).getName(); } private Bar getBar() { return new Bar(); } + + private ChildBar getChildBar() { + return new ChildBar(); + } } """, """ @@ -589,12 +595,17 @@ class Foo { public void getBarName() { String.format(getBar().getName()); getBar().getName(); - (getBar()).getName(); + getChildBar().getName(); + ((getBar())).getName(); } private Bar getBar() { return new Bar(); } + + private ChildBar getChildBar() { + return new ChildBar(); + } } """ ) From 1c92cfc970708bc998b1c94f744d6bf420f7b226 Mon Sep 17 00:00:00 2001 From: lingenj Date: Fri, 15 Aug 2025 11:55:00 +0200 Subject: [PATCH 04/10] Keep prefix of Parentheses --- .../org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java index 2fa65fa172..37e695bc31 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java @@ -155,7 +155,7 @@ public J visitTypeCast(J.TypeCast typeCast, ExecutionContext ctx) { public J visitParentheses(J.Parentheses parens, ExecutionContext ctx) { J.Parentheses parentheses = (J.Parentheses) super.visitParentheses(parens, ctx); if (getCursor().getMessage(REMOVE_UNNECESSARY_PARENTHESES, false)) { - return parentheses.getTree(); + return parentheses.getTree().withPrefix(parentheses.getPrefix()); } return parentheses; } From f59a32f32f6e22c818dd484d6037c55fd426a471 Mon Sep 17 00:00:00 2001 From: lingenj Date: Fri, 15 Aug 2025 12:11:53 +0200 Subject: [PATCH 05/10] Return statement with parentheses expression should be able to remove redundant cast --- .../staticanalysis/RemoveRedundantTypeCast.java | 16 +++++++++++++++- .../RemoveRedundantTypeCastTest.java | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java index 37e695bc31..0562584e3f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java @@ -98,7 +98,7 @@ public J visitTypeCast(J.TypeCast typeCast, ExecutionContext ctx) { if (TypeUtils.isAssignableTo(castType, expressionType)) { targetType = castType; } - } else if (parentValue instanceof J.Return && ((J.Return) parentValue).getExpression() == typeCast) { + } else if (parentValue instanceof J.Return && expressionIsTypeCast((J.Return) parentValue, typeCast)) { parent = parent.dropParentUntil(is -> is instanceof J.Lambda || is instanceof J.MethodDeclaration || is instanceof J.ClassDeclaration || @@ -169,6 +169,20 @@ private JavaType getParameterType(JavaType.Method method, int arg) { JavaType type = parameterTypes.get(parameterTypes.size() - 1); return type instanceof JavaType.Array ? ((JavaType.Array) type).getElemType() : type; } + + private boolean expressionIsTypeCast(J.Return return_, J.TypeCast typeCast) { + if (return_.getExpression() instanceof J.Parentheses) { + return expressionIsTypeCast((J.Parentheses) return_.getExpression(), typeCast); + } + return return_.getExpression() == typeCast; + } + + private boolean expressionIsTypeCast(J.Parentheses parentheses, J.TypeCast typeCast) { + if (parentheses.getTree() instanceof J.Parentheses) { + return expressionIsTypeCast((J.Parentheses ) parentheses.getTree(), typeCast); + } + return parentheses.getTree() == typeCast; + } }; } } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java index 6ef204f972..95ae3ab070 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java @@ -471,7 +471,7 @@ void removeImport() { class Test { List method(List list) { - return (ArrayList) list; + return ((ArrayList) list); } } """, From 4af1a1c69cbe1c54431ef8d01d9d92beb098b0a0 Mon Sep 17 00:00:00 2001 From: lingenj Date: Fri, 15 Aug 2025 16:34:26 +0200 Subject: [PATCH 06/10] Handle Kotlin case --- .../RemoveRedundantTypeCast.java | 3 ++- .../RemoveRedundantTypeCastTest.java | 24 ++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java index 0562584e3f..9e543c236b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java @@ -75,7 +75,8 @@ public J visitTypeCast(J.TypeCast typeCast, ExecutionContext ctx) { JavaType castType = visitedTypeCast.getType(); JavaType targetType = null; - if (castType.equals(expressionType)) { + // Null-check; because for Kotlin's Gradle files, we don't always have all type information available + if (castType != null && castType.equals(expressionType)) { targetType = castType; } else if (parentValue instanceof J.VariableDeclarations) { targetType = ((J.VariableDeclarations) parentValue).getVariables().get(0).getType(); diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java index 95ae3ab070..fed73a54bf 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java @@ -22,7 +22,8 @@ import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; -import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.java.Assertions.*; +import static org.openrewrite.kotlin.Assertions.kotlin; @SuppressWarnings("ALL") class RemoveRedundantTypeCastTest implements RewriteTest { @@ -611,4 +612,25 @@ private ChildBar getChildBar() { ) ); } + + @Test + void kotlinDsl() { + rewriteRun( + //language=kotlin + kotlin( + """ + class Test { + val s2 = method() as String + fun method() = "example" + } + """, + """ + class Test { + val s2 = method() + fun method() = "example" + } + """ + ) + ); + } } From f44f32c7df38678c93a66c689446eac1e93631c6 Mon Sep 17 00:00:00 2001 From: lingenj Date: Fri, 15 Aug 2025 16:35:44 +0200 Subject: [PATCH 07/10] Handle Kotlin case --- .../org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java index 9e543c236b..b31961eecc 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java @@ -75,7 +75,7 @@ public J visitTypeCast(J.TypeCast typeCast, ExecutionContext ctx) { JavaType castType = visitedTypeCast.getType(); JavaType targetType = null; - // Null-check; because for Kotlin's Gradle files, we don't always have all type information available + // Necessary null-check; because for Kotlin's Gradle files, we don't have all type information available if (castType != null && castType.equals(expressionType)) { targetType = castType; } else if (parentValue instanceof J.VariableDeclarations) { From ae58c30c1e04d26dbe0a4ca4032ce03a838f8c4d Mon Sep 17 00:00:00 2001 From: Jacob van Lingen Date: Fri, 15 Aug 2025 16:38:38 +0200 Subject: [PATCH 08/10] Update src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java index fed73a54bf..630f17bd6d 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java @@ -22,7 +22,7 @@ import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; -import static org.openrewrite.java.Assertions.*; +import static org.openrewrite.java.Assertions.java; import static org.openrewrite.kotlin.Assertions.kotlin; @SuppressWarnings("ALL") From c853c61d44c6adf2d5d371ce112f9cf23a7adb22 Mon Sep 17 00:00:00 2001 From: lingenj Date: Fri, 15 Aug 2025 17:29:37 +0200 Subject: [PATCH 09/10] Exclude Kotlin --- .../RemoveRedundantTypeCast.java | 8 +++---- .../RemoveRedundantTypeCastTest.java | 22 +------------------ 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java index b31961eecc..14471219e3 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java @@ -18,6 +18,7 @@ import org.openrewrite.*; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.tree.*; +import org.openrewrite.staticanalysis.kotlin.KotlinFileChecker; import java.time.Duration; import java.util.List; @@ -52,7 +53,7 @@ public Set getTags() { @Override public TreeVisitor getVisitor() { - return new JavaVisitor() { + return Preconditions.check(Preconditions.not(new KotlinFileChecker<>()), new JavaVisitor() { @Override public J visitTypeCast(J.TypeCast typeCast, ExecutionContext ctx) { J visited = super.visitTypeCast(typeCast, ctx); @@ -75,8 +76,7 @@ public J visitTypeCast(J.TypeCast typeCast, ExecutionContext ctx) { JavaType castType = visitedTypeCast.getType(); JavaType targetType = null; - // Necessary null-check; because for Kotlin's Gradle files, we don't have all type information available - if (castType != null && castType.equals(expressionType)) { + if (castType.equals(expressionType)) { targetType = castType; } else if (parentValue instanceof J.VariableDeclarations) { targetType = ((J.VariableDeclarations) parentValue).getVariables().get(0).getType(); @@ -184,6 +184,6 @@ private boolean expressionIsTypeCast(J.Parentheses parentheses, J.TypeCast ty } return parentheses.getTree() == typeCast; } - }; + }); } } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java index 630f17bd6d..94a166c08f 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java @@ -15,6 +15,7 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.NonNull; import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; import org.openrewrite.Issue; @@ -612,25 +613,4 @@ private ChildBar getChildBar() { ) ); } - - @Test - void kotlinDsl() { - rewriteRun( - //language=kotlin - kotlin( - """ - class Test { - val s2 = method() as String - fun method() = "example" - } - """, - """ - class Test { - val s2 = method() - fun method() = "example" - } - """ - ) - ); - } } From 165c589ed98a99b9a00b124fb025bf8363b88567 Mon Sep 17 00:00:00 2001 From: lingenj Date: Fri, 15 Aug 2025 17:45:12 +0200 Subject: [PATCH 10/10] Polish --- .../openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java index 94a166c08f..95ae3ab070 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java @@ -15,7 +15,6 @@ */ package org.openrewrite.staticanalysis; -import org.jspecify.annotations.NonNull; import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; import org.openrewrite.Issue; @@ -24,7 +23,6 @@ import org.openrewrite.test.RewriteTest; import static org.openrewrite.java.Assertions.java; -import static org.openrewrite.kotlin.Assertions.kotlin; @SuppressWarnings("ALL") class RemoveRedundantTypeCastTest implements RewriteTest {