From d11a123a1b21697a2947cac7c31a347f08c2cc4b Mon Sep 17 00:00:00 2001 From: Laurent Redor Date: Tue, 28 Oct 2025 12:12:03 +0100 Subject: [PATCH 1/5] [DRAFT] Convert Guava `Predicates.instanceOf` Trye to fix issue #918, but without success. --- .../guava/NoGuavaPredicatesInstanceOf.java | 73 +++++++++++++++++++ .../resources/META-INF/rewrite/no-guava.yml | 1 + .../NoGuavaPredicatesInstanceOfTest.java | 68 +++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java create mode 100644 src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java diff --git a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java new file mode 100644 index 0000000000..4eca71be91 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java @@ -0,0 +1,73 @@ +/* + * Copyright 2025 the original author or authors. + *

+ * 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 + *

+ * https://docs.moderne.io/licensing/moderne-source-available-license + *

+ * 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.guava; + +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.JavaTemplate; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.ShortenFullyQualifiedTypeReferences; +import org.openrewrite.java.search.UsesMethod; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; + +import java.util.Set; + +import static java.util.Collections.singleton; + +public class NoGuavaPredicatesInstanceOf extends Recipe { + private static final MethodMatcher PREDICATES_INSTANCE_OF = new MethodMatcher("com.google.common.base.Predicates instanceOf(..)"); + + @Override + public String getDisplayName() { + return "Prefer `ASpecificClass.class::isInstance`"; + } + + @Override + public String getDescription() { + return "Prefer `ASpecificClass.class::isInstance` over `Predicates.instanceOf(ASpecificClass.class)`."; + } + + @Override + public Set getTags() { + return singleton("guava"); + } + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check( + new UsesMethod<>(PREDICATES_INSTANCE_OF), + new JavaIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + if (PREDICATES_INSTANCE_OF.matches(method)) { + maybeRemoveImport("com.google.common.base.Predicates"); + + return JavaTemplate.builder("#{any()}::isInstance") + .build() + .apply(getCursor(), + method.getCoordinates().replace(), + method.getArguments().get(0)); + } + return super.visitMethodInvocation(method, ctx); + } + } + ); + } +} diff --git a/src/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml index 2122f3cb89..a6b219ccb7 100644 --- a/src/main/resources/META-INF/rewrite/no-guava.yml +++ b/src/main/resources/META-INF/rewrite/no-guava.yml @@ -40,6 +40,7 @@ recipeList: - org.openrewrite.java.migrate.guava.NoGuavaMapsNewTreeMap - org.openrewrite.java.migrate.guava.NoGuavaPredicatesAndOr - org.openrewrite.java.migrate.guava.NoGuavaPredicatesEqualTo + - org.openrewrite.java.migrate.guava.NoGuavaPredicatesInstanceOf - org.openrewrite.java.migrate.guava.NoGuavaPrimitiveAsList - org.openrewrite.java.migrate.guava.NoGuavaRefasterRecipes - org.openrewrite.java.migrate.guava.NoGuavaMapsNewHashMap diff --git a/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java new file mode 100644 index 0000000000..89a7247cf8 --- /dev/null +++ b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2025 the original author or authors. + *

+ * 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 + *

+ * https://docs.moderne.io/licensing/moderne-source-available-license + *

