diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJBigDecimalRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJBigDecimalRules.java index 272b3ab194..576e8bde2b 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJBigDecimalRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJBigDecimalRules.java @@ -9,6 +9,7 @@ import java.math.BigDecimal; import org.assertj.core.api.AbstractBigDecimalAssert; import org.assertj.core.api.BigDecimalAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** * Refaster rules related to AssertJ assertions over {@link BigDecimal}s. @@ -20,6 +21,7 @@ * instances, but also their scale. As a result various seemingly straightforward transformations * would actually subtly change the assertion's semantics. */ +@OnlineDocumentation final class AssertJBigDecimalRules { private AssertJBigDecimalRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJBigIntegerRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJBigIntegerRules.java index 7b3ad736ca..3f9ca69b4c 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJBigIntegerRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJBigIntegerRules.java @@ -8,9 +8,11 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate; import java.math.BigInteger; import org.assertj.core.api.AbstractBigIntegerAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; // XXX: If we add a rule that drops unnecessary `L` suffixes from literal longs, then the `0L`/`1L` // cases below can go. +@OnlineDocumentation final class AssertJBigIntegerRules { private AssertJBigIntegerRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJBooleanRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJBooleanRules.java index 9c28353c63..cc582f1a1a 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJBooleanRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJBooleanRules.java @@ -8,7 +8,9 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate; import com.google.errorprone.refaster.annotation.UseImportPolicy; import org.assertj.core.api.AbstractBooleanAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJBooleanRules { private AssertJBooleanRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJByteRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJByteRules.java index 1c1134c387..7f4d2d5150 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJByteRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJByteRules.java @@ -7,7 +7,9 @@ import com.google.errorprone.refaster.annotation.AfterTemplate; import com.google.errorprone.refaster.annotation.BeforeTemplate; import org.assertj.core.api.AbstractByteAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJByteRules { private AssertJByteRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJCharSequenceRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJCharSequenceRules.java index 545b7fec08..573f9efa02 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJCharSequenceRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJCharSequenceRules.java @@ -8,7 +8,9 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate; import com.google.errorprone.refaster.annotation.UseImportPolicy; import org.assertj.core.api.AbstractAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJCharSequenceRules { private AssertJCharSequenceRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJComparableRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJComparableRules.java index ea92d58fb6..cd76d5d8ff 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJComparableRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJComparableRules.java @@ -8,7 +8,9 @@ import com.google.errorprone.refaster.annotation.UseImportPolicy; import org.assertj.core.api.AbstractComparableAssert; import org.assertj.core.api.AbstractIntegerAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJComparableRules { private AssertJComparableRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJDoubleRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJDoubleRules.java index 624018ffd9..8d640235b9 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJDoubleRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJDoubleRules.java @@ -8,7 +8,9 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate; import org.assertj.core.api.AbstractDoubleAssert; import org.assertj.core.data.Offset; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJDoubleRules { private AssertJDoubleRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJEnumerableRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJEnumerableRules.java index a891bfbc06..bc19fd72dc 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJEnumerableRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJEnumerableRules.java @@ -6,7 +6,9 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate; import java.util.Collection; import org.assertj.core.api.EnumerableAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJEnumerableRules { private AssertJEnumerableRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJFloatRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJFloatRules.java index 8952ca4f2b..0d7d92e436 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJFloatRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJFloatRules.java @@ -8,7 +8,9 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate; import org.assertj.core.api.AbstractFloatAssert; import org.assertj.core.data.Offset; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJFloatRules { private AssertJFloatRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJIntegerRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJIntegerRules.java index d134bfe6c8..6495f8bb3e 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJIntegerRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJIntegerRules.java @@ -7,7 +7,9 @@ import com.google.errorprone.refaster.annotation.AfterTemplate; import com.google.errorprone.refaster.annotation.BeforeTemplate; import org.assertj.core.api.AbstractIntegerAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJIntegerRules { private AssertJIntegerRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJLongRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJLongRules.java index 3ac9275fc2..35c16db3da 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJLongRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJLongRules.java @@ -7,7 +7,9 @@ import com.google.errorprone.refaster.annotation.AfterTemplate; import com.google.errorprone.refaster.annotation.BeforeTemplate; import org.assertj.core.api.AbstractLongAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJLongRules { private AssertJLongRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJMapRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJMapRules.java index dfbd303ea3..59f99e9504 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJMapRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJMapRules.java @@ -5,7 +5,9 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate; import java.util.Map; import org.assertj.core.api.AbstractMapAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJMapRules { private AssertJMapRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJNumberRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJNumberRules.java index fac67d6068..43453d3031 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJNumberRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJNumberRules.java @@ -19,8 +19,10 @@ import org.assertj.core.api.AbstractLongAssert; import org.assertj.core.api.AbstractShortAssert; import org.assertj.core.api.NumberAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; import tech.picnic.errorprone.refaster.matchers.IsCharacter; +@OnlineDocumentation final class AssertJNumberRules { private AssertJNumberRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJObjectRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJObjectRules.java index b5122b7296..41b1a51c61 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJObjectRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJObjectRules.java @@ -10,7 +10,9 @@ import org.assertj.core.api.AbstractBooleanAssert; import org.assertj.core.api.AbstractStringAssert; import org.assertj.core.api.ObjectAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJObjectRules { private AssertJObjectRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJOptionalRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJOptionalRules.java index f14bef43ad..2cab238072 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJOptionalRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJOptionalRules.java @@ -14,7 +14,9 @@ import org.assertj.core.api.AbstractOptionalAssert; import org.assertj.core.api.ObjectAssert; import org.assertj.core.api.OptionalAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJOptionalRules { private AssertJOptionalRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJPrimitiveRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJPrimitiveRules.java index 7635e37037..e3285067ab 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJPrimitiveRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJPrimitiveRules.java @@ -9,7 +9,9 @@ import com.google.errorprone.refaster.annotation.UseImportPolicy; import org.assertj.core.api.AbstractBooleanAssert; import org.assertj.core.api.AbstractDoubleAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJPrimitiveRules { private AssertJPrimitiveRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJRules.java index 4da5ba9c92..8ee7396d9a 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJRules.java @@ -53,6 +53,7 @@ import org.assertj.core.api.OptionalDoubleAssert; import org.assertj.core.api.OptionalIntAssert; import org.assertj.core.api.OptionalLongAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; import tech.picnic.errorprone.refaster.matchers.IsArray; /** Refaster rules related to AssertJ expressions and statements. */ @@ -118,8 +119,9 @@ // XXX: `assertThat(ImmutableList.sortedCopyOf(cmp, values)).somethingExactOrder` -> just compare // "in any order". // XXX: Turns out a lot of this is also covered by https://github.com/palantir/assertj-automation. -// See how we can combine these things. Do note that (at present) their Refaster rules don't show up -// as Error Prone checks. So we'd have to build an integration for that. +// See how we can combine these things. Do note that (at present) their Refaster rules don't +// show up as Error Prone checks. So we'd have to build an integration for that. +@OnlineDocumentation final class AssertJRules { private AssertJRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJShortRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJShortRules.java index a09e34ca2b..4818920357 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJShortRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJShortRules.java @@ -7,7 +7,9 @@ import com.google.errorprone.refaster.annotation.AfterTemplate; import com.google.errorprone.refaster.annotation.BeforeTemplate; import org.assertj.core.api.AbstractShortAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJShortRules { private AssertJShortRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJStringRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJStringRules.java index d149d3b1ff..0f3fee47ba 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJStringRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJStringRules.java @@ -8,7 +8,9 @@ import com.google.errorprone.refaster.annotation.UseImportPolicy; import org.assertj.core.api.AbstractAssert; import org.assertj.core.api.AbstractStringAssert; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +@OnlineDocumentation final class AssertJStringRules { private AssertJStringRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJThrowingCallableRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJThrowingCallableRules.java index f221f66425..3bfb7502ee 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJThrowingCallableRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssertJThrowingCallableRules.java @@ -16,6 +16,7 @@ import org.assertj.core.api.AbstractObjectAssert; import org.assertj.core.api.AbstractThrowableAssert; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** * Refaster rules related to AssertJ assertions over expressions that may throw a {@link Throwable} @@ -26,6 +27,7 @@ * types. Note that only the most common assertion expressions are rewritten here; covering all * cases would require the implementation of an Error Prone check instead. */ +@OnlineDocumentation final class AssertJThrowingCallableRules { private AssertJThrowingCallableRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssortedRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssortedRules.java index 872f9eced8..1d5523736f 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssortedRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/AssortedRules.java @@ -28,11 +28,13 @@ import java.util.Set; import java.util.stream.Stream; import javax.annotation.Nullable; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** * Assorted Refaster rules that do not (yet) belong in one of the other classes with more topical * Refaster rules. */ +@OnlineDocumentation final class AssortedRules { private AssortedRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/BigDecimalRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/BigDecimalRules.java index 7f27917660..b23406170b 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/BigDecimalRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/BigDecimalRules.java @@ -4,8 +4,10 @@ import com.google.errorprone.refaster.annotation.AfterTemplate; import com.google.errorprone.refaster.annotation.BeforeTemplate; import java.math.BigDecimal; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link BigDecimal}s. */ +@OnlineDocumentation final class BigDecimalRules { private BigDecimalRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/CollectionRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/CollectionRules.java index e02c12c2a2..19df9107db 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/CollectionRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/CollectionRules.java @@ -19,10 +19,12 @@ import java.util.SortedSet; import java.util.function.IntFunction; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with (arbitrary) collections. */ // XXX: There are other Guava `Iterables` methods that should not be called if the input is known to // be a `Collection`. Add those here. +@OnlineDocumentation final class CollectionRules { private CollectionRules() {} 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 3a1e94d32b..83ea4c7378 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 @@ -24,8 +24,10 @@ import java.util.function.ToDoubleFunction; import java.util.function.ToIntFunction; import java.util.function.ToLongFunction; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link Comparator}s. */ +@OnlineDocumentation final class ComparatorRules { private ComparatorRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/DoubleStreamRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/DoubleStreamRules.java index 7e8e007b19..ca64f929d8 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/DoubleStreamRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/DoubleStreamRules.java @@ -12,8 +12,10 @@ import java.util.function.DoubleUnaryOperator; import java.util.stream.DoubleStream; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link DoubleStream}s. */ +@OnlineDocumentation final class DoubleStreamRules { private DoubleStreamRules() {} 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 6aaccd1d4d..244a9eceec 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 @@ -6,8 +6,10 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate; import java.util.Objects; import java.util.function.Predicate; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with (in)equalities. */ +@OnlineDocumentation final class EqualityRules { private EqualityRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableListMultimapRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableListMultimapRules.java index 87fc03db95..b68a7a1601 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableListMultimapRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableListMultimapRules.java @@ -24,8 +24,10 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link ImmutableListMultimap}s. */ +@OnlineDocumentation final class ImmutableListMultimapRules { private ImmutableListMultimapRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableListRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableListRules.java index de9aca4deb..1848c268b9 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableListRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableListRules.java @@ -20,8 +20,10 @@ import java.util.Iterator; import java.util.List; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link ImmutableList}s. */ +@OnlineDocumentation final class ImmutableListRules { private ImmutableListRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableMapRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableMapRules.java index 2d1906983f..aa68506b83 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableMapRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableMapRules.java @@ -21,8 +21,10 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link ImmutableMap}s. */ +@OnlineDocumentation final class ImmutableMapRules { private ImmutableMapRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableMultisetRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableMultisetRules.java index 07802c7741..fee76fd89d 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableMultisetRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableMultisetRules.java @@ -13,8 +13,10 @@ import java.util.Collection; import java.util.Iterator; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link ImmutableMultiset}s. */ +@OnlineDocumentation final class ImmutableMultisetRules { private ImmutableMultisetRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSetMultimapRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSetMultimapRules.java index 8272c05b57..1c704943fc 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSetMultimapRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSetMultimapRules.java @@ -21,8 +21,10 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link ImmutableSetMultimap}s. */ +@OnlineDocumentation final class ImmutableSetMultimapRules { private ImmutableSetMultimapRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSetRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSetRules.java index b7159b886a..f4d5ba78b7 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSetRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSetRules.java @@ -17,8 +17,10 @@ import java.util.Iterator; import java.util.Set; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link ImmutableSet}s. */ +@OnlineDocumentation final class ImmutableSetRules { private ImmutableSetRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSortedMapRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSortedMapRules.java index ff03163cad..36975c7025 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSortedMapRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSortedMapRules.java @@ -13,8 +13,10 @@ import java.util.Comparator; import java.util.Map; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link ImmutableSortedMap}s. */ +@OnlineDocumentation final class ImmutableSortedMapRules { private ImmutableSortedMapRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSortedMultisetRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSortedMultisetRules.java index 0e8a252223..c66f86e7d1 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSortedMultisetRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSortedMultisetRules.java @@ -15,8 +15,10 @@ import java.util.Comparator; import java.util.Iterator; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link ImmutableSortedMultiset}s. */ +@OnlineDocumentation final class ImmutableSortedMultisetRules { private ImmutableSortedMultisetRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSortedSetRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSortedSetRules.java index 627fdf7c0f..7117818569 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSortedSetRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ImmutableSortedSetRules.java @@ -15,8 +15,10 @@ import java.util.Comparator; import java.util.Iterator; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link ImmutableSortedSet}s. */ +@OnlineDocumentation final class ImmutableSortedSetRules { private ImmutableSortedSetRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/IntStreamRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/IntStreamRules.java index 382f5d332b..2c43fc3cc1 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/IntStreamRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/IntStreamRules.java @@ -12,8 +12,10 @@ import java.util.function.IntUnaryOperator; import java.util.stream.IntStream; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link IntStream}s. */ +@OnlineDocumentation final class IntStreamRules { private IntStreamRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/JUnitRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/JUnitRules.java index b2c5942856..218213ab9a 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/JUnitRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/JUnitRules.java @@ -8,8 +8,10 @@ import com.google.errorprone.refaster.annotation.Repeated; import com.google.errorprone.refaster.annotation.UseImportPolicy; import org.junit.jupiter.params.provider.Arguments; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to JUnit expressions and statements. */ +@OnlineDocumentation final class JUnitRules { private JUnitRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/LongStreamRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/LongStreamRules.java index 45b4c0da30..363157c55f 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/LongStreamRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/LongStreamRules.java @@ -12,8 +12,10 @@ import java.util.function.LongUnaryOperator; import java.util.stream.LongStream; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link LongStream}s. */ +@OnlineDocumentation final class LongStreamRules { private LongStreamRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/MapEntryRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/MapEntryRules.java index 27d3af80c3..22c2cfe67f 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/MapEntryRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/MapEntryRules.java @@ -14,8 +14,10 @@ import java.util.AbstractMap; import java.util.Comparator; import java.util.Map; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link Map.Entry} instances. */ +@OnlineDocumentation final class MapEntryRules { private MapEntryRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/MockitoRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/MockitoRules.java index b17af9b514..dbb7b16770 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/MockitoRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/MockitoRules.java @@ -10,8 +10,10 @@ import com.google.errorprone.refaster.annotation.UseImportPolicy; import org.mockito.Mockito; import org.mockito.verification.VerificationMode; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to Mockito expressions and statements. */ +@OnlineDocumentation final class MockitoRules { private MockitoRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/MultimapRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/MultimapRules.java index ff64ad784c..576508d745 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/MultimapRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/MultimapRules.java @@ -8,8 +8,10 @@ import java.util.Collection; import java.util.Set; import javax.annotation.Nullable; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link Multimap}s. */ +@OnlineDocumentation final class MultimapRules { private MultimapRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/NullRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/NullRules.java index 770843886c..0182547ef9 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/NullRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/NullRules.java @@ -10,8 +10,10 @@ import java.util.Objects; import java.util.function.Predicate; import javax.annotation.Nullable; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with (possibly) null values. */ +@OnlineDocumentation final class NullRules { private NullRules() {} 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 7c990060a5..6a1ec4d951 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 @@ -17,8 +17,10 @@ import java.util.function.Supplier; import java.util.stream.Stream; import javax.annotation.Nullable; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link Optional}s. */ +@OnlineDocumentation final class OptionalRules { private OptionalRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/PrimitiveRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/PrimitiveRules.java index 041abec002..b0c6c138d2 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/PrimitiveRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/PrimitiveRules.java @@ -3,8 +3,10 @@ import com.google.common.primitives.Ints; import com.google.errorprone.refaster.annotation.AfterTemplate; import com.google.errorprone.refaster.annotation.BeforeTemplate; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with primitives. */ +@OnlineDocumentation final class PrimitiveRules { private PrimitiveRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ReactorRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ReactorRules.java index e03b521533..500bd40e34 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ReactorRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ReactorRules.java @@ -25,9 +25,11 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import reactor.test.publisher.PublisherProbe; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; import tech.picnic.errorprone.refaster.matchers.ThrowsCheckedException; /** Refaster rules related to Reactor expressions and statements. */ +@OnlineDocumentation final class ReactorRules { private ReactorRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/RxJava2AdapterRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/RxJava2AdapterRules.java index b03245c7b1..ad75d06fc5 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/RxJava2AdapterRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/RxJava2AdapterRules.java @@ -13,8 +13,10 @@ import reactor.adapter.rxjava.RxJava2Adapter; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link RxJava2Adapter}. */ +@OnlineDocumentation final class RxJava2AdapterRules { private RxJava2AdapterRules() {} 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 336393e151..17abcf97c3 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 @@ -22,8 +22,10 @@ import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link Stream}s. */ +@OnlineDocumentation final class StreamRules { private StreamRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StringRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StringRules.java index 3e01463f68..a18b289565 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StringRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StringRules.java @@ -17,9 +17,11 @@ import java.util.Optional; import java.util.function.Function; import javax.annotation.Nullable; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with {@link String}s. */ // XXX: Should we prefer `s -> !s.isEmpty()` or `not(String::isEmpty)`? +@OnlineDocumentation final class StringRules { private StringRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/TimeRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/TimeRules.java index 6b33428dc6..5ff77ff8fa 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/TimeRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/TimeRules.java @@ -21,8 +21,10 @@ import java.time.chrono.ChronoZonedDateTime; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalUnit; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with time. */ +@OnlineDocumentation final class TimeRules { private TimeRules() {} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/WebClientRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/WebClientRules.java index 8452f00d17..62e43e7050 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/WebClientRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/WebClientRules.java @@ -20,11 +20,13 @@ import org.springframework.web.reactive.function.client.WebClient.RequestBodyUriSpec; import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec; import org.springframework.web.reactive.function.client.WebClient.RequestHeadersUriSpec; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** * Refaster rules related to expressions dealing with {@link * org.springframework.web.reactive.function.client.WebClient} and related types. */ +@OnlineDocumentation final class WebClientRules { private WebClientRules() {} diff --git a/pom.xml b/pom.xml index 67001d1494..84a6a5572a 100644 --- a/pom.xml +++ b/pom.xml @@ -622,6 +622,10 @@ + + + @@ -821,6 +825,11 @@ error_prone_core ${version.error-prone} + + com.google.auto.value + auto-value + ${version.auto-value} + com.google.auto.service auto-service @@ -1256,6 +1265,9 @@ pitest-maven 1.9.8 + + *.AutoValue_* + @@ -1318,6 +1330,23 @@ + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + true + + + + + + -XepAllErrorsAsWarnings + + -XepDisableWarningsInGeneratedCode diff --git a/refaster-compiler/pom.xml b/refaster-compiler/pom.xml index c494ab1dc0..dc43b58dfa 100644 --- a/refaster-compiler/pom.xml +++ b/refaster-compiler/pom.xml @@ -27,6 +27,10 @@ ${groupId.error-prone} error_prone_core + + ${project.groupId} + refaster-support + com.google.auto.service auto-service-annotations diff --git a/refaster-compiler/src/main/java/tech/picnic/errorprone/refaster/plugin/RefasterRuleCompilerTaskListener.java b/refaster-compiler/src/main/java/tech/picnic/errorprone/refaster/plugin/RefasterRuleCompilerTaskListener.java index 7731ee0810..3c13b74d67 100644 --- a/refaster-compiler/src/main/java/tech/picnic/errorprone/refaster/plugin/RefasterRuleCompilerTaskListener.java +++ b/refaster-compiler/src/main/java/tech/picnic/errorprone/refaster/plugin/RefasterRuleCompilerTaskListener.java @@ -1,12 +1,12 @@ package tech.picnic.errorprone.refaster.plugin; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ListMultimap; -import com.google.common.collect.Multimaps; +import com.google.common.collect.ImmutableClassToInstanceMap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; import com.google.errorprone.CodeTransformer; -import com.google.errorprone.CompositeCodeTransformer; import com.google.errorprone.refaster.RefasterRuleBuilderScanner; +import com.google.errorprone.refaster.UTemplater; import com.google.errorprone.refaster.annotation.BeforeTemplate; import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.AnnotationTree; @@ -26,12 +26,13 @@ import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.UncheckedIOException; -import java.util.List; +import java.lang.annotation.Annotation; import java.util.Map; import javax.annotation.Nullable; import javax.tools.FileObject; import javax.tools.JavaFileManager; import javax.tools.StandardLocation; +import tech.picnic.errorprone.refaster.AnnotatedCompositeCodeTransformer; /** * A variant of {@code com.google.errorprone.refaster.RefasterRuleCompilerAnalyzer} that stores @@ -58,26 +59,60 @@ public void finished(TaskEvent taskEvent) { return; } - ImmutableListMultimap rules = compileRefasterRules(tree); - for (Map.Entry> rule : Multimaps.asMap(rules).entrySet()) { + ImmutableMap rules = compileRefasterRules(tree); + for (Map.Entry rule : rules.entrySet()) { try { - outputCodeTransformers(rule.getValue(), getOutputFile(taskEvent, rule.getKey())); + outputCodeTransformer(rule.getValue(), getOutputFile(taskEvent, rule.getKey())); } catch (IOException e) { throw new UncheckedIOException("Failed to persist compiled Refaster rules", e); } } } - private boolean containsRefasterRules(ClassTree tree) { + private ImmutableMap compileRefasterRules(ClassTree tree) { + ImmutableMap.Builder rules = ImmutableMap.builder(); + new TreeScanner>() { + @Nullable + @Override + public Void visitClass(ClassTree node, ImmutableClassToInstanceMap annotations) { + ClassSymbol symbol = ASTHelpers.getSymbol(node); + + ImmutableList transformers = + ImmutableList.copyOf(RefasterRuleBuilderScanner.extractRules(node, context)); + if (!transformers.isEmpty()) { + rules.put( + node, + AnnotatedCompositeCodeTransformer.create( + toPackageName(symbol), transformers, annotations)); + } + + return super.visitClass(node, merge(annotations, UTemplater.annotationMap(symbol))); + } + }.scan(tree, ImmutableClassToInstanceMap.of()); + return rules.buildOrThrow(); + } + + private FileObject getOutputFile(TaskEvent taskEvent, ClassTree tree) throws IOException { + ClassSymbol symbol = ASTHelpers.getSymbol(tree); + + JavaFileManager fileManager = context.get(JavaFileManager.class); + return fileManager.getFileForOutput( + StandardLocation.CLASS_OUTPUT, + toPackageName(symbol), + toSimpleFlatName(symbol) + ".refaster", + taskEvent.getSourceFile()); + } + + private static boolean containsRefasterRules(ClassTree tree) { return Boolean.TRUE.equals( new TreeScanner() { @Override - public Boolean visitAnnotation(AnnotationTree node, @Nullable Void v) { + public Boolean visitAnnotation(AnnotationTree node, @Nullable Void unused) { Symbol sym = ASTHelpers.getSymbol(node); return (sym != null && sym.getQualifiedName() .contentEquals(BeforeTemplate.class.getCanonicalName())) - || super.visitAnnotation(node, v); + || super.visitAnnotation(node, unused); } @Override @@ -87,40 +122,31 @@ public Boolean reduce(Boolean r1, Boolean r2) { }.scan(tree, null)); } - private ImmutableListMultimap compileRefasterRules(ClassTree tree) { - ListMultimap rules = ArrayListMultimap.create(); - new TreeScanner() { - @Nullable - @Override - public Void visitClass(ClassTree node, @Nullable Void v) { - rules.putAll(node, RefasterRuleBuilderScanner.extractRules(node, context)); - return super.visitClass(node, null); - } - }.scan(tree, null); - return ImmutableListMultimap.copyOf(rules); + /** Merges two annotation mappings, preferring the second over the first in case of conflicts. */ + private static ImmutableClassToInstanceMap merge( + ImmutableClassToInstanceMap first, + ImmutableClassToInstanceMap second) { + return ImmutableClassToInstanceMap.builder() + .putAll(Maps.filterKeys(first, k -> !second.containsKey(k))) + .putAll(second) + .build(); } - private FileObject getOutputFile(TaskEvent taskEvent, ClassTree tree) throws IOException { - ClassSymbol symbol = ASTHelpers.getSymbol(tree); + private static String toPackageName(ClassSymbol symbol) { PackageSymbol enclosingPackage = ASTHelpers.enclosingPackage(symbol); - String packageName = enclosingPackage == null ? "" : enclosingPackage.toString(); - String relativeName = toSimpleFlatName(symbol) + ".refaster"; - - JavaFileManager fileManager = context.get(JavaFileManager.class); - return fileManager.getFileForOutput( - StandardLocation.CLASS_OUTPUT, packageName, relativeName, taskEvent.getSourceFile()); + return enclosingPackage == null ? "" : enclosingPackage.toString(); } - private static CharSequence toSimpleFlatName(ClassSymbol classSymbol) { - Name flatName = classSymbol.flatName(); + private static CharSequence toSimpleFlatName(ClassSymbol symbol) { + Name flatName = symbol.flatName(); int lastDot = flatName.lastIndexOf((byte) '.'); return lastDot < 0 ? flatName : flatName.subSequence(lastDot + 1, flatName.length()); } - private static void outputCodeTransformers(List rules, FileObject target) + private static void outputCodeTransformer(CodeTransformer codeTransformer, FileObject target) throws IOException { try (ObjectOutput output = new ObjectOutputStream(target.openOutputStream())) { - output.writeObject(CompositeCodeTransformer.compose(rules)); + output.writeObject(codeTransformer); } } } diff --git a/refaster-runner/pom.xml b/refaster-runner/pom.xml index c0265fc3a7..9bdd3a65ed 100644 --- a/refaster-runner/pom.xml +++ b/refaster-runner/pom.xml @@ -29,6 +29,11 @@ error_prone_check_api provided + + ${groupId.error-prone} + error_prone_test_helpers + test + ${project.groupId} refaster-compiler @@ -37,6 +42,10 @@ `annotationProcessorPaths` configuration below. --> provided + + ${project.groupId} + refaster-support + com.google.auto.service auto-service-annotations @@ -67,6 +76,11 @@ junit-jupiter-engine test + + org.junit.jupiter + junit-jupiter-params + test + diff --git a/refaster-runner/src/main/java/tech/picnic/errorprone/refaster/runner/Refaster.java b/refaster-runner/src/main/java/tech/picnic/errorprone/refaster/runner/Refaster.java index 203e2fe25b..ef3b70161f 100644 --- a/refaster-runner/src/main/java/tech/picnic/errorprone/refaster/runner/Refaster.java +++ b/refaster-runner/src/main/java/tech/picnic/errorprone/refaster/runner/Refaster.java @@ -3,7 +3,9 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableRangeSet.toImmutableRangeSet; import static com.google.errorprone.BugPattern.LinkType.NONE; +import static com.google.errorprone.BugPattern.SeverityLevel.ERROR; import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION; +import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION; import static java.util.function.Predicate.not; @@ -16,9 +18,11 @@ import com.google.common.collect.RangeSet; import com.google.common.collect.TreeRangeSet; import com.google.errorprone.BugPattern; +import com.google.errorprone.BugPattern.SeverityLevel; import com.google.errorprone.CodeTransformer; import com.google.errorprone.CompositeCodeTransformer; import com.google.errorprone.ErrorProneFlags; +import com.google.errorprone.ErrorProneOptions.Severity; import com.google.errorprone.SubContext; import com.google.errorprone.VisitorState; import com.google.errorprone.annotations.CanIgnoreReturnValue; @@ -33,6 +37,7 @@ import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -40,8 +45,8 @@ * A {@link BugChecker} that flags code that can be simplified using Refaster rules located on the * classpath. * - *

