diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/EqualityRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/EqualityRules.java index effd045410..119f1634f3 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/EqualityRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/EqualityRules.java @@ -1,11 +1,16 @@ package tech.picnic.errorprone.refasterrules; +import static java.util.function.Predicate.not; + import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.refaster.Refaster; import com.google.errorprone.refaster.annotation.AfterTemplate; import com.google.errorprone.refaster.annotation.AlsoNegation; import com.google.errorprone.refaster.annotation.BeforeTemplate; +import com.google.errorprone.refaster.annotation.MayOptionallyUse; +import com.google.errorprone.refaster.annotation.Placeholder; import java.util.Objects; +import java.util.Optional; import java.util.function.Predicate; import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; @@ -131,4 +136,62 @@ boolean after(boolean a, boolean b) { return a == b; } } + + /** + * Don't pass a lambda expression to {@link Predicate#not(Predicate)}; instead push the negation + * into the lambda expression. + */ + abstract static class PredicateLambda { + @Placeholder(allowsIdentity = true) + abstract boolean predicate(@MayOptionallyUse T value); + + @BeforeTemplate + Predicate before() { + return not(v -> predicate(v)); + } + + @AfterTemplate + Predicate after() { + return v -> !predicate(v); + } + } + + /** Avoid contrived ways of handling {@code null} values during equality testing. */ + static final class EqualsLhsNullable { + @BeforeTemplate + boolean before(T value1, S value2) { + return Optional.ofNullable(value1).equals(Optional.of(value2)); + } + + @AfterTemplate + boolean after(T value1, S value2) { + return value2.equals(value1); + } + } + + /** Avoid contrived ways of handling {@code null} values during equality testing. */ + static final class EqualsRhsNullable { + @BeforeTemplate + boolean before(T value1, S value2) { + return Optional.of(value1).equals(Optional.ofNullable(value2)); + } + + @AfterTemplate + boolean after(T value1, S value2) { + return value1.equals(value2); + } + } + + /** Avoid contrived ways of handling {@code null} values during equality testing. */ + static final class EqualsLhsAndRhsNullable { + @BeforeTemplate + boolean before(T value1, S value2) { + return Optional.ofNullable(value1).equals(Optional.ofNullable(value2)); + } + + @AfterTemplate + boolean after(T value1, S value2) { + return Objects.equals(value1, value2); + } + } } diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/EqualityRulesTestInput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/EqualityRulesTestInput.java index d025e7d031..93b9764079 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/EqualityRulesTestInput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/EqualityRulesTestInput.java @@ -1,16 +1,20 @@ package tech.picnic.errorprone.refasterrules; +import static java.util.function.Predicate.not; + import com.google.common.collect.BoundType; import com.google.common.collect.ImmutableSet; import java.math.RoundingMode; import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; import java.util.stream.Stream; import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase; final class EqualityRulesTest implements RefasterRuleCollectionTestCase { @Override public ImmutableSet elidedTypesAndStaticImports() { - return ImmutableSet.of(Objects.class); + return ImmutableSet.of(Objects.class, Optional.class, not(null)); } ImmutableSet testPrimitiveOrReferenceEquality() { @@ -60,4 +64,20 @@ ImmutableSet testIndirectDoubleNegation() { !(3.0 != 4.0), !(BoundType.OPEN != BoundType.CLOSED)); } + + Predicate testPredicateLambda() { + return not(v -> v.isEmpty()); + } + + boolean testEqualsLhsNullable() { + return Optional.ofNullable("foo").equals(Optional.of("bar")); + } + + boolean testEqualsRhsNullable() { + return Optional.of("foo").equals(Optional.ofNullable("bar")); + } + + boolean testEqualsLhsAndRhsNullable() { + return Optional.ofNullable("foo").equals(Optional.ofNullable("bar")); + } } diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/EqualityRulesTestOutput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/EqualityRulesTestOutput.java index 793c24f3e2..c61eb76748 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/EqualityRulesTestOutput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/EqualityRulesTestOutput.java @@ -1,16 +1,20 @@ package tech.picnic.errorprone.refasterrules; +import static java.util.function.Predicate.not; + import com.google.common.collect.BoundType; import com.google.common.collect.ImmutableSet; import java.math.RoundingMode; import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; import java.util.stream.Stream; import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase; final class EqualityRulesTest implements RefasterRuleCollectionTestCase { @Override public ImmutableSet elidedTypesAndStaticImports() { - return ImmutableSet.of(Objects.class); + return ImmutableSet.of(Objects.class, Optional.class, not(null)); } ImmutableSet testPrimitiveOrReferenceEquality() { @@ -60,4 +64,20 @@ ImmutableSet testIndirectDoubleNegation() { 3.0 == 4.0, BoundType.OPEN == BoundType.CLOSED); } + + Predicate testPredicateLambda() { + return v -> !v.isEmpty(); + } + + boolean testEqualsLhsNullable() { + return "bar".equals("foo"); + } + + boolean testEqualsRhsNullable() { + return "foo".equals("bar"); + } + + boolean testEqualsLhsAndRhsNullable() { + return Objects.equals("foo", "bar"); + } }