+ * 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.guava; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.InMemoryExecutionContext; +import org.openrewrite.Issue; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class NoGuavaPredicatesInstanceOfTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec + .recipe(new NoGuavaPredicatesInstanceOf()) + .parser(JavaParser.fromJavaVersion().classpathFromResources(new InMemoryExecutionContext(), "guava")); + } + + @DocumentExample + @Test + void predicatesEqualToToPredicateIsEqual() { + rewriteRun( + //language=java + java( + """ + import java.util.Collection; + + import com.google.common.base.Predicates; + import com.google.common.collect.Iterables; + + class Test { + boolean test(Collection collection) { + return Iterables.all(collection, Predicates.instanceOf(String.class)); + } + } + """, + """ + import java.util.Collection; + + import com.google.common.collect.Iterables; + + class Test { + boolean test(Collection collection) { + return Iterables.all(collection, String.class::isInstance); + } + } + """ + ) + ); + } +} From a1eaa6aac1d438f0e236cf236c4b4ae74daf6b1b Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 28 Oct 2025 22:48:32 +0100 Subject: [PATCH 2/5] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../java/migrate/guava/NoGuavaPredicatesInstanceOf.java | 1 - .../java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java index 4eca71be91..fd94ccbffc 100644 --- a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java +++ b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java @@ -22,7 +22,6 @@ import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; -import org.openrewrite.java.ShortenFullyQualifiedTypeReferences; import org.openrewrite.java.search.UsesMethod; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; diff --git a/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java index 89a7247cf8..64fd182d7a 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java @@ -18,7 +18,6 @@ import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; import org.openrewrite.InMemoryExecutionContext; -import org.openrewrite.Issue; import org.openrewrite.java.JavaParser; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; From 6296c10f43abe6d3b1d49b98ec63da79659f5787 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 29 Oct 2025 19:04:15 +0100 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../java/migrate/guava/NoGuavaPredicatesInstanceOf.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java index fd94ccbffc..757fc75b47 100644 --- a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java +++ b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java @@ -24,8 +24,6 @@ import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.search.UsesMethod; import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.JavaType; - import java.util.Set; import static java.util.Collections.singleton; From 6cf0177d4cc3d540a8e9fd7a1bdc18ee80b1d716 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 31 Oct 2025 14:14:55 +0100 Subject: [PATCH 4/5] Use explicit constructors for now --- .../guava/NoGuavaPredicatesInstanceOf.java | 47 ++++++++++++------- .../NoGuavaPredicatesInstanceOfTest.java | 2 + 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java index 757fc75b47..9f0b59df3d 100644 --- a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java +++ b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java @@ -15,17 +15,19 @@ */ package org.openrewrite.java.migrate.guava; -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.JavaTemplate; +import org.openrewrite.*; +import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.search.UsesMethod; import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JLeftPadded; +import org.openrewrite.java.tree.JRightPadded; +import org.openrewrite.java.tree.Space; +import org.openrewrite.marker.Markers; + import java.util.Set; +import static java.util.Collections.emptyList; import static java.util.Collections.singleton; public class NoGuavaPredicatesInstanceOf extends Recipe { @@ -33,12 +35,12 @@ public class NoGuavaPredicatesInstanceOf extends Recipe { @Override public String getDisplayName() { - return "Prefer `ASpecificClass.class::isInstance`"; + return "Prefer `A.class::isInstance`"; } @Override public String getDescription() { - return "Prefer `ASpecificClass.class::isInstance` over `Predicates.instanceOf(ASpecificClass.class)`."; + return "Prefer `A.class::isInstance` over `Predicates.instanceOf(A.class)`."; } @Override @@ -50,17 +52,30 @@ public Set getTags() { public TreeVisitor getVisitor() { return Preconditions.check( new UsesMethod<>(PREDICATES_INSTANCE_OF), - new JavaIsoVisitor() { + new JavaVisitor() { @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { if (PREDICATES_INSTANCE_OF.matches(method)) { maybeRemoveImport("com.google.common.base.Predicates"); - - return JavaTemplate.builder("#{any()}::isInstance") - .build() - .apply(getCursor(), - method.getCoordinates().replace(), - method.getArguments().get(0)); + // XXX `JavaTemplate.builder("#{any()}::isInstance")` failed here + // TODO Add type information for reference and identifier + return new J.MemberReference( + Tree.randomId(), + method.getPrefix(), + Markers.EMPTY, + JRightPadded.build(method.getArguments().get(0)), + null, + JLeftPadded.build(new J.Identifier( + Tree.randomId(), + Space.EMPTY, + Markers.EMPTY, + emptyList(), + "isInstance", + null, + null)), + null, + null, + null); } return super.visitMethodInvocation(method, ctx); } diff --git a/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java index 64fd182d7a..414e67351c 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java @@ -21,6 +21,7 @@ import org.openrewrite.java.JavaParser; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.TypeValidation; import static org.openrewrite.java.Assertions.java; @@ -36,6 +37,7 @@ public void defaults(RecipeSpec spec) { @Test void predicatesEqualToToPredicateIsEqual() { rewriteRun( + spec -> spec.afterTypeValidationOptions(TypeValidation.none()), // TODO Remove suppression //language=java java( """ From b6c31ff775116d155afa7f5b1e253fc9a00c706d Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 12 Nov 2025 18:58:51 +0100 Subject: [PATCH 5/5] Require a public no args constructor Fixes #933 --- .../java/migrate/guava/NoGuavaPredicatesInstanceOf.java | 8 +++----- .../migrate/guava/NoGuavaPredicatesInstanceOfTest.java | 3 ++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java index 9f0b59df3d..54b48755b7 100644 --- a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java +++ b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOf.java @@ -19,10 +19,7 @@ import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.search.UsesMethod; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.JLeftPadded; -import org.openrewrite.java.tree.JRightPadded; -import org.openrewrite.java.tree.Space; +import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; import java.util.Set; @@ -57,13 +54,14 @@ public TreeVisitor getVisitor() { public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { if (PREDICATES_INSTANCE_OF.matches(method)) { maybeRemoveImport("com.google.common.base.Predicates"); + Expression clazz = method.getArguments().get(0); // XXX `JavaTemplate.builder("#{any()}::isInstance")` failed here // TODO Add type information for reference and identifier return new J.MemberReference( Tree.randomId(), method.getPrefix(), Markers.EMPTY, - JRightPadded.build(method.getArguments().get(0)), + JRightPadded.build(clazz), null, JLeftPadded.build(new J.Identifier( Tree.randomId(), diff --git a/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java index 414e67351c..c8b94bb8b2 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicatesInstanceOfTest.java @@ -37,7 +37,8 @@ public void defaults(RecipeSpec spec) { @Test void predicatesEqualToToPredicateIsEqual() { rewriteRun( - spec -> spec.afterTypeValidationOptions(TypeValidation.none()), // TODO Remove suppression + // XXX Pending JavaTemplate support for MemberReference + spec -> spec.afterTypeValidationOptions(TypeValidation.none()), //language=java java( """