This checker locates all {@code *.refaster} classpath resources and assumes they contain a - * {@link CodeTransformer}. The set of loaded Refaster rules can be restricted by passing {@code + *

This checker locates all {@code *.refaster} classpath resources and assumes that they contain + * a {@link CodeTransformer}. The set of loaded Refaster rules can be restricted by passing {@code * -XepOpt:Refaster:NamePattern=}. */ @AutoService(BugChecker.class) @@ -104,7 +109,7 @@ public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState s */ // XXX: This selection logic solves an issue described in // https://github.com/google/error-prone/issues/559. Consider contributing it back upstream. - private static void applyMatches( + private void applyMatches( Iterable allMatches, EndPosTable endPositions, VisitorState state) { ImmutableList byReplacementSize = ImmutableList.sortedCopyOf( @@ -118,12 +123,55 @@ private static void applyMatches( ImmutableRangeSet ranges = getReplacementRanges(description, endPositions); if (ranges.asRanges().stream().noneMatch(replacedSections::intersects)) { /* This suggested fix does not overlap with any ("larger") replacement seen until now. Apply it. */ - state.reportMatch(description); + state.reportMatch(augmentDescription(description, getSeverityOverride(state))); replacedSections.addAll(ranges); } } } + private Optional getSeverityOverride(VisitorState state) { + return Optional.ofNullable(state.errorProneOptions().getSeverityMap().get(canonicalName())) + .flatMap(Refaster::toSeverityLevel); + } + + private static Optional toSeverityLevel(Severity severity) { + switch (severity) { + case DEFAULT: + return Optional.empty(); + case WARN: + return Optional.of(WARNING); + case ERROR: + return Optional.of(ERROR); + default: + throw new IllegalStateException(String.format("Unsupported severity='%s'", severity)); + } + } + + /** + * Updates the given {@link Description}'s details by standardizing the reported check name, + * updating the associated message, and optionally overriding its severity. + * + *

The assigned severity is overridden only if this bug checker's severity was explicitly + * configured. + * + *

The original check name (i.e. the Refaster rule name) is prepended to the {@link + * Description}'s message. The replacement check name ("Refaster Rule", a name which includes a + * space) is chosen such that it is guaranteed not to match any canonical bug checker name (as + * that could cause {@link VisitorState#reportMatch(Description)}} to override the reported + * severity). + */ + private static Description augmentDescription( + Description description, Optional severityOverride) { + return Description.builder( + description.position, + "Refaster Rule", + description.getLink(), + severityOverride.orElse(description.severity), + String.join(": ", description.checkName, description.getRawMessage())) + .addAllFixes(description.fixes) + .build(); + } + private static int getReplacedCodeSize(Description description, EndPosTable endPositions) { return getReplacements(description, endPositions).mapToInt(Replacement::length).sum(); } diff --git a/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/CodeTransformersTest.java b/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/CodeTransformersTest.java index 5888f03422..5a2a71980d 100644 --- a/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/CodeTransformersTest.java +++ b/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/CodeTransformersTest.java @@ -6,12 +6,17 @@ final class CodeTransformersTest { /** - * Verifies that {@link CodeTransformers#getAllCodeTransformers()} finds the code transformer + * Verifies that {@link CodeTransformers#getAllCodeTransformers()} finds the code transformers * compiled from {@link FooRules} on the classpath. */ @Test void getAllCodeTransformers() { assertThat(CodeTransformers.getAllCodeTransformers().keySet()) - .containsExactly("FooRules$SimpleRule"); + .containsExactlyInAnyOrder( + "FooRules$StringOfSizeZeroRule", + "FooRules$StringOfSizeZeroVerboseRule", + "FooRules$StringOfSizeOneRule", + "FooRules$ExtraGrouping$StringOfSizeTwoRule", + "FooRules$ExtraGrouping$StringOfSizeThreeRule"); } } diff --git a/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/FooRules.java b/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/FooRules.java index be79875a47..df20e66eb7 100644 --- a/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/FooRules.java +++ b/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/FooRules.java @@ -1,14 +1,21 @@ package tech.picnic.errorprone.refaster.runner; +import static com.google.errorprone.BugPattern.SeverityLevel.ERROR; +import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION; +import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; + import com.google.errorprone.refaster.annotation.AfterTemplate; import com.google.errorprone.refaster.annotation.BeforeTemplate; +import tech.picnic.errorprone.refaster.annotation.Description; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +import tech.picnic.errorprone.refaster.annotation.Severity; -/** An example rule collection used to test {@link CodeTransformers}. */ +/** An example rule collection used to test {@link CodeTransformers} and {@link Refaster}. */ final class FooRules { private FooRules() {} - /** Simple rule for testing purposes. */ - static final class SimpleRule { + /** A simple rule for testing purposes, lacking any custom annotations. */ + static final class StringOfSizeZeroRule { @BeforeTemplate boolean before(String string) { return string.toCharArray().length == 0; @@ -19,4 +26,73 @@ boolean after(String string) { return string.isEmpty(); } } + + /** + * A simple rule for testing purposes, matching the same set of expressions as {@link + * StringOfSizeZeroRule}, but producing a larger replacement string. + */ + static final class StringOfSizeZeroVerboseRule { + @BeforeTemplate + boolean before(String string) { + return string.toCharArray().length == 0; + } + + @AfterTemplate + boolean after(String string) { + return string.length() + 1 == 1; + } + } + + /** A simple rule for testing purposes, having several custom annotations. */ + @Description("A custom description about matching single-char strings") + @OnlineDocumentation + @Severity(WARNING) + static final class StringOfSizeOneRule { + @BeforeTemplate + boolean before(String string) { + return string.toCharArray().length == 1; + } + + @AfterTemplate + boolean after(String string) { + return string.length() == 1; + } + } + + /** A nested class with annotations that are inherited by the Refaster rules contained in it. */ + @Description("A custom subgroup description") + @OnlineDocumentation("https://example.com/rule/${topLevelClassName}#${nestedClassName}") + @Severity(ERROR) + static final class ExtraGrouping { + private ExtraGrouping() {} + + /** A simple rule for testing purposes, inheriting custom annotations. */ + static final class StringOfSizeTwoRule { + @BeforeTemplate + boolean before(String string) { + return string.toCharArray().length == 2; + } + + @AfterTemplate + boolean after(String string) { + return string.length() == 2; + } + } + + /** A simple rule for testing purposes, overriding custom annotations. */ + @Description("A custom description about matching three-char strings") + @OnlineDocumentation("https://example.com/custom") + @Severity(SUGGESTION) + static final class StringOfSizeThreeRule { + @BeforeTemplate + boolean before(String string) { + return string.toCharArray().length == 3; + } + + @AfterTemplate + boolean after(String string) { + return string.length() == 3; + } + } + } } diff --git a/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/RefasterTest.java b/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/RefasterTest.java new file mode 100644 index 0000000000..8ec4e5122c --- /dev/null +++ b/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/RefasterTest.java @@ -0,0 +1,272 @@ +package tech.picnic.errorprone.refaster.runner; + +import static com.google.common.base.Predicates.containsPattern; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.errorprone.BugPattern.SeverityLevel.ERROR; +import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION; +import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; +import static java.util.Comparator.comparingInt; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import com.google.common.collect.ImmutableList; +import com.google.errorprone.BugCheckerInfo; +import com.google.errorprone.BugCheckerRefactoringTestHelper; +import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode; +import com.google.errorprone.BugPattern.SeverityLevel; +import com.google.errorprone.CompilationTestHelper; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import tech.picnic.errorprone.refaster.ErrorProneFork; + +final class RefasterTest { + private final CompilationTestHelper compilationHelper = + CompilationTestHelper.newInstance(Refaster.class, getClass()) + .matchAllDiagnostics() + .expectErrorMessage( + "StringOfSizeZeroRule", + containsPattern( + "\\[Refaster Rule\\] FooRules\\.StringOfSizeZeroRule: Refactoring opportunity\\s+.+\\s+")) + .expectErrorMessage( + "StringOfSizeOneRule", + containsPattern( + "\\[Refaster Rule\\] FooRules\\.StringOfSizeOneRule: " + + "A custom description about matching single-char strings\\s+.+\\s+" + + "\\(see https://error-prone.picnic.tech/refasterrules/FooRules#StringOfSizeOneRule\\)")) + .expectErrorMessage( + "StringOfSizeTwoRule", + containsPattern( + "\\[Refaster Rule\\] FooRules\\.ExtraGrouping\\.StringOfSizeTwoRule: " + + "A custom subgroup description\\s+.+\\s+" + + "\\(see https://example.com/rule/FooRules#ExtraGrouping.StringOfSizeTwoRule\\)")) + .expectErrorMessage( + "StringOfSizeThreeRule", + containsPattern( + "\\[Refaster Rule\\] FooRules\\.ExtraGrouping\\.StringOfSizeThreeRule: " + + "A custom description about matching three-char strings\\s+.+\\s+" + + "\\(see https://example.com/custom\\)")); + private final BugCheckerRefactoringTestHelper refactoringTestHelper = + BugCheckerRefactoringTestHelper.newInstance(Refaster.class, getClass()); + private final BugCheckerRefactoringTestHelper restrictedRefactoringTestHelper = + BugCheckerRefactoringTestHelper.newInstance(Refaster.class, getClass()) + .setArgs( + "-XepOpt:Refaster:NamePattern=.*\\$(StringOfSizeZeroVerboseRule|StringOfSizeTwoRule)$"); + + @Test + void identification() { + compilationHelper + .addSourceLines( + "A.java", + "class A {", + " void m() {", + " // BUG: Diagnostic matches: StringOfSizeZeroRule", + " boolean b1 = \"foo\".toCharArray().length == 0;", + " // BUG: Diagnostic matches: StringOfSizeOneRule", + " boolean b2 = \"bar\".toCharArray().length == 1;", + " // BUG: Diagnostic matches: StringOfSizeTwoRule", + " boolean b3 = \"baz\".toCharArray().length == 2;", + " // BUG: Diagnostic matches: StringOfSizeThreeRule", + " boolean b4 = \"qux\".toCharArray().length == 3;", + " }", + "}") + .doTest(); + } + + private static Stream severityAssignmentTestCases() { + /* + * The _actual_ default severity is assigned by the `CodeTransformer`s to which the `Refaster` + * bug checker delegates. Here we verify that the absence of an `@Severity` annotation yields + * the same severity as the bug checker's declared severity. + */ + SeverityLevel defaultSeverity = BugCheckerInfo.create(Refaster.class).defaultSeverity(); + + /* { arguments, expectedSeverities } */ + return Stream.concat( + Stream.of( + arguments( + ImmutableList.of(), ImmutableList.of(defaultSeverity, WARNING, ERROR, SUGGESTION)), + arguments(ImmutableList.of("-Xep:Refaster:OFF"), ImmutableList.of()), + arguments( + ImmutableList.of("-Xep:Refaster:DEFAULT"), + ImmutableList.of(defaultSeverity, WARNING, ERROR, SUGGESTION)), + arguments( + ImmutableList.of("-Xep:Refaster:WARN"), + ImmutableList.of(WARNING, WARNING, WARNING, WARNING)), + arguments( + ImmutableList.of("-Xep:Refaster:ERROR"), + ImmutableList.of(ERROR, ERROR, ERROR, ERROR)), + arguments( + ImmutableList.of("-XepAllErrorsAsWarnings"), + ImmutableList.of(defaultSeverity, WARNING, WARNING, SUGGESTION)), + arguments( + ImmutableList.of("-Xep:Refaster:OFF", "-XepAllErrorsAsWarnings"), + ImmutableList.of()), + arguments( + ImmutableList.of("-Xep:Refaster:DEFAULT", "-XepAllErrorsAsWarnings"), + ImmutableList.of(defaultSeverity, WARNING, WARNING, SUGGESTION)), + arguments( + ImmutableList.of("-Xep:Refaster:WARN", "-XepAllErrorsAsWarnings"), + ImmutableList.of(WARNING, WARNING, WARNING, WARNING)), + arguments( + ImmutableList.of("-Xep:Refaster:ERROR", "-XepAllErrorsAsWarnings"), + ImmutableList.of(WARNING, WARNING, WARNING, WARNING))), + ErrorProneFork.isErrorProneForkAvailable() + ? Stream.of( + arguments( + ImmutableList.of("-Xep:Refaster:OFF", "-XepAllSuggestionsAsWarnings"), + ImmutableList.of()), + arguments( + ImmutableList.of("-Xep:Refaster:DEFAULT", "-XepAllSuggestionsAsWarnings"), + ImmutableList.of(WARNING, WARNING, ERROR, WARNING)), + arguments( + ImmutableList.of("-Xep:Refaster:WARN", "-XepAllSuggestionsAsWarnings"), + ImmutableList.of(WARNING, WARNING, WARNING, WARNING)), + arguments( + ImmutableList.of("-Xep:Refaster:ERROR", "-XepAllSuggestionsAsWarnings"), + ImmutableList.of(ERROR, ERROR, ERROR, ERROR)), + arguments( + ImmutableList.of( + "-Xep:Refaster:OFF", + "-XepAllErrorsAsWarnings", + "-XepAllSuggestionsAsWarnings"), + ImmutableList.of()), + arguments( + ImmutableList.of( + "-Xep:Refaster:DEFAULT", + "-XepAllErrorsAsWarnings", + "-XepAllSuggestionsAsWarnings"), + ImmutableList.of(WARNING, WARNING, WARNING, WARNING)), + arguments( + ImmutableList.of( + "-Xep:Refaster:WARN", + "-XepAllErrorsAsWarnings", + "-XepAllSuggestionsAsWarnings"), + ImmutableList.of(WARNING, WARNING, WARNING, WARNING)), + arguments( + ImmutableList.of( + "-Xep:Refaster:ERROR", + "-XepAllErrorsAsWarnings", + "-XepAllSuggestionsAsWarnings"), + ImmutableList.of(WARNING, WARNING, WARNING, WARNING))) + : Stream.empty()); + } + + /** + * Verifies that the bug checker flags refactoring opportunities with the appropriate severity + * level. + * + * @implNote This test setup is rather cumbersome, because {@link CompilationTestHelper} does not + * enable direct assertions against the severity of collected diagnostics output. + */ + @MethodSource("severityAssignmentTestCases") + @ParameterizedTest + void severityAssignment( + ImmutableList arguments, ImmutableList expectedSeverities) { + assertThatThrownBy( + () -> + compilationHelper + .setArgs(arguments) + .addSourceLines( + "A.java", + "class A {", + " void m() {", + " boolean[] bs = {", + " \"foo\".toCharArray().length == 0,", + " \"bar\".toCharArray().length == 1,", + " \"baz\".toCharArray().length == 2,", + " \"qux\".toCharArray().length == 3", + " };", + " }", + "}") + .doTest()) + .isInstanceOf(AssertionError.class) + .message() + .satisfies( + message -> + assertThat(extractRefasterSeverities("A.java", message)) + .containsExactlyElementsOf(expectedSeverities)); + } + + private static ImmutableList extractRefasterSeverities( + String fileName, String message) { + return Pattern.compile( + String.format( + "/%s:(\\d+): (Note|warning|error): \\[Refaster Rule\\]", Pattern.quote(fileName))) + .matcher(message) + .results() + .sorted(comparingInt(r -> Integer.parseInt(r.group(1)))) + .map(r -> toSeverityLevel(r.group(2))) + .collect(toImmutableList()); + } + + private static SeverityLevel toSeverityLevel(String compilerDiagnosticsPrefix) { + switch (compilerDiagnosticsPrefix) { + case "Note": + return SUGGESTION; + case "warning": + return WARNING; + case "error": + return ERROR; + default: + throw new IllegalStateException( + String.format("Unrecognized diagnostics prefix '%s'", compilerDiagnosticsPrefix)); + } + } + + @Test + void replacement() { + refactoringTestHelper + .addInputLines( + "A.java", + "class A {", + " void m() {", + " boolean b1 = \"foo\".toCharArray().length == 0;", + " boolean b2 = \"bar\".toCharArray().length == 1;", + " boolean b3 = \"baz\".toCharArray().length == 2;", + " boolean b4 = \"qux\".toCharArray().length == 3;", + " }", + "}") + .addOutputLines( + "A.java", + "class A {", + " void m() {", + " boolean b1 = \"foo\".isEmpty();", + " boolean b2 = \"bar\".length() == 1;", + " boolean b3 = \"baz\".length() == 2;", + " boolean b4 = \"qux\".length() == 3;", + " }", + "}") + .doTest(TestMode.TEXT_MATCH); + } + + @Test + void restrictedReplacement() { + restrictedRefactoringTestHelper + .addInputLines( + "A.java", + "class A {", + " void m() {", + " boolean b1 = \"foo\".toCharArray().length == 0;", + " boolean b2 = \"bar\".toCharArray().length == 1;", + " boolean b3 = \"baz\".toCharArray().length == 2;", + " boolean b4 = \"qux\".toCharArray().length == 3;", + " }", + "}") + .addOutputLines( + "A.java", + "class A {", + " void m() {", + " boolean b1 = \"foo\".length() + 1 == 1;", + " boolean b2 = \"bar\".toCharArray().length == 1;", + " boolean b3 = \"baz\".length() == 2;", + " boolean b4 = \"qux\".toCharArray().length == 3;", + " }", + "}") + .doTest(TestMode.TEXT_MATCH); + } +} diff --git a/refaster-support/pom.xml b/refaster-support/pom.xml index 51680fc3f4..b8a1835630 100644 --- a/refaster-support/pom.xml +++ b/refaster-support/pom.xml @@ -46,6 +46,11 @@ error_prone_test_helpers test + + com.google.auto.value + auto-value-annotations + provided + com.google.code.findbugs jsr305 @@ -56,6 +61,11 @@ guava provided + + org.assertj + assertj-core + test + org.junit.jupiter junit-jupiter-api @@ -66,5 +76,15 @@ junit-jupiter-engine test + + org.junit.jupiter + junit-jupiter-params + test + + + org.mockito + mockito-core + test + diff --git a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/AnnotatedCompositeCodeTransformer.java b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/AnnotatedCompositeCodeTransformer.java new file mode 100644 index 0000000000..e43f0ea4c5 --- /dev/null +++ b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/AnnotatedCompositeCodeTransformer.java @@ -0,0 +1,152 @@ +package tech.picnic.errorprone.refaster; + +import static com.google.common.base.Preconditions.checkState; +import static com.google.errorprone.BugPattern.SeverityLevel.ERROR; +import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION; +import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; +import static tech.picnic.errorprone.refaster.annotation.OnlineDocumentation.NESTED_CLASS_URL_PLACEHOLDER; +import static tech.picnic.errorprone.refaster.annotation.OnlineDocumentation.TOP_LEVEL_CLASS_URL_PLACEHOLDER; + +import com.google.auto.value.AutoValue; +import com.google.common.base.Splitter; +import com.google.common.collect.Comparators; +import com.google.common.collect.ImmutableClassToInstanceMap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterators; +import com.google.errorprone.BugPattern.SeverityLevel; +import com.google.errorprone.CodeTransformer; +import com.google.errorprone.CompositeCodeTransformer; +import com.google.errorprone.DescriptionListener; +import com.google.errorprone.ErrorProneOptions; +import com.google.errorprone.matchers.Description; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.util.Context; +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.util.Iterator; +import java.util.Optional; +import java.util.function.Function; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +import tech.picnic.errorprone.refaster.annotation.Severity; + +/** + * A {@link CompositeCodeTransformer} that augments the {@link Description} of Refaster rule + * matches. + * + *

The content is augmented based on custom {@link tech.picnic.errorprone.refaster.annotation + * annotations} available on the matching {@link CodeTransformer} or on this {@link + * CompositeCodeTransformer} as a fallback, if any. + */ +@AutoValue +public abstract class AnnotatedCompositeCodeTransformer implements CodeTransformer, Serializable { + private static final long serialVersionUID = 1L; + private static final Splitter CLASS_NAME_SPLITTER = Splitter.on('.').limit(2); + + abstract String packageName(); + + abstract ImmutableList transformers(); + + @Override + public abstract ImmutableClassToInstanceMap annotations(); + + /** + * Creates an instance of an {@link AnnotatedCompositeCodeTransformer}. + * + * @param packageName The package in which the wrapped {@link CodeTransformer}s reside. + * @param transformers The {@link CodeTransformer}s to which to delegate. + * @param annotations The annotations that are applicable to this {@link CodeTransformer}. + * @return A non-{@code null} {@link AnnotatedCompositeCodeTransformer}. + */ + public static AnnotatedCompositeCodeTransformer create( + String packageName, + ImmutableList transformers, + ImmutableClassToInstanceMap annotations) { + return new AutoValue_AnnotatedCompositeCodeTransformer(packageName, transformers, annotations); + } + + @Override + public final void apply(TreePath path, Context context, DescriptionListener listener) { + for (CodeTransformer transformer : transformers()) { + transformer.apply( + path, + context, + description -> + listener.onDescribed(augmentDescription(description, transformer, context))); + } + } + + private Description augmentDescription( + Description description, CodeTransformer delegate, Context context) { + String shortCheckName = getShortCheckName(description.checkName); + return Description.builder( + description.position, + shortCheckName, + getLinkPattern(delegate, shortCheckName).orElse(null), + overrideSeverity(getSeverity(delegate), context), + getDescription(delegate)) + .addAllFixes(description.fixes) + .build(); + } + + private String getShortCheckName(String fullCheckName) { + if (packageName().isEmpty()) { + return fullCheckName; + } + + String prefix = packageName() + '.'; + checkState( + fullCheckName.startsWith(prefix), + "Refaster rule class '%s' is not located in package '%s'", + fullCheckName, + packageName()); + + return fullCheckName.substring(prefix.length()); + } + + private Optional getLinkPattern(CodeTransformer delegate, String checkName) { + Iterator nameComponents = CLASS_NAME_SPLITTER.splitToStream(checkName).iterator(); + return getAnnotationValue(OnlineDocumentation.class, OnlineDocumentation::value, delegate) + .map(url -> url.replace(TOP_LEVEL_CLASS_URL_PLACEHOLDER, nameComponents.next())) + .map( + url -> + url.replace(NESTED_CLASS_URL_PLACEHOLDER, Iterators.getNext(nameComponents, ""))); + } + + private SeverityLevel getSeverity(CodeTransformer delegate) { + /* + * The default severity should be kept in sync with the default severity of the + * `tech.picnic.errorprone.refaster.runner.Refaster` bug checker. (The associated + * `RefasterTest#severityAssignment` test verifies this invariant.) + */ + return getAnnotationValue(Severity.class, Severity::value, delegate).orElse(SUGGESTION); + } + + private String getDescription(CodeTransformer delegate) { + return getAnnotationValue( + tech.picnic.errorprone.refaster.annotation.Description.class, + tech.picnic.errorprone.refaster.annotation.Description::value, + delegate) + .orElse("Refactoring opportunity"); + } + + private Optional getAnnotationValue( + Class annotation, Function extractor, CodeTransformer delegate) { + return getAnnotationValue(delegate, annotation) + .or(() -> getAnnotationValue(this, annotation)) + .map(extractor); + } + + private static Optional getAnnotationValue( + CodeTransformer codeTransformer, Class annotation) { + return Optional.ofNullable(codeTransformer.annotations().getInstance(annotation)); + } + + private static SeverityLevel overrideSeverity(SeverityLevel severity, Context context) { + ErrorProneOptions options = context.get(ErrorProneOptions.class); + SeverityLevel minSeverity = + ErrorProneFork.isSuggestionsAsWarningsEnabled(options) ? WARNING : SUGGESTION; + SeverityLevel maxSeverity = options.isDropErrorsToWarnings() ? WARNING : ERROR; + + return Comparators.max(Comparators.min(severity, minSeverity), maxSeverity); + } +} diff --git a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/ErrorProneFork.java b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/ErrorProneFork.java new file mode 100644 index 0000000000..38d29c403c --- /dev/null +++ b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/ErrorProneFork.java @@ -0,0 +1,56 @@ +package tech.picnic.errorprone.refaster; + +import com.google.errorprone.ErrorProneOptions; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Optional; + +/** + * Utility class that enables the runtime to determine whether Picnic's fork of Error Prone is on + * the classpath. + * + * @see Picnic's Error Prone fork + */ +public final class ErrorProneFork { + private static final Optional ERROR_PRONE_OPTIONS_IS_SUGGESTIONS_AS_WARNINGS_METHOD = + Arrays.stream(ErrorProneOptions.class.getDeclaredMethods()) + .filter(m -> Modifier.isPublic(m.getModifiers())) + .filter(m -> "isSuggestionsAsWarnings".equals(m.getName())) + .findFirst(); + + private ErrorProneFork() {} + + /** + * Tells whether Picnic's fork of Error Prone is available. + * + * @return {@code true} iff classpath introspection indicates the presence of Error Prone + * modifications that are assumed to be present only in Picnic's fork. + */ + public static boolean isErrorProneForkAvailable() { + return ERROR_PRONE_OPTIONS_IS_SUGGESTIONS_AS_WARNINGS_METHOD.isPresent(); + } + + /** + * Tells whether the custom {@code -XepAllSuggestionsAsWarnings} flag is set. + * + * @param options The currently active Error Prone options. + * @return {@code true} iff {@link #isErrorProneForkAvailable() the Error Prone fork is available} + * and the aforementioned flag is set. + * @see google/error-prone#3301 + */ + public static boolean isSuggestionsAsWarningsEnabled(ErrorProneOptions options) { + return ERROR_PRONE_OPTIONS_IS_SUGGESTIONS_AS_WARNINGS_METHOD + .filter(m -> Boolean.TRUE.equals(invoke(m, options))) + .isPresent(); + } + + private static Object invoke(Method method, Object obj, Object... args) { + try { + return method.invoke(obj, args); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new IllegalStateException(String.format("Failed to invoke method '%s'", method), e); + } + } +} diff --git a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/annotation/Description.java b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/annotation/Description.java new file mode 100644 index 0000000000..84af8a5727 --- /dev/null +++ b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/annotation/Description.java @@ -0,0 +1,22 @@ +package tech.picnic.errorprone.refaster.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Describes the intent of a Refaster rule or group of Refaster rules. + * + *

