diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/checkreturnvalue/CanIgnoreReturnValueSuggester.java b/core/src/main/java/com/google/errorprone/bugpatterns/checkreturnvalue/CanIgnoreReturnValueSuggester.java index f74a48cdda8..9f34dc1ca7d 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/checkreturnvalue/CanIgnoreReturnValueSuggester.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/checkreturnvalue/CanIgnoreReturnValueSuggester.java @@ -32,7 +32,9 @@ import static com.google.errorprone.util.ASTHelpers.stripParentheses; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; import com.google.errorprone.BugPattern; +import com.google.errorprone.ErrorProneFlags; import com.google.errorprone.VisitorState; import com.google.errorprone.bugpatterns.BugChecker; import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher; @@ -74,8 +76,12 @@ public final class CanIgnoreReturnValueSuggester extends BugChecker implements M private static final String AUTO_VALUE = "com.google.auto.value.AutoValue"; private static final String IMMUTABLE = "com.google.errorprone.annotations.Immutable"; - private static final String CRV = "com.google.errorprone.annotations.CheckReturnValue"; private static final String CIRV = "com.google.errorprone.annotations.CanIgnoreReturnValue"; + private static final ImmutableSet EXEMPTING_METHOD_ANNOTATIONS = + ImmutableSet.of( + CIRV, + "com.google.errorprone.annotations.CheckReturnValue", + "com.google.errorprone.refaster.annotation.AfterTemplate"); private static final Supplier PROTO_BUILDER = VisitorState.memoize(s -> s.getTypeFromString("com.google.protobuf.MessageLite.Builder")); @@ -83,12 +89,25 @@ public final class CanIgnoreReturnValueSuggester extends BugChecker implements M private static final ImmutableSet BANNED_METHOD_PREFIXES = ImmutableSet.of("get", "is", "has", "new", "clone", "copy"); + private final ImmutableSet exemptingMethodAnnotations; + + public CanIgnoreReturnValueSuggester(ErrorProneFlags errorProneFlags) { + this.exemptingMethodAnnotations = + errorProneFlags + .getSet("CanIgnoreReturnValue:ExemptingMethodAnnotations") + .map( + additionalAnnotations -> + Sets.union(additionalAnnotations, EXEMPTING_METHOD_ANNOTATIONS).immutableCopy()) + .orElse(EXEMPTING_METHOD_ANNOTATIONS); + } + @Override public Description matchMethod(MethodTree methodTree, VisitorState state) { MethodSymbol methodSymbol = getSymbol(methodTree); - // if the method is already directly annotated w/ @CRV or @CIRV, bail out - if (hasAnnotation(methodSymbol, CRV, state) || hasAnnotation(methodSymbol, CIRV, state)) { + // If the method has an exempting annotation, then bail out. + if (exemptingMethodAnnotations.stream() + .anyMatch(annotation -> ASTHelpers.hasAnnotation(methodSymbol, annotation, state))) { return Description.NO_MATCH; } diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/checkreturnvalue/CanIgnoreReturnValueSuggesterTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/checkreturnvalue/CanIgnoreReturnValueSuggesterTest.java index 69690a5b5e4..7aef6264819 100644 --- a/core/src/test/java/com/google/errorprone/bugpatterns/checkreturnvalue/CanIgnoreReturnValueSuggesterTest.java +++ b/core/src/test/java/com/google/errorprone/bugpatterns/checkreturnvalue/CanIgnoreReturnValueSuggesterTest.java @@ -609,6 +609,24 @@ public void constructor() { .doTest(); } + @Test + public void refasterAfterTemplate() { + helper + .addInputLines( + "A.java", + "import com.google.errorprone.refaster.annotation.AfterTemplate;", + "class A {", + " static final class MethodLacksBeforeTemplateAnnotation {", + " @AfterTemplate", + " String after(String str) {", + " return str;", + " }", + " }", + "}") + .expectUnchanged() + .doTest(); + } + @Test public void sometimesThrows() { helper @@ -832,4 +850,26 @@ public void providesMethod_b267362954() { .expectUnchanged() .doTest(); } + + @Test + public void exemptedByCustomAnnotation() { + helper + .addInputLines("Foo.java", "package example;", "@interface Foo {}") + .expectUnchanged() + .addInputLines( + "ExemptedByCustomAnnotation.java", + "package example;", + "public final class ExemptedByCustomAnnotation {", + " private String name;", + "", + " @Foo", + " public ExemptedByCustomAnnotation setName(String name) {", + " this.name = name;", + " return this;", + " }", + "}") + .expectUnchanged() + .setArgs("-XepOpt:CanIgnoreReturnValue:ExemptingMethodAnnotations=example.Foo") + .doTest(); + } }