Skip to content

Commit 95028a5

Browse files
authored
Add UsePredicateNot to replace cast-and-negate with Predicate.not(..) (#1072)
* Add `UsePredicateNot` to replace cast-and-negate with `Predicate.not(..)` Targets Java 11+ code like `((Predicate<T>) lambdaOrMethodRef).negate()` and rewrites to `Predicate.not(lambdaOrMethodRef)`. * Regenerate `recipes.csv` * Use `Expression.unwrap()` in `UsePredicateNot`
1 parent a58b8b2 commit 95028a5

5 files changed

Lines changed: 249 additions & 6 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2026 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.util;
17+
18+
import lombok.Getter;
19+
import org.openrewrite.ExecutionContext;
20+
import org.openrewrite.Preconditions;
21+
import org.openrewrite.Recipe;
22+
import org.openrewrite.TreeVisitor;
23+
import org.openrewrite.java.JavaTemplate;
24+
import org.openrewrite.java.JavaVisitor;
25+
import org.openrewrite.java.MethodMatcher;
26+
import org.openrewrite.java.search.UsesJavaVersion;
27+
import org.openrewrite.java.search.UsesMethod;
28+
import org.openrewrite.java.tree.Expression;
29+
import org.openrewrite.java.tree.J;
30+
import org.openrewrite.java.tree.TypeUtils;
31+
32+
import java.time.Duration;
33+
34+
public class UsePredicateNot extends Recipe {
35+
36+
private static final String PREDICATE_FQN = "java.util.function.Predicate";
37+
private static final MethodMatcher PREDICATE_NEGATE = new MethodMatcher(PREDICATE_FQN + " negate()");
38+
39+
@Getter
40+
final String displayName = "Prefer `Predicate.not(..)` over casting to `Predicate` and calling `negate()`";
41+
42+
@Getter
43+
final String description = "Replace `((Predicate<T>) lambdaOrMethodRef).negate()` with `Predicate.not(lambdaOrMethodRef)` as of Java 11.";
44+
45+
@Getter
46+
final Duration estimatedEffortPerOccurrence = Duration.ofMinutes(1);
47+
48+
@Override
49+
public TreeVisitor<?, ExecutionContext> getVisitor() {
50+
return Preconditions.check(
51+
Preconditions.and(new UsesJavaVersion<>(11), new UsesMethod<>(PREDICATE_NEGATE)),
52+
new JavaVisitor<ExecutionContext>() {
53+
@Override
54+
public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
55+
J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx);
56+
if (!PREDICATE_NEGATE.matches(m) || m.getSelect() == null) {
57+
return m;
58+
}
59+
Expression unwrapped = m.getSelect().unwrap();
60+
if (!(unwrapped instanceof J.TypeCast)) {
61+
return m;
62+
}
63+
J.TypeCast cast = (J.TypeCast) unwrapped;
64+
if (!TypeUtils.isAssignableTo(PREDICATE_FQN, cast.getType())) {
65+
return m;
66+
}
67+
maybeAddImport(PREDICATE_FQN);
68+
return JavaTemplate.builder("Predicate.not(#{any()})")
69+
.imports(PREDICATE_FQN)
70+
.build()
71+
.apply(getCursor(), m.getCoordinates().replace(), cast.getExpression());
72+
}
73+
}
74+
);
75+
}
76+
}

src/main/resources/META-INF/rewrite/java-util-apis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,5 @@ recipeList:
4444
- org.openrewrite.java.migrate.util.UseListOf
4545
- org.openrewrite.java.migrate.util.UseLocaleOf
4646
- org.openrewrite.java.migrate.util.UseMapOf
47+
- org.openrewrite.java.migrate.util.UsePredicateNot
4748
- org.openrewrite.java.migrate.util.UseSetOf

src/main/resources/META-INF/rewrite/java-version-11.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ recipeList:
6969
- org.openrewrite.java.migrate.util.OptionalNotPresentToIsEmpty
7070
- org.openrewrite.java.migrate.util.OptionalNotEmptyToIsPresent
7171
- org.openrewrite.java.migrate.util.OptionalStreamRecipe
72+
- org.openrewrite.java.migrate.util.UsePredicateNot
7273
# Disabled as too opinionated for general use
7374
# https://github.com/openrewrite/rewrite-migrate-java/issues/958
7475
# - org.openrewrite.java.migrate.util.UseEnumSetOf:

0 commit comments

Comments
 (0)