diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ComparatorRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ComparatorRules.java index f8ec1393d55..51a26592792 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ComparatorRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ComparatorRules.java @@ -219,7 +219,6 @@ int after(T value1, T value2) { */ static final class MinOfVarargs { @BeforeTemplate - @SuppressWarnings("StreamOfArray" /* In practice individual values are provided. */) T before(@Repeated T value, Comparator cmp) { return Stream.of(Refaster.asVarargs(value)).min(cmp).orElseThrow(); } @@ -287,7 +286,6 @@ T after(T value1, T value2, Comparator cmp) { */ static final class MaxOfVarargs { @BeforeTemplate - @SuppressWarnings("StreamOfArray" /* In practice individual values are provided. */) T before(@Repeated T value, Comparator cmp) { return Stream.of(Refaster.asVarargs(value)).max(cmp).orElseThrow(); } diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StreamRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StreamRules.java index 604abf1f74e..04499a86f7f 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StreamRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StreamRules.java @@ -25,6 +25,7 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate; import com.google.errorprone.refaster.annotation.Matches; import com.google.errorprone.refaster.annotation.MayOptionallyUse; +import com.google.errorprone.refaster.annotation.NotMatches; import com.google.errorprone.refaster.annotation.Placeholder; import com.google.errorprone.refaster.annotation.UseImportPolicy; import java.util.Arrays; @@ -46,6 +47,7 @@ import java.util.stream.Stream; import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; import tech.picnic.errorprone.refaster.matchers.IsLambdaExpressionOrMethodReference; +import tech.picnic.errorprone.refaster.matchers.IsRefasterAsVarargs; /** Refaster rules related to expressions dealing with {@link Stream}s. */ @OnlineDocumentation @@ -100,12 +102,9 @@ Stream after(T object) { * Prefer {@link Arrays#stream(Object[])} over {@link Stream#of(Object[])}, as the former is * clearer. */ - // XXX: Introduce a `Matcher` that identifies `Refaster.asVarargs(...)` invocations and annotate - // the `array` parameter as `@NotMatches(IsRefasterAsVarargs.class)`. Then elsewhere - // `@SuppressWarnings("StreamOfArray")` annotations can be dropped. static final class StreamOfArray { @BeforeTemplate - Stream before(T[] array) { + Stream before(@NotMatches(IsRefasterAsVarargs.class) T[] array) { return Stream.of(array); } diff --git a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/IsRefasterAsVarargs.java b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/IsRefasterAsVarargs.java new file mode 100644 index 00000000000..8a542be16ab --- /dev/null +++ b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/IsRefasterAsVarargs.java @@ -0,0 +1,23 @@ +package tech.picnic.errorprone.refaster.matchers; + +import static com.google.errorprone.matchers.Matchers.staticMethod; + +import com.google.errorprone.VisitorState; +import com.google.errorprone.matchers.Matcher; +import com.google.errorprone.refaster.Refaster; +import com.sun.source.tree.ExpressionTree; + +/** A matcher of {@link Refaster#asVarargs} method invocations. */ +public final class IsRefasterAsVarargs implements Matcher { + private static final long serialVersionUID = 1L; + private static final Matcher DELEGATE = + staticMethod().onClass(Refaster.class.getName()).namedAnyOf("asVarargs"); + + /** Instantiates a new {@link IsRefasterAsVarargs} instance. */ + public IsRefasterAsVarargs() {} + + @Override + public boolean matches(ExpressionTree expressionTree, VisitorState state) { + return DELEGATE.matches(expressionTree, state); + } +} diff --git a/refaster-support/src/test/java/tech/picnic/errorprone/refaster/matchers/IsRefasterAsVarargsTest.java b/refaster-support/src/test/java/tech/picnic/errorprone/refaster/matchers/IsRefasterAsVarargsTest.java new file mode 100644 index 00000000000..8df32498f0f --- /dev/null +++ b/refaster-support/src/test/java/tech/picnic/errorprone/refaster/matchers/IsRefasterAsVarargsTest.java @@ -0,0 +1,52 @@ +package tech.picnic.errorprone.refaster.matchers; + +import static com.google.errorprone.BugPattern.SeverityLevel.ERROR; + +import com.google.errorprone.BugPattern; +import com.google.errorprone.CompilationTestHelper; +import com.google.errorprone.bugpatterns.BugChecker; +import org.junit.jupiter.api.Test; + +final class IsRefasterAsVarargsTest { + @Test + void matches() { + CompilationTestHelper.newInstance(MatcherTestChecker.class, getClass()) + .addSourceLines( + "A.java", + "import com.google.errorprone.refaster.Refaster;", + "", + "class A {", + " int[] negative1() {", + " return new int[4];", + " }", + "", + " static String[] util() {", + " return new String[5];", + " }", + "", + " String[] negative2() {", + " return A.util();", + " }", + "", + " String[] positive1() {", + " String a = \"a\";", + " // BUG: Diagnostic contains:", + " return Refaster.asVarargs(a);", + " }", + "}") + .doTest(); + } + + /** A {@link BugChecker} that simply delegates to {@link IsRefasterAsVarargs}. */ + @BugPattern(summary = "Flags expressions matched by `IsRefasterAsVarargs`", severity = ERROR) + public static final class MatcherTestChecker extends AbstractMatcherTestChecker { + private static final long serialVersionUID = 1L; + + // XXX: This is a false positive reported by Checkstyle. See + // https://github.com/checkstyle/checkstyle/issues/10161#issuecomment-1242732120. + @SuppressWarnings("RedundantModifier") + public MatcherTestChecker() { + super(new IsRefasterAsVarargs()); + } + } +}