diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/OptionalRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/OptionalRules.java index 1c58ee3d7a..a5e75f3f77 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/OptionalRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/OptionalRules.java @@ -355,6 +355,41 @@ Optional after(Optional optional) { } } + /** + * Avoid unnecessary {@link Optional} to {@link Stream} conversion when filtering that ultimately + * returns the same result. + */ + static final class OptionalStreamFilter { + @BeforeTemplate + Optional before(Optional optional, Predicate predicate) { + return Refaster.anyOf( + optional.stream().filter(predicate).findAny(), + optional.stream().filter(predicate).findFirst()); + } + + @AfterTemplate + Optional after(Optional optional, Predicate predicate) { + return optional.filter(predicate); + } + } + + /** + * Avoid unnecessary {@link Optional} to {@link Stream} conversion when mapping that ultimately + * returns the same result. + */ + static final class OptionalStreamMap { + @BeforeTemplate + Optional before(Optional optional, Function function) { + return Refaster.anyOf( + optional.stream().map(function).findAny(), optional.stream().map(function).findFirst()); + } + + @AfterTemplate + Optional after(Optional optional, Function function) { + return optional.map(function); + } + } + // XXX: Add a rule for: // `optional.flatMap(x -> pred(x) ? Optional.empty() : Optional.of(x))` and variants. // (Maybe canonicalize the inner expression. Maybe we rewrite already.) diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/OptionalRulesTestInput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/OptionalRulesTestInput.java index d06d9da7d9..8c6d59b77f 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/OptionalRulesTestInput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/OptionalRulesTestInput.java @@ -109,4 +109,16 @@ ImmutableSet> testOptionalIdentity() { Optional.of("baz").stream().min(String::compareTo), Optional.of("qux").stream().max(String::compareTo)); } + + ImmutableSet> testOptionalStreamFilter() { + return ImmutableSet.of( + Optional.of("foo").stream().filter(String::isEmpty).findFirst(), + Optional.of("bar").stream().filter(String::isEmpty).findAny()); + } + + ImmutableSet> testOptionalStreamMap() { + return ImmutableSet.of( + Optional.of(1).stream().map(String::valueOf).findFirst(), + Optional.of(2).stream().map(String::valueOf).findAny()); + } } diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/OptionalRulesTestOutput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/OptionalRulesTestOutput.java index e20f6b4a6f..89f32dc10c 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/OptionalRulesTestOutput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/OptionalRulesTestOutput.java @@ -103,4 +103,14 @@ ImmutableSet> testOptionalIdentity() { return ImmutableSet.of( Optional.of("foo"), Optional.of("bar"), Optional.of("baz"), Optional.of("qux")); } + + ImmutableSet> testOptionalStreamFilter() { + return ImmutableSet.of( + Optional.of("foo").filter(String::isEmpty), Optional.of("bar").filter(String::isEmpty)); + } + + ImmutableSet> testOptionalStreamMap() { + return ImmutableSet.of( + Optional.of(1).map(String::valueOf), Optional.of(2).map(String::valueOf)); + } }