Annotations on nested classes override the description associated with any enclosing class. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +public @interface Description { + /** + * A description of the annotated Refaster rule(s). + * + * @return A non-{@code null} string. + */ + String value(); +} diff --git a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/annotation/OnlineDocumentation.java b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/annotation/OnlineDocumentation.java new file mode 100644 index 0000000000..f41a2f1b67 --- /dev/null +++ b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/annotation/OnlineDocumentation.java @@ -0,0 +1,48 @@ +package tech.picnic.errorprone.refaster.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Signals that a Refaster rule or group of Refaster rules comes with online documentation. + * + *

The provided value may be a full URL, or a URL pattern containing either or both of the + * {@value TOP_LEVEL_CLASS_URL_PLACEHOLDER} and {@value NESTED_CLASS_URL_PLACEHOLDER} placeholders. + * + *

By default it is assumed that the Refaster rule(s) are documented on the Error Prone Support + * website. Annotations on nested classes override the documentation URL associated with any + * enclosing class. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +public @interface OnlineDocumentation { + /** + * The URL placeholder value that will be replaced with the name of the top-level class in which + * the annotated Refaster rule is located. + */ + String TOP_LEVEL_CLASS_URL_PLACEHOLDER = "${topLevelClassName}"; + /** + * The URL placeholder value that will be replaced with the name of the nested class in which the + * annotated Refaster rule is located, if applicable. + * + *

If the Refaster rule is not defined in a nested class then this placeholder will be replaced + * with the empty string. In case the Refaster rule is syntactically nested inside a deeper + * hierarchy of classes, then this placeholder will be replaced with concatenation of the names of + * all these classes (except the top-level class name), separated by dots. + */ + String NESTED_CLASS_URL_PLACEHOLDER = "${nestedClassName}"; + + /** + * The URL or URL pattern of the website at which the annotated Refaster rule(s) are documented. + * + * @return A non-{@code null} string, optionally containing the {@value + * TOP_LEVEL_CLASS_URL_PLACEHOLDER} and {@value NESTED_CLASS_URL_PLACEHOLDER} placeholders. + */ + String value() default + "https://error-prone.picnic.tech/refasterrules/" + + TOP_LEVEL_CLASS_URL_PLACEHOLDER + + '#' + + NESTED_CLASS_URL_PLACEHOLDER; +} diff --git a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/annotation/Severity.java b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/annotation/Severity.java new file mode 100644 index 0000000000..afa5a6c6e1 --- /dev/null +++ b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/annotation/Severity.java @@ -0,0 +1,25 @@ +package tech.picnic.errorprone.refaster.annotation; + +import com.google.errorprone.BugPattern.SeverityLevel; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Describes the severity of a Refaster rule or group of Refaster rules. + * + *

The default severity is the severity assigned to the {@code Refaster} bug checker, which may + * be controlled explicitly by running Error Prone with e.g. {@code -Xep:Refaster:WARN}. Annotations + * on nested classes override the severity associated with any enclosing class. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +public @interface Severity { + /** + * The expected severity of any match of the annotated Refaster rule(s). + * + * @return An Error Prone severity level. + */ + SeverityLevel value(); +} diff --git a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/annotation/package-info.java b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/annotation/package-info.java new file mode 100644 index 0000000000..53e8e88739 --- /dev/null +++ b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/annotation/package-info.java @@ -0,0 +1,8 @@ +/** + * A collection of annotations that can be placed on Refaster rule classes and Refaster rule + * collection classes, thus influencing the way in which associated rule matches are reported in + * non-patch mode. + */ +@com.google.errorprone.annotations.CheckReturnValue +@javax.annotation.ParametersAreNonnullByDefault +package tech.picnic.errorprone.refaster.annotation; diff --git a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/package-info.java b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/package-info.java new file mode 100644 index 0000000000..4955569986 --- /dev/null +++ b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/package-info.java @@ -0,0 +1,4 @@ +/** Assorted classes that aid the compilation or evaluation of Refaster rules. */ +@com.google.errorprone.annotations.CheckReturnValue +@javax.annotation.ParametersAreNonnullByDefault +package tech.picnic.errorprone.refaster; diff --git a/refaster-support/src/test/java/tech/picnic/errorprone/refaster/AnnotatedCompositeCodeTransformerTest.java b/refaster-support/src/test/java/tech/picnic/errorprone/refaster/AnnotatedCompositeCodeTransformerTest.java new file mode 100644 index 0000000000..d49dcf3006 --- /dev/null +++ b/refaster-support/src/test/java/tech/picnic/errorprone/refaster/AnnotatedCompositeCodeTransformerTest.java @@ -0,0 +1,204 @@ +package tech.picnic.errorprone.refaster; + +import static com.google.errorprone.BugPattern.SeverityLevel.ERROR; +import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION; +import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.auto.value.AutoAnnotation; +import com.google.common.collect.ImmutableClassToInstanceMap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.errorprone.BugPattern.SeverityLevel; +import com.google.errorprone.CodeTransformer; +import com.google.errorprone.DescriptionListener; +import com.google.errorprone.ErrorProneOptions; +import com.google.errorprone.fixes.Fix; +import com.google.errorprone.matchers.Description; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +import tech.picnic.errorprone.refaster.annotation.Severity; + +// XXX: Test the `ErrorProneOptions`-based severity override logic. (Right now that logic is tested +// through `RefasterTest`, but ideally it is covered by tests in this class, closer to the code that +// implements the relevant logic.) See the comment in `#context()` below. +final class AnnotatedCompositeCodeTransformerTest { + private static final DiagnosticPosition DUMMY_POSITION = mock(DiagnosticPosition.class); + private static final Fix DUMMY_FIX = mock(Fix.class); + private static final TreePath DUMMY_PATH = mock(TreePath.class); + private static final String DEFAULT_PACKAGE = ""; + private static final String CUSTOM_PACKAGE = "com.example"; + private static final String SIMPLE_CLASS_NAME = "MyRefasterRule"; + + private static Stream applyTestCases() { + /* { context, packageName, ruleName, compositeAnnotations, delegateAnnotations, expectedDescription } */ + return Stream.of( + arguments( + context(), + DEFAULT_PACKAGE, + SIMPLE_CLASS_NAME, + ImmutableSet.of(), + ImmutableSet.of(), + description( + SIMPLE_CLASS_NAME, Optional.empty(), SUGGESTION, "Refactoring opportunity")), + arguments( + context(), + CUSTOM_PACKAGE, + CUSTOM_PACKAGE + '.' + SIMPLE_CLASS_NAME, + ImmutableSet.of( + descriptionAnnotation("Composite description"), + documentationAnnotation("https://example.com"), + severityAnnotation(ERROR)), + ImmutableSet.of(), + description( + SIMPLE_CLASS_NAME, + Optional.of("https://example.com"), + ERROR, + "Composite description")), + arguments( + context(), + DEFAULT_PACKAGE, + SIMPLE_CLASS_NAME, + ImmutableSet.of(), + ImmutableSet.of( + descriptionAnnotation("Rule description"), + documentationAnnotation("https://example.com/rule/${topLevelClassName}"), + severityAnnotation(WARNING)), + description( + SIMPLE_CLASS_NAME, + Optional.of("https://example.com/rule/" + SIMPLE_CLASS_NAME), + WARNING, + "Rule description")), + arguments( + context(), + CUSTOM_PACKAGE, + CUSTOM_PACKAGE + '.' + SIMPLE_CLASS_NAME + ".SomeInnerClass.NestedEvenDeeper", + ImmutableSet.of( + descriptionAnnotation("Some description"), + documentationAnnotation("https://example.com"), + severityAnnotation(ERROR)), + ImmutableSet.of( + descriptionAnnotation("Overriding description"), + documentationAnnotation( + "https://example.com/rule/${topLevelClassName}/${nestedClassName}"), + severityAnnotation(SUGGESTION)), + description( + SIMPLE_CLASS_NAME + ".SomeInnerClass.NestedEvenDeeper", + Optional.of( + "https://example.com/rule/" + + SIMPLE_CLASS_NAME + + "/SomeInnerClass.NestedEvenDeeper"), + SUGGESTION, + "Overriding description"))); + } + + @MethodSource("applyTestCases") + @ParameterizedTest + void apply( + Context context, + String packageName, + String ruleName, + ImmutableSet compositeAnnotations, + ImmutableSet delegateAnnotations, + Description expectedDescription) { + CodeTransformer codeTransformer = + AnnotatedCompositeCodeTransformer.create( + packageName, + ImmutableList.of( + delegateCodeTransformer( + delegateAnnotations, context, refasterDescription(ruleName))), + indexAnnotations(compositeAnnotations)); + + List collected = new ArrayList<>(); + codeTransformer.apply(DUMMY_PATH, context, collected::add); + assertThat(collected) + .satisfiesExactly( + actual -> { + assertThat(actual.position).isEqualTo(expectedDescription.position); + assertThat(actual.checkName).isEqualTo(expectedDescription.checkName); + assertThat(actual.fixes).containsExactlyElementsOf(expectedDescription.fixes); + assertThat(actual.getLink()).isEqualTo(expectedDescription.getLink()); + assertThat(actual.getRawMessage()).isEqualTo(expectedDescription.getRawMessage()); + }); + } + + private static ImmutableClassToInstanceMap indexAnnotations( + ImmutableSet annotations) { + return ImmutableClassToInstanceMap.copyOf( + Maps.uniqueIndex(annotations, Annotation::annotationType)); + } + + private static CodeTransformer delegateCodeTransformer( + ImmutableSet annotations, + Context expectedContext, + Description returnedDescription) { + CodeTransformer codeTransformer = mock(CodeTransformer.class); + + when(codeTransformer.annotations()).thenReturn(indexAnnotations(annotations)); + doAnswer( + inv -> { + inv.getArgument(2).onDescribed(returnedDescription); + return null; + }) + .when(codeTransformer) + .apply(eq(DUMMY_PATH), eq(expectedContext), notNull()); + + return codeTransformer; + } + + /** + * Returns a {@link Description} with some default values as produced by {@link + * com.google.errorprone.refaster.RefasterScanner}. + */ + private static Description refasterDescription(String name) { + return description(name, Optional.of(""), WARNING, ""); + } + + private static Description description( + String name, Optional link, SeverityLevel severityLevel, String message) { + return Description.builder(DUMMY_POSITION, name, link.orElse(null), severityLevel, message) + .addFix(DUMMY_FIX) + .build(); + } + + private static Context context() { + // XXX: Use `ErrorProneOptions#processArgs` to test the + // `AnnotatedCompositeCodeTransformer#overrideSeverity` logic. + Context context = mock(Context.class); + when(context.get(ErrorProneOptions.class)).thenReturn(ErrorProneOptions.empty()); + return context; + } + + @AutoAnnotation + private static tech.picnic.errorprone.refaster.annotation.Description descriptionAnnotation( + String value) { + return new AutoAnnotation_AnnotatedCompositeCodeTransformerTest_descriptionAnnotation(value); + } + + @AutoAnnotation + private static OnlineDocumentation documentationAnnotation(String value) { + return new AutoAnnotation_AnnotatedCompositeCodeTransformerTest_documentationAnnotation(value); + } + + @AutoAnnotation + private static Severity severityAnnotation(SeverityLevel value) { + return new AutoAnnotation_AnnotatedCompositeCodeTransformerTest_severityAnnotation(value); + } +} diff --git a/refaster-support/src/test/java/tech/picnic/errorprone/refaster/ErrorProneForkTest.java b/refaster-support/src/test/java/tech/picnic/errorprone/refaster/ErrorProneForkTest.java new file mode 100644 index 0000000000..06b11b8501 --- /dev/null +++ b/refaster-support/src/test/java/tech/picnic/errorprone/refaster/ErrorProneForkTest.java @@ -0,0 +1,67 @@ +package tech.picnic.errorprone.refaster; + +import static com.google.errorprone.BugPattern.SeverityLevel.ERROR; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import com.google.errorprone.BugPattern; +import com.google.errorprone.CompilationTestHelper; +import com.google.errorprone.ErrorProneOptions; +import com.google.errorprone.VisitorState; +import com.google.errorprone.bugpatterns.BugChecker; +import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher; +import com.google.errorprone.matchers.Description; +import com.sun.source.tree.ClassTree; +import org.junit.jupiter.api.Test; + +final class ErrorProneForkTest { + @Test + void isErrorProneForkAvailable() { + assertThat(ErrorProneFork.isErrorProneForkAvailable()) + .isEqualTo(Boolean.TRUE.toString().equals(System.getProperty("error-prone-fork-in-use"))); + } + + @Test + void isSuggestionsAsWarningsEnabledWithoutFlag() { + CompilationTestHelper.newInstance(TestChecker.class, getClass()) + .addSourceLines( + "A.java", + "// BUG: Diagnostic contains: Suggestions as warnings enabled: false", + "class A {}") + .doTest(); + } + + @Test + void isSuggestionsAsWarningsEnabledWithFlag() { + assumeTrue( + ErrorProneFork.isErrorProneForkAvailable(), + "Picnic's Error Prone fork is not on the classpath"); + + CompilationTestHelper.newInstance(TestChecker.class, getClass()) + .setArgs("-XepAllSuggestionsAsWarnings") + .addSourceLines( + "A.java", + "// BUG: Diagnostic contains: Suggestions as warnings enabled: true", + "class A {}") + .doTest(); + } + + /** + * A {@link BugChecker} that reports the result of {@link + * ErrorProneFork#isSuggestionsAsWarningsEnabled(ErrorProneOptions)}. + */ + @BugPattern(summary = "Flags classes with a custom error message", severity = ERROR) + public static final class TestChecker extends BugChecker implements ClassTreeMatcher { + private static final long serialVersionUID = 1L; + + @Override + public Description matchClass(ClassTree tree, VisitorState state) { + return buildDescription(tree) + .setMessage( + String.format( + "Suggestions as warnings enabled: %s", + ErrorProneFork.isSuggestionsAsWarningsEnabled(state.errorProneOptions()))) + .build(); + } + } +} diff --git a/refaster-test-support/src/main/java/tech/picnic/errorprone/refaster/test/RefasterRuleCollection.java b/refaster-test-support/src/main/java/tech/picnic/errorprone/refaster/test/RefasterRuleCollection.java index ce01ec9919..daf2d52513 100644 --- a/refaster-test-support/src/main/java/tech/picnic/errorprone/refaster/test/RefasterRuleCollection.java +++ b/refaster-test-support/src/main/java/tech/picnic/errorprone/refaster/test/RefasterRuleCollection.java @@ -175,11 +175,11 @@ private static ImmutableRangeMap indexRuleMatches( ImmutableRangeMap.Builder ruleMatches = ImmutableRangeMap.builder(); for (Description description : matches) { + String ruleName = extractRefasterRuleName(description); Set replacements = Iterables.getOnlyElement(description.fixes).getReplacements(endPositions); for (Replacement replacement : replacements) { - ruleMatches.put( - replacement.range(), getSubstringAfterFinalDelimiter('.', description.checkName)); + ruleMatches.put(replacement.range(), ruleName); } } @@ -226,6 +226,13 @@ private void reportViolations( state.reportMatch(describeMatch(tree, fixWithComment)); } + private static String extractRefasterRuleName(Description description) { + String message = description.getRawMessage(); + int index = message.indexOf(':'); + checkState(index >= 0, "Failed to extract Refaster rule name from string '%s'", message); + return getSubstringAfterFinalDelimiter('.', message.substring(0, index)); + } + private static String getSubstringAfterFinalDelimiter(char delimiter, String value) { int index = value.lastIndexOf(delimiter); checkState(index >= 0, "String '%s' does not contain character '%s'", value, delimiter);