mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 15:49:33 +00:00
Compare commits
2 Commits
rossendrij
...
sschroever
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd9c248777 | ||
|
|
9954663bd5 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
with:
|
||||
java-version: ${{ matrix.jdk }}
|
||||
java-distribution: ${{ matrix.distribution }}
|
||||
maven-version: 3.9.9
|
||||
maven-version: 3.9.8
|
||||
- name: Display build environment details
|
||||
run: mvn --version
|
||||
- name: Build project against vanilla Error Prone, compile Javadoc
|
||||
|
||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
with:
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.9
|
||||
maven-version: 3.9.8
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
|
||||
with:
|
||||
|
||||
2
.github/workflows/pitest-analyze-pr.yml
vendored
2
.github/workflows/pitest-analyze-pr.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
checkout-fetch-depth: 2
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.9
|
||||
maven-version: 3.9.8
|
||||
- name: Run Pitest
|
||||
# By running with features `+GIT(from[HEAD~1]), +gitci`, Pitest only
|
||||
# analyzes lines changed in the associated pull request, as GitHub
|
||||
|
||||
2
.github/workflows/pitest-update-pr.yml
vendored
2
.github/workflows/pitest-update-pr.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
with:
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.9
|
||||
maven-version: 3.9.8
|
||||
- name: Download Pitest analysis artifact
|
||||
uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4
|
||||
with:
|
||||
|
||||
2
.github/workflows/run-integration-tests.yml
vendored
2
.github/workflows/run-integration-tests.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
checkout-ref: "refs/pull/${{ github.event.issue.number }}/head"
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.9
|
||||
maven-version: 3.9.8
|
||||
- name: Install project to local Maven repository
|
||||
run: mvn -T1C install -DskipTests -Dverification.skip
|
||||
- name: Run integration test
|
||||
|
||||
2
.github/workflows/sonarcloud.yml
vendored
2
.github/workflows/sonarcloud.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
checkout-fetch-depth: 0
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.9
|
||||
maven-version: 3.9.8
|
||||
- name: Create missing `test` directory
|
||||
# XXX: Drop this step in favour of actually having a test.
|
||||
run: mkdir refaster-compiler/src/test
|
||||
|
||||
@@ -67,11 +67,6 @@
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto.service</groupId>
|
||||
<artifactId>auto-service-annotations</artifactId>
|
||||
|
||||
@@ -18,12 +18,14 @@ import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
import com.sun.source.tree.LiteralTree;
|
||||
import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import tech.picnic.errorprone.refaster.matchers.RequiresComputation;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
@@ -34,12 +36,12 @@ import tech.picnic.errorprone.utils.SourceCode;
|
||||
* it does, the suggested fix changes the program's semantics. Such fragile code must instead be
|
||||
* refactored such that the side-effectful code does not appear accidental.
|
||||
*/
|
||||
// XXX: This rule may introduce a compilation error: the `value` expression may reference a
|
||||
// non-effectively final variable, which is not allowed in the replacement lambda expression.
|
||||
// Review whether a `@Matcher` can be used to avoid this.
|
||||
// XXX: Once the `MethodReferenceUsageCheck` bug checker becomes generally usable, consider leaving
|
||||
// the method reference cleanup to that check, and express the remainder of the logic in this class
|
||||
// using a Refaster template, i.c.w. a `@NotMatches(RequiresComputation.class)` constraint.
|
||||
// XXX: Consider also implementing the inverse, in which `.orElseGet(() -> someConstant)` is
|
||||
// flagged.
|
||||
// XXX: Once the `MethodReferenceUsageCheck` becomes generally usable, consider leaving the method
|
||||
// reference cleanup to that check, and express the remainder of the logic in this class using a
|
||||
// Refaster template, i.c.w. a `@Matches` constraint that implements the `requiresComputation`
|
||||
// logic.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
@@ -49,17 +51,16 @@ import tech.picnic.errorprone.utils.SourceCode;
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = PERFORMANCE)
|
||||
public final class OptionalOrElseGet extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
public final class OptionalOrElse extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> REQUIRES_COMPUTATION = new RequiresComputation();
|
||||
private static final Matcher<ExpressionTree> OPTIONAL_OR_ELSE_METHOD =
|
||||
instanceMethod().onExactClass(Optional.class.getCanonicalName()).namedAnyOf("orElse");
|
||||
// XXX: Also exclude invocations of `@Placeholder`-annotated methods.
|
||||
private static final Matcher<ExpressionTree> REFASTER_METHOD =
|
||||
staticMethod().onClass(Refaster.class.getCanonicalName());
|
||||
|
||||
/** Instantiates a new {@link OptionalOrElseGet} instance. */
|
||||
public OptionalOrElseGet() {}
|
||||
/** Instantiates a new {@link OptionalOrElse} instance. */
|
||||
public OptionalOrElse() {}
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
@@ -68,8 +69,7 @@ public final class OptionalOrElseGet extends BugChecker implements MethodInvocat
|
||||
}
|
||||
|
||||
ExpressionTree argument = Iterables.getOnlyElement(tree.getArguments());
|
||||
if (!REQUIRES_COMPUTATION.matches(argument, state)
|
||||
|| REFASTER_METHOD.matches(argument, state)) {
|
||||
if (!requiresComputation(argument) || REFASTER_METHOD.matches(argument, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
@@ -91,6 +91,18 @@ public final class OptionalOrElseGet extends BugChecker implements MethodInvocat
|
||||
return describeMatch(tree, fix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the given expression contains anything other than a literal or a (possibly
|
||||
* dereferenced) variable or constant.
|
||||
*/
|
||||
private static boolean requiresComputation(ExpressionTree tree) {
|
||||
return !(tree instanceof IdentifierTree
|
||||
|| tree instanceof LiteralTree
|
||||
|| (tree instanceof MemberSelectTree memberSelect
|
||||
&& !requiresComputation(memberSelect.getExpression()))
|
||||
|| ASTHelpers.constValue(tree) != null);
|
||||
}
|
||||
|
||||
/** Returns the nullary method reference matching the given expression, if any. */
|
||||
private static Optional<String> tryMethodReferenceConversion(
|
||||
ExpressionTree tree, VisitorState state) {
|
||||
@@ -106,7 +118,7 @@ public final class OptionalOrElseGet extends BugChecker implements MethodInvocat
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (REQUIRES_COMPUTATION.matches(memberSelect.getExpression(), state)) {
|
||||
if (requiresComputation(memberSelect.getExpression())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Function;
|
||||
@@ -245,33 +244,6 @@ final class ComparatorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Collections#sort(List)} over more verbose alternatives. */
|
||||
static final class CollectionsSort<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
void before(List<T> collection) {
|
||||
Collections.sort(collection, naturalOrder());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
void after(List<T> collection) {
|
||||
Collections.sort(collection);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Collections#min(Collection)} over more verbose alternatives. */
|
||||
static final class CollectionsMin<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
T before(Collection<T> collection) {
|
||||
return Refaster.anyOf(
|
||||
Collections.min(collection, naturalOrder()), Collections.max(collection, reverseOrder()));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(Collection<T> collection) {
|
||||
return Collections.min(collection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid unnecessary creation of a {@link Stream} to determine the minimum of a known collection
|
||||
* of values.
|
||||
@@ -292,7 +264,7 @@ final class ComparatorRules {
|
||||
* Avoid unnecessary creation of a {@link Stream} to determine the minimum of a known collection
|
||||
* of values.
|
||||
*/
|
||||
static final class CollectionsMinWithComparator<S, T extends S> {
|
||||
static final class MinOfCollection<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
T before(Collection<T> collection, Comparator<S> cmp) {
|
||||
return collection.stream().min(cmp).orElseThrow();
|
||||
@@ -371,20 +343,6 @@ final class ComparatorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Collections#max(Collection)} over more verbose alternatives. */
|
||||
static final class CollectionsMax<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
T before(Collection<T> collection) {
|
||||
return Refaster.anyOf(
|
||||
Collections.max(collection, naturalOrder()), Collections.min(collection, reverseOrder()));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(Collection<T> collection) {
|
||||
return Collections.max(collection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid unnecessary creation of a {@link Stream} to determine the maximum of a known collection
|
||||
* of values.
|
||||
@@ -405,7 +363,7 @@ final class ComparatorRules {
|
||||
* Avoid unnecessary creation of a {@link Stream} to determine the maximum of a known collection
|
||||
* of values.
|
||||
*/
|
||||
static final class CollectionsMaxWithComparator<S, T extends S> {
|
||||
static final class MaxOfCollection<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
T before(Collection<T> collection, Comparator<S> cmp) {
|
||||
return collection.stream().max(cmp).orElseThrow();
|
||||
|
||||
@@ -2,6 +2,7 @@ package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
@@ -11,6 +12,10 @@ import com.google.errorprone.refaster.annotation.MayOptionallyUse;
|
||||
import com.google.errorprone.refaster.annotation.NotMatches;
|
||||
import com.google.errorprone.refaster.annotation.Placeholder;
|
||||
import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
@@ -20,13 +25,30 @@ import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
import tech.picnic.errorprone.refaster.matchers.RequiresComputation;
|
||||
import tech.picnic.errorprone.refaster.matchers.IsLikelyTrivialComputation;
|
||||
|
||||
/** Refaster rules related to expressions dealing with {@link Optional}s. */
|
||||
@OnlineDocumentation
|
||||
final class OptionalRules {
|
||||
private OptionalRules() {}
|
||||
|
||||
// XXX: Move or drop.
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface BeforeExample {}
|
||||
|
||||
// XXX: Move or drop.
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface AfterExample {}
|
||||
|
||||
// XXX: Move or drop.
|
||||
interface RefasterExpressionExamples<T> {
|
||||
ImmutableList<T> before();
|
||||
|
||||
ImmutableList<T> after();
|
||||
}
|
||||
|
||||
/** Prefer {@link Optional#empty()} over the more contrived alternative. */
|
||||
static final class OptionalEmpty<T> {
|
||||
@BeforeTemplate
|
||||
@@ -53,6 +75,40 @@ final class OptionalRules {
|
||||
Optional<T> after(T object) {
|
||||
return Optional.ofNullable(object);
|
||||
}
|
||||
|
||||
@BeforeExample
|
||||
@SuppressWarnings({
|
||||
"TernaryOperatorOptionalNegativeFiltering",
|
||||
"TernaryOperatorOptionalPositiveFiltering"
|
||||
} /* Special cases. */)
|
||||
ImmutableList<Optional<String>> input() {
|
||||
return ImmutableList.of(
|
||||
toString() == null ? Optional.empty() : Optional.of(toString()),
|
||||
toString() != null ? Optional.of(toString()) : Optional.empty());
|
||||
}
|
||||
|
||||
@AfterExample
|
||||
ImmutableList<Optional<String>> output() {
|
||||
return ImmutableList.of(Optional.ofNullable(toString()), Optional.ofNullable(toString()));
|
||||
}
|
||||
|
||||
static final class Examples implements RefasterExpressionExamples<Optional<String>> {
|
||||
@Override
|
||||
@SuppressWarnings({
|
||||
"TernaryOperatorOptionalNegativeFiltering",
|
||||
"TernaryOperatorOptionalPositiveFiltering"
|
||||
} /* Special cases. */)
|
||||
public ImmutableList<Optional<String>> before() {
|
||||
return ImmutableList.of(
|
||||
toString() == null ? Optional.empty() : Optional.of(toString()),
|
||||
toString() != null ? Optional.of(toString()) : Optional.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<Optional<String>> after() {
|
||||
return ImmutableList.of(Optional.ofNullable(toString()), Optional.ofNullable(toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Optional#isEmpty()} over the more verbose alternative. */
|
||||
@@ -66,6 +122,16 @@ final class OptionalRules {
|
||||
boolean after(Optional<T> optional) {
|
||||
return optional.isEmpty();
|
||||
}
|
||||
|
||||
@BeforeExample
|
||||
ImmutableList<Boolean> input() {
|
||||
return ImmutableList.of(!Optional.empty().isPresent(), !Optional.of("foo").isPresent());
|
||||
}
|
||||
|
||||
@AfterExample
|
||||
ImmutableList<Boolean> output() {
|
||||
return ImmutableList.of(Optional.empty().isEmpty(), Optional.of("foo").isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Optional#isPresent()} over the inverted alternative. */
|
||||
@@ -79,6 +145,16 @@ final class OptionalRules {
|
||||
boolean after(Optional<T> optional) {
|
||||
return optional.isPresent();
|
||||
}
|
||||
|
||||
@BeforeExample
|
||||
ImmutableList<Boolean> input() {
|
||||
return ImmutableList.of(!Optional.empty().isEmpty(), !Optional.of("foo").isEmpty());
|
||||
}
|
||||
|
||||
@AfterExample
|
||||
ImmutableList<Boolean> output() {
|
||||
return ImmutableList.of(Optional.empty().isPresent(), Optional.of("foo").isPresent());
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Optional#orElseThrow()} over the less explicit {@link Optional#get()}. */
|
||||
@@ -255,21 +331,24 @@ final class OptionalRules {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Optional#orElse(Object)} over {@link Optional#orElseGet(Supplier)} if the
|
||||
* fallback value does not require non-trivial computation.
|
||||
* Prefer {@link Optional#orElseGet(Supplier)} over {@link Optional#orElse(Object)} if the
|
||||
* fallback value is not the result of a trivial computation.
|
||||
*/
|
||||
// XXX: This rule is the counterpart to the `OptionalOrElseGet` bug checker. Once the
|
||||
// `MethodReferenceUsage` bug checker is "production ready", that bug checker may similarly be
|
||||
// replaced with a Refaster rule.
|
||||
static final class OptionalOrElse<T> {
|
||||
// XXX: This rule may introduce a compilation error: the `value` expression may reference a
|
||||
// non-effectively final variable, which is not allowed in the replacement lambda expression.
|
||||
// Review whether a `@Matcher` can be used to avoid this.
|
||||
// XXX: Once `MethodReferenceUsage` is "production ready", replace
|
||||
// `@NotMatches(IsLikelyTrivialComputation.class)` with `@Matches(RequiresComputation.class)` (and
|
||||
// reimplement the matcher accordingly).
|
||||
static final class OptionalOrElseGet<T> {
|
||||
@BeforeTemplate
|
||||
T before(Optional<T> optional, @NotMatches(RequiresComputation.class) T value) {
|
||||
return optional.orElseGet(() -> value);
|
||||
T before(Optional<T> optional, @NotMatches(IsLikelyTrivialComputation.class) T value) {
|
||||
return optional.orElse(value);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(Optional<T> optional, T value) {
|
||||
return optional.orElse(value);
|
||||
return optional.orElseGet(() -> value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,12 +449,7 @@ final class OptionalRules {
|
||||
/** Prefer {@link Optional#or(Supplier)} over more verbose alternatives. */
|
||||
static final class OptionalOrOtherOptional<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings({
|
||||
"LexicographicalAnnotationAttributeListing" /* `key-*` entry must remain last. */,
|
||||
"NestedOptionals" /* This violation will be rewritten. */,
|
||||
"OptionalOrElse" /* Parameters represent expressions that may require computation. */,
|
||||
"key-to-resolve-AnnotationUseStyle-and-TrailingComment-check-conflict"
|
||||
})
|
||||
@SuppressWarnings("NestedOptionals")
|
||||
Optional<T> before(Optional<T> optional1, Optional<T> optional2) {
|
||||
// XXX: Note that rewriting the first and third variant will change the code's behavior if
|
||||
// `optional2` has side-effects.
|
||||
|
||||
@@ -8,8 +8,6 @@ import com.google.common.primitives.Floats;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.google.common.primitives.Shorts;
|
||||
import com.google.common.primitives.UnsignedInts;
|
||||
import com.google.common.primitives.UnsignedLongs;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.AlsoNegation;
|
||||
@@ -78,8 +76,6 @@ final class PrimitiveRules {
|
||||
}
|
||||
|
||||
/** Prefer {@link Math#toIntExact(long)} over the Guava alternative. */
|
||||
// XXX: This rule changes the exception possibly thrown from `IllegalArgumentException` to
|
||||
// `ArithmeticException`.
|
||||
static final class LongToIntExact {
|
||||
@BeforeTemplate
|
||||
int before(long l) {
|
||||
@@ -196,8 +192,72 @@ final class PrimitiveRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Boolean#compare(boolean, boolean)} over the Guava alternative. */
|
||||
static final class BooleanCompare {
|
||||
@BeforeTemplate
|
||||
int before(boolean a, boolean b) {
|
||||
return Booleans.compare(a, b);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(boolean a, boolean b) {
|
||||
return Boolean.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Character#compare(char, char)} over the Guava alternative. */
|
||||
static final class CharacterCompare {
|
||||
@BeforeTemplate
|
||||
int before(char a, char b) {
|
||||
return Chars.compare(a, b);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(char a, char b) {
|
||||
return Character.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Short#compare(short, short)} over the Guava alternative. */
|
||||
static final class ShortCompare {
|
||||
@BeforeTemplate
|
||||
int before(short a, short b) {
|
||||
return Shorts.compare(a, b);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(short a, short b) {
|
||||
return Short.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Integer#compare(int, int)} over the Guava alternative. */
|
||||
static final class IntegerCompare {
|
||||
@BeforeTemplate
|
||||
int before(int a, int b) {
|
||||
return Ints.compare(a, b);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(int a, int b) {
|
||||
return Integer.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Long#compare(long, long)} over the Guava alternative. */
|
||||
static final class LongCompare {
|
||||
@BeforeTemplate
|
||||
int before(long a, long b) {
|
||||
return Longs.compare(a, b);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(long a, long b) {
|
||||
return Long.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Float#compare(float, float)} over the Guava alternative. */
|
||||
// XXX: Drop this rule once https://github.com/google/guava/pull/7371 is released.
|
||||
static final class FloatCompare {
|
||||
@BeforeTemplate
|
||||
int before(float a, float b) {
|
||||
@@ -211,7 +271,6 @@ final class PrimitiveRules {
|
||||
}
|
||||
|
||||
/** Prefer {@link Double#compare(double, double)} over the Guava alternative. */
|
||||
// XXX: Drop this rule once https://github.com/google/guava/pull/7371 is released.
|
||||
static final class DoubleCompare {
|
||||
@BeforeTemplate
|
||||
int before(double a, double b) {
|
||||
@@ -383,205 +442,4 @@ final class PrimitiveRules {
|
||||
return Long.signum(l) == -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Integer#compareUnsigned(int, int)} over third-party alternatives. */
|
||||
static final class IntegerCompareUnsigned {
|
||||
@BeforeTemplate
|
||||
int before(int x, int y) {
|
||||
return UnsignedInts.compare(x, y);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(int x, int y) {
|
||||
return Integer.compareUnsigned(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Long#compareUnsigned(long, long)} over third-party alternatives. */
|
||||
static final class LongCompareUnsigned {
|
||||
@BeforeTemplate
|
||||
long before(long x, long y) {
|
||||
return UnsignedLongs.compare(x, y);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
long after(long x, long y) {
|
||||
return Long.compareUnsigned(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Integer#divideUnsigned(int, int)} over third-party alternatives. */
|
||||
static final class IntegerDivideUnsigned {
|
||||
@BeforeTemplate
|
||||
int before(int x, int y) {
|
||||
return UnsignedInts.divide(x, y);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(int x, int y) {
|
||||
return Integer.divideUnsigned(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Long#divideUnsigned(long, long)} over third-party alternatives. */
|
||||
static final class LongDivideUnsigned {
|
||||
@BeforeTemplate
|
||||
long before(long x, long y) {
|
||||
return UnsignedLongs.divide(x, y);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
long after(long x, long y) {
|
||||
return Long.divideUnsigned(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Integer#remainderUnsigned(int, int)} over third-party alternatives. */
|
||||
static final class IntegerRemainderUnsigned {
|
||||
@BeforeTemplate
|
||||
int before(int x, int y) {
|
||||
return UnsignedInts.remainder(x, y);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(int x, int y) {
|
||||
return Integer.remainderUnsigned(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Long#remainderUnsigned(long, long)} over third-party alternatives. */
|
||||
static final class LongRemainderUnsigned {
|
||||
@BeforeTemplate
|
||||
long before(long x, long y) {
|
||||
return UnsignedLongs.remainder(x, y);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
long after(long x, long y) {
|
||||
return Long.remainderUnsigned(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer JDK's {@link Integer#parseUnsignedInt(String)} over third-party or more verbose
|
||||
* alternatives.
|
||||
*/
|
||||
static final class IntegerParseUnsignedInt {
|
||||
@BeforeTemplate
|
||||
int before(String string) {
|
||||
return Refaster.anyOf(
|
||||
UnsignedInts.parseUnsignedInt(string), Integer.parseUnsignedInt(string, 10));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(String string) {
|
||||
return Integer.parseUnsignedInt(string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer JDK's {@link Long#parseUnsignedLong(String)} over third-party or more verbose
|
||||
* alternatives.
|
||||
*/
|
||||
static final class LongParseUnsignedLong {
|
||||
@BeforeTemplate
|
||||
long before(String string) {
|
||||
return Refaster.anyOf(
|
||||
UnsignedLongs.parseUnsignedLong(string), Long.parseUnsignedLong(string, 10));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
long after(String string) {
|
||||
return Long.parseUnsignedLong(string);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Integer#parseUnsignedInt(String, int)} over third-party alternatives. */
|
||||
static final class IntegerParseUnsignedIntWithRadix {
|
||||
@BeforeTemplate
|
||||
int before(String string, int radix) {
|
||||
return UnsignedInts.parseUnsignedInt(string, radix);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(String string, int radix) {
|
||||
return Integer.parseUnsignedInt(string, radix);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Long#parseUnsignedLong(String, int)} over third-party alternatives. */
|
||||
static final class LongParseUnsignedLongWithRadix {
|
||||
@BeforeTemplate
|
||||
long before(String string, int radix) {
|
||||
return UnsignedLongs.parseUnsignedLong(string, radix);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
long after(String string, int radix) {
|
||||
return Long.parseUnsignedLong(string, radix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer JDK's {@link Integer#toUnsignedString(int)} over third-party or more verbose
|
||||
* alternatives.
|
||||
*/
|
||||
static final class IntegerToUnsignedString {
|
||||
@BeforeTemplate
|
||||
String before(int i) {
|
||||
return Refaster.anyOf(UnsignedInts.toString(i), Integer.toUnsignedString(i, 10));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(int i) {
|
||||
return Integer.toUnsignedString(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer JDK's {@link Long#toUnsignedString(long)} over third-party or more verbose alternatives.
|
||||
*/
|
||||
static final class LongToUnsignedString {
|
||||
@BeforeTemplate
|
||||
String before(long i) {
|
||||
return Refaster.anyOf(UnsignedLongs.toString(i), Long.toUnsignedString(i, 10));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(long i) {
|
||||
return Long.toUnsignedString(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer JDK's {@link Integer#toUnsignedString(int,int)} over third-party or more verbose
|
||||
* alternatives.
|
||||
*/
|
||||
static final class IntegerToUnsignedStringWithRadix {
|
||||
@BeforeTemplate
|
||||
String before(int i, int radix) {
|
||||
return UnsignedInts.toString(i, radix);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(int i, int radix) {
|
||||
return Integer.toUnsignedString(i, radix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer JDK's {@link Long#toUnsignedString(long,int)} over third-party or more verbose
|
||||
* alternatives.
|
||||
*/
|
||||
static final class LongToUnsignedStringWithRadix {
|
||||
@BeforeTemplate
|
||||
String before(long i, int radix) {
|
||||
return UnsignedLongs.toString(i, radix);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(long i, int radix) {
|
||||
return Long.toUnsignedString(i, radix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import static java.util.stream.Collectors.toCollection;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static reactor.function.TupleUtils.function;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -1957,22 +1956,6 @@ final class ReactorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't propagate {@link Mono} cancellations to an upstream cache value computation, as
|
||||
* completion of such computations may benefit concurrent or subsequent cache usages.
|
||||
*/
|
||||
static final class MonoFromFutureAsyncLoadingCacheGet<K, V> {
|
||||
@BeforeTemplate
|
||||
Mono<V> before(AsyncLoadingCache<K, V> cache, K key) {
|
||||
return Mono.fromFuture(() -> cache.get(key));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<V> after(AsyncLoadingCache<K, V> cache, K key) {
|
||||
return Mono.fromFuture(() -> cache.get(key), /* suppressCancel= */ true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Flux#fromStream(Supplier)} over {@link Flux#fromStream(Stream)}, as the former
|
||||
* yields a {@link Flux} that is more likely to behave as expected when subscribed to more than
|
||||
|
||||
@@ -244,11 +244,4 @@ final class StringRules {
|
||||
return Utf8.encodedLength(str);
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite `String#copyValueOf(char[])` to `new String(char[])`.
|
||||
// Example:
|
||||
// String.copyValueOf(new char[]);
|
||||
// -->
|
||||
// new String(new char[]);
|
||||
|
||||
}
|
||||
|
||||
@@ -5,15 +5,14 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class OptionalOrElseGetTest {
|
||||
final class OptionalOrElseTest {
|
||||
@Test
|
||||
void identification() {
|
||||
CompilationTestHelper.newInstance(OptionalOrElseGet.class, getClass())
|
||||
CompilationTestHelper.newInstance(OptionalOrElse.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import com.google.errorprone.refaster.Refaster;",
|
||||
"import java.util.Optional;",
|
||||
"import java.util.function.Supplier;",
|
||||
"",
|
||||
"class A {",
|
||||
" private final Optional<Object> optional = Optional.empty();",
|
||||
@@ -28,7 +27,6 @@ final class OptionalOrElseGetTest {
|
||||
" optional.orElse(string);",
|
||||
" optional.orElse(this.string);",
|
||||
" optional.orElse(Refaster.anyOf(\"constant\", \"another\"));",
|
||||
" Optional.<Supplier<String>>empty().orElse(() -> \"constant\");",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Optional.empty().orElse(string + \"constant\");",
|
||||
@@ -69,7 +67,7 @@ final class OptionalOrElseGetTest {
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
BugCheckerRefactoringTestHelper.newInstance(OptionalOrElseGet.class, getClass())
|
||||
BugCheckerRefactoringTestHelper.newInstance(OptionalOrElse.class, getClass())
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import java.util.Optional;",
|
||||
@@ -107,21 +107,11 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Comparator.<String>reverseOrder().compare("baz", "qux"));
|
||||
}
|
||||
|
||||
void testCollectionsSort() {
|
||||
Collections.sort(ImmutableList.of("foo", "bar"), naturalOrder());
|
||||
}
|
||||
|
||||
ImmutableSet<String> testCollectionsMin() {
|
||||
return ImmutableSet.of(
|
||||
Collections.min(ImmutableList.of("foo"), naturalOrder()),
|
||||
Collections.max(ImmutableList.of("bar"), reverseOrder()));
|
||||
}
|
||||
|
||||
String testMinOfArray() {
|
||||
return Arrays.stream(new String[0]).min(naturalOrder()).orElseThrow();
|
||||
}
|
||||
|
||||
String testCollectionsMinWithComparator() {
|
||||
String testMinOfCollection() {
|
||||
return ImmutableSet.of("foo", "bar").stream().min(naturalOrder()).orElseThrow();
|
||||
}
|
||||
|
||||
@@ -153,17 +143,11 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Collections.min(ImmutableSet.of("a", "b"), (a, b) -> 1));
|
||||
}
|
||||
|
||||
ImmutableSet<String> testCollectionsMax() {
|
||||
return ImmutableSet.of(
|
||||
Collections.max(ImmutableList.of("foo"), naturalOrder()),
|
||||
Collections.min(ImmutableList.of("bar"), reverseOrder()));
|
||||
}
|
||||
|
||||
String testMaxOfArray() {
|
||||
return Arrays.stream(new String[0]).max(naturalOrder()).orElseThrow();
|
||||
}
|
||||
|
||||
String testCollectionsMaxWithComparator() {
|
||||
String testMaxOfCollection() {
|
||||
return ImmutableSet.of("foo", "bar").stream().max(naturalOrder()).orElseThrow();
|
||||
}
|
||||
|
||||
|
||||
@@ -98,20 +98,11 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of("foo".compareTo("bar"), "qux".compareTo("baz"));
|
||||
}
|
||||
|
||||
void testCollectionsSort() {
|
||||
Collections.sort(ImmutableList.of("foo", "bar"));
|
||||
}
|
||||
|
||||
ImmutableSet<String> testCollectionsMin() {
|
||||
return ImmutableSet.of(
|
||||
Collections.min(ImmutableList.of("foo")), Collections.min(ImmutableList.of("bar")));
|
||||
}
|
||||
|
||||
String testMinOfArray() {
|
||||
return Collections.min(Arrays.asList(new String[0]), naturalOrder());
|
||||
}
|
||||
|
||||
String testCollectionsMinWithComparator() {
|
||||
String testMinOfCollection() {
|
||||
return Collections.min(ImmutableSet.of("foo", "bar"), naturalOrder());
|
||||
}
|
||||
|
||||
@@ -143,16 +134,11 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Comparators.min("a", "b", (a, b) -> 1));
|
||||
}
|
||||
|
||||
ImmutableSet<String> testCollectionsMax() {
|
||||
return ImmutableSet.of(
|
||||
Collections.max(ImmutableList.of("foo")), Collections.max(ImmutableList.of("bar")));
|
||||
}
|
||||
|
||||
String testMaxOfArray() {
|
||||
return Collections.max(Arrays.asList(new String[0]), naturalOrder());
|
||||
}
|
||||
|
||||
String testCollectionsMaxWithComparator() {
|
||||
String testMaxOfCollection() {
|
||||
return Collections.max(ImmutableSet.of("foo", "bar"), naturalOrder());
|
||||
}
|
||||
|
||||
|
||||
@@ -83,9 +83,11 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return Optional.of("foo").orElseGet(() -> Optional.of("bar").orElseThrow());
|
||||
}
|
||||
|
||||
ImmutableSet<String> testOptionalOrElse() {
|
||||
ImmutableSet<String> testOptionalOrElseGet() {
|
||||
return ImmutableSet.of(
|
||||
Optional.of("foo").orElseGet(() -> "bar"), Optional.of("baz").orElseGet(() -> toString()));
|
||||
Optional.of("foo").orElse("bar"),
|
||||
Optional.of("baz").orElse(toString()),
|
||||
Optional.of("qux").orElse(String.valueOf(true)));
|
||||
}
|
||||
|
||||
ImmutableSet<Object> testStreamFlatMapOptional() {
|
||||
|
||||
@@ -80,9 +80,11 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return Optional.of("foo").or(() -> Optional.of("bar")).orElseThrow();
|
||||
}
|
||||
|
||||
ImmutableSet<String> testOptionalOrElse() {
|
||||
ImmutableSet<String> testOptionalOrElseGet() {
|
||||
return ImmutableSet.of(
|
||||
Optional.of("foo").orElse("bar"), Optional.of("baz").orElseGet(() -> toString()));
|
||||
Optional.of("foo").orElse("bar"),
|
||||
Optional.of("baz").orElse(toString()),
|
||||
Optional.of("qux").orElseGet(() -> String.valueOf(true)));
|
||||
}
|
||||
|
||||
ImmutableSet<Object> testStreamFlatMapOptional() {
|
||||
|
||||
@@ -9,8 +9,6 @@ import com.google.common.primitives.Floats;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.google.common.primitives.Shorts;
|
||||
import com.google.common.primitives.UnsignedInts;
|
||||
import com.google.common.primitives.UnsignedLongs;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
|
||||
@@ -24,9 +22,7 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Floats.class,
|
||||
Ints.class,
|
||||
Longs.class,
|
||||
Shorts.class,
|
||||
UnsignedInts.class,
|
||||
UnsignedLongs.class);
|
||||
Shorts.class);
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testLessThan() {
|
||||
@@ -109,6 +105,26 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return Doubles.hashCode(1);
|
||||
}
|
||||
|
||||
int testBooleanCompare() {
|
||||
return Booleans.compare(false, true);
|
||||
}
|
||||
|
||||
int testCharacterCompare() {
|
||||
return Chars.compare('a', 'b');
|
||||
}
|
||||
|
||||
int testShortCompare() {
|
||||
return Shorts.compare((short) 1, (short) 2);
|
||||
}
|
||||
|
||||
int testIntegerCompare() {
|
||||
return Ints.compare(1, 2);
|
||||
}
|
||||
|
||||
int testLongCompare() {
|
||||
return Longs.compare(1, 2);
|
||||
}
|
||||
|
||||
int testFloatCompare() {
|
||||
return Floats.compare(1, 2);
|
||||
}
|
||||
@@ -174,60 +190,4 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(
|
||||
Long.signum(1L) < 0, Long.signum(2L) <= -1, Long.signum(3L) >= 0, Long.signum(4L) > -1);
|
||||
}
|
||||
|
||||
int testIntegerCompareUnsigned() {
|
||||
return UnsignedInts.compare(1, 2);
|
||||
}
|
||||
|
||||
long testLongCompareUnsigned() {
|
||||
return UnsignedLongs.compare(1, 2);
|
||||
}
|
||||
|
||||
int testIntegerDivideUnsigned() {
|
||||
return UnsignedInts.divide(1, 2);
|
||||
}
|
||||
|
||||
long testLongDivideUnsigned() {
|
||||
return UnsignedLongs.divide(1, 2);
|
||||
}
|
||||
|
||||
int testIntegerRemainderUnsigned() {
|
||||
return UnsignedInts.remainder(1, 2);
|
||||
}
|
||||
|
||||
long testLongRemainderUnsigned() {
|
||||
return UnsignedLongs.remainder(1, 2);
|
||||
}
|
||||
|
||||
ImmutableSet<Integer> testIntegerParseUnsignedInt() {
|
||||
return ImmutableSet.of(UnsignedInts.parseUnsignedInt("1"), Integer.parseUnsignedInt("2", 10));
|
||||
}
|
||||
|
||||
ImmutableSet<Long> testLongParseUnsignedLong() {
|
||||
return ImmutableSet.of(UnsignedLongs.parseUnsignedLong("1"), Long.parseUnsignedLong("2", 10));
|
||||
}
|
||||
|
||||
int testIntegerParseUnsignedIntWithRadix() {
|
||||
return UnsignedInts.parseUnsignedInt("1", 2);
|
||||
}
|
||||
|
||||
long testLongParseUnsignedLongWithRadix() {
|
||||
return UnsignedLongs.parseUnsignedLong("1", 2);
|
||||
}
|
||||
|
||||
ImmutableSet<String> testIntegerToUnsignedString() {
|
||||
return ImmutableSet.of(UnsignedInts.toString(1), Integer.toUnsignedString(2, 10));
|
||||
}
|
||||
|
||||
ImmutableSet<String> testLongToUnsignedString() {
|
||||
return ImmutableSet.of(UnsignedLongs.toString(1), Long.toUnsignedString(2, 10));
|
||||
}
|
||||
|
||||
String testIntegerToUnsignedStringWithRadix() {
|
||||
return UnsignedInts.toString(1, 2);
|
||||
}
|
||||
|
||||
String testLongToUnsignedStringWithRadix() {
|
||||
return UnsignedLongs.toString(1, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ import com.google.common.primitives.Floats;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.google.common.primitives.Shorts;
|
||||
import com.google.common.primitives.UnsignedInts;
|
||||
import com.google.common.primitives.UnsignedLongs;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
|
||||
@@ -24,9 +22,7 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Floats.class,
|
||||
Ints.class,
|
||||
Longs.class,
|
||||
Shorts.class,
|
||||
UnsignedInts.class,
|
||||
UnsignedLongs.class);
|
||||
Shorts.class);
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testLessThan() {
|
||||
@@ -109,6 +105,26 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return Double.hashCode(1);
|
||||
}
|
||||
|
||||
int testBooleanCompare() {
|
||||
return Boolean.compare(false, true);
|
||||
}
|
||||
|
||||
int testCharacterCompare() {
|
||||
return Character.compare('a', 'b');
|
||||
}
|
||||
|
||||
int testShortCompare() {
|
||||
return Short.compare((short) 1, (short) 2);
|
||||
}
|
||||
|
||||
int testIntegerCompare() {
|
||||
return Integer.compare(1, 2);
|
||||
}
|
||||
|
||||
int testLongCompare() {
|
||||
return Long.compare(1, 2);
|
||||
}
|
||||
|
||||
int testFloatCompare() {
|
||||
return Float.compare(1, 2);
|
||||
}
|
||||
@@ -174,60 +190,4 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(
|
||||
Long.signum(1L) == -1, Long.signum(2L) == -1, Long.signum(3L) != -1, Long.signum(4L) != -1);
|
||||
}
|
||||
|
||||
int testIntegerCompareUnsigned() {
|
||||
return Integer.compareUnsigned(1, 2);
|
||||
}
|
||||
|
||||
long testLongCompareUnsigned() {
|
||||
return Long.compareUnsigned(1, 2);
|
||||
}
|
||||
|
||||
int testIntegerDivideUnsigned() {
|
||||
return Integer.divideUnsigned(1, 2);
|
||||
}
|
||||
|
||||
long testLongDivideUnsigned() {
|
||||
return Long.divideUnsigned(1, 2);
|
||||
}
|
||||
|
||||
int testIntegerRemainderUnsigned() {
|
||||
return Integer.remainderUnsigned(1, 2);
|
||||
}
|
||||
|
||||
long testLongRemainderUnsigned() {
|
||||
return Long.remainderUnsigned(1, 2);
|
||||
}
|
||||
|
||||
ImmutableSet<Integer> testIntegerParseUnsignedInt() {
|
||||
return ImmutableSet.of(Integer.parseUnsignedInt("1"), Integer.parseUnsignedInt("2"));
|
||||
}
|
||||
|
||||
ImmutableSet<Long> testLongParseUnsignedLong() {
|
||||
return ImmutableSet.of(Long.parseUnsignedLong("1"), Long.parseUnsignedLong("2"));
|
||||
}
|
||||
|
||||
int testIntegerParseUnsignedIntWithRadix() {
|
||||
return Integer.parseUnsignedInt("1", 2);
|
||||
}
|
||||
|
||||
long testLongParseUnsignedLongWithRadix() {
|
||||
return Long.parseUnsignedLong("1", 2);
|
||||
}
|
||||
|
||||
ImmutableSet<String> testIntegerToUnsignedString() {
|
||||
return ImmutableSet.of(Integer.toUnsignedString(1), Integer.toUnsignedString(2));
|
||||
}
|
||||
|
||||
ImmutableSet<String> testLongToUnsignedString() {
|
||||
return ImmutableSet.of(Long.toUnsignedString(1), Long.toUnsignedString(2));
|
||||
}
|
||||
|
||||
String testIntegerToUnsignedStringWithRadix() {
|
||||
return Integer.toUnsignedString(1, 2);
|
||||
}
|
||||
|
||||
String testLongToUnsignedStringWithRadix() {
|
||||
return Long.toUnsignedString(1, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import static java.util.stream.Collectors.minBy;
|
||||
import static java.util.stream.Collectors.toCollection;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -665,10 +664,6 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return Mono.fromFuture(CompletableFuture.completedFuture(null), true);
|
||||
}
|
||||
|
||||
Mono<String> testMonoFromFutureAsyncLoadingCacheGet() {
|
||||
return Mono.fromFuture(() -> ((AsyncLoadingCache<Integer, String>) null).get(0));
|
||||
}
|
||||
|
||||
Flux<Integer> testFluxFromStreamSupplier() {
|
||||
return Flux.fromStream(Stream.of(1));
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import static java.util.stream.Collectors.toCollection;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static reactor.function.TupleUtils.function;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -645,10 +644,6 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return Mono.fromFuture(() -> CompletableFuture.completedFuture(null), true);
|
||||
}
|
||||
|
||||
Mono<String> testMonoFromFutureAsyncLoadingCacheGet() {
|
||||
return Mono.fromFuture(() -> ((AsyncLoadingCache<Integer, String>) null).get(0), true);
|
||||
}
|
||||
|
||||
Flux<Integer> testFluxFromStreamSupplier() {
|
||||
return Flux.fromStream(() -> Stream.of(1));
|
||||
}
|
||||
|
||||
@@ -96,8 +96,4 @@ final class StringRulesTest implements RefasterRuleCollectionTestCase {
|
||||
int testUtf8EncodedLength() {
|
||||
return "foo".getBytes(UTF_8).length;
|
||||
}
|
||||
|
||||
String testNewStringChar() {
|
||||
return String.copyValueOf(new char[] {'f', 'o', 'o'});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +96,4 @@ final class StringRulesTest implements RefasterRuleCollectionTestCase {
|
||||
int testUtf8EncodedLength() {
|
||||
return Utf8.encodedLength("foo");
|
||||
}
|
||||
|
||||
String testNewStringChar() {
|
||||
return new String(new char[] {'f', 'o', 'o'});
|
||||
}
|
||||
}
|
||||
|
||||
33
pom.xml
33
pom.xml
@@ -208,16 +208,16 @@
|
||||
<version.auto-value>1.11.0</version.auto-value>
|
||||
<version.error-prone>${version.error-prone-orig}</version.error-prone>
|
||||
<version.error-prone-fork>v${version.error-prone-orig}-picnic-1</version.error-prone-fork>
|
||||
<version.error-prone-orig>2.31.0</version.error-prone-orig>
|
||||
<version.error-prone-orig>2.30.0</version.error-prone-orig>
|
||||
<version.error-prone-slf4j>0.1.25</version.error-prone-slf4j>
|
||||
<version.guava-beta-checker>1.0</version.guava-beta-checker>
|
||||
<version.jdk>17</version.jdk>
|
||||
<version.maven>3.9.9</version.maven>
|
||||
<version.mockito>5.13.0</version.mockito>
|
||||
<version.maven>3.9.8</version.maven>
|
||||
<version.mockito>5.12.0</version.mockito>
|
||||
<version.nopen-checker>1.0.1</version.nopen-checker>
|
||||
<version.nullaway>0.11.2</version.nullaway>
|
||||
<version.pitest-git>1.1.4</version.pitest-git>
|
||||
<version.rewrite-templating>1.14.0</version.rewrite-templating>
|
||||
<version.rewrite-templating>1.13.0</version.rewrite-templating>
|
||||
<version.surefire>3.2.3</version.surefire>
|
||||
</properties>
|
||||
|
||||
@@ -300,11 +300,6 @@
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>3.1.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto</groupId>
|
||||
<artifactId>auto-common</artifactId>
|
||||
@@ -343,7 +338,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava-bom</artifactId>
|
||||
<version>33.3.0-jre</version>
|
||||
<version>33.2.1-jre</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -382,7 +377,7 @@
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>2.2.23</version>
|
||||
<version>2.2.22</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
@@ -417,7 +412,7 @@
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy</artifactId>
|
||||
<version>1.15.1</version>
|
||||
<version>1.14.19</version>
|
||||
</dependency>
|
||||
<!-- Specified so that Renovate will file Maven upgrade PRs, which
|
||||
subsequently will cause `maven-enforcer-plugin` to require that
|
||||
@@ -486,7 +481,7 @@
|
||||
<dependency>
|
||||
<groupId>org.openrewrite.recipe</groupId>
|
||||
<artifactId>rewrite-recipe-bom</artifactId>
|
||||
<version>2.17.0</version>
|
||||
<version>2.16.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -500,19 +495,19 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>6.1.12</version>
|
||||
<version>6.1.11</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
<version>3.3.3</version>
|
||||
<version>3.3.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-bom</artifactId>
|
||||
<version>6.3.3</version>
|
||||
<version>6.3.1</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -567,7 +562,7 @@
|
||||
<plugin>
|
||||
<groupId>com.spotify.fmt</groupId>
|
||||
<artifactId>fmt-maven-plugin</artifactId>
|
||||
<version>2.24</version>
|
||||
<version>2.22.1</version>
|
||||
<configuration>
|
||||
<additionalSourceDirectories>
|
||||
<additionalSourceDirectory>${basedir}/src/test/resources</additionalSourceDirectory>
|
||||
@@ -653,7 +648,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>3.4.0</version>
|
||||
<configuration>
|
||||
<checkstyleRules>
|
||||
<!-- We only enable rules that are not enforced by
|
||||
@@ -902,7 +897,7 @@
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>10.18.0</version>
|
||||
<version>10.17.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.spring.nohttp</groupId>
|
||||
|
||||
@@ -2,7 +2,6 @@ package tech.picnic.errorprone.refaster.matchers;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ArrayAccessTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
@@ -10,19 +9,34 @@ import com.sun.source.tree.LambdaExpressionTree;
|
||||
import com.sun.source.tree.LiteralTree;
|
||||
import com.sun.source.tree.MemberReferenceTree;
|
||||
import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.ParenthesizedTree;
|
||||
import com.sun.source.tree.TypeCastTree;
|
||||
import com.sun.source.tree.UnaryTree;
|
||||
|
||||
/** A matcher of expressions that may a non-trivial amount of computation. */
|
||||
public final class RequiresComputation implements Matcher<ExpressionTree> {
|
||||
/** A matcher of expressions that likely require little to no computation. */
|
||||
public final class IsLikelyTrivialComputation implements Matcher<ExpressionTree> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Instantiates a new {@link RequiresComputation} instance. */
|
||||
public RequiresComputation() {}
|
||||
/** Instantiates a new {@link IsLikelyTrivialComputation} instance. */
|
||||
public IsLikelyTrivialComputation() {}
|
||||
|
||||
@Override
|
||||
public boolean matches(ExpressionTree expressionTree, VisitorState state) {
|
||||
if (expressionTree instanceof MethodInvocationTree methodInvocation) {
|
||||
// XXX: Method invocations are generally *not* trivial computations, but we make an exception
|
||||
// for nullary method invocations on the result of a trivial computation. This exception
|
||||
// allows this `Matcher` to by the `OptionalOrElseGet` Refaster rule, such that it does not
|
||||
// suggest the introduction of lambda expressions that are better expressed as method
|
||||
// references. Once the `MethodReferenceUsage` bug checker is production-ready, this exception
|
||||
// should be removed. (But at that point, instead defining a `RequiresComputation` matcher may
|
||||
// be more appropriate.)
|
||||
if (methodInvocation.getArguments().isEmpty()
|
||||
&& matches(methodInvocation.getMethodSelect())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return matches(expressionTree);
|
||||
}
|
||||
|
||||
@@ -30,11 +44,11 @@ public final class RequiresComputation implements Matcher<ExpressionTree> {
|
||||
// Depending on feedback such trees may be matched in the future.
|
||||
private static boolean matches(ExpressionTree expressionTree) {
|
||||
if (expressionTree instanceof ArrayAccessTree arrayAccess) {
|
||||
return matches(arrayAccess.getExpression()) || matches(arrayAccess.getIndex());
|
||||
return matches(arrayAccess.getExpression()) && matches(arrayAccess.getIndex());
|
||||
}
|
||||
|
||||
if (expressionTree instanceof LiteralTree) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (expressionTree instanceof LambdaExpressionTree) {
|
||||
@@ -42,14 +56,11 @@ public final class RequiresComputation implements Matcher<ExpressionTree> {
|
||||
* Lambda expressions encapsulate computations, but their definition does not involve
|
||||
* significant computation.
|
||||
*/
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (expressionTree instanceof IdentifierTree) {
|
||||
// XXX: Generally identifiers don't by themselves represent a computation, though they may be
|
||||
// a stand-in for one if they are a Refaster template method argument. Can we identify such
|
||||
// cases, also when the `Matcher` is invoked by Refaster?
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (expressionTree instanceof MemberReferenceTree memberReference) {
|
||||
@@ -69,11 +80,11 @@ public final class RequiresComputation implements Matcher<ExpressionTree> {
|
||||
}
|
||||
|
||||
if (expressionTree instanceof UnaryTree unary) {
|
||||
// XXX: Arguably side-effectful options such as pre- and post-increment and -decrement
|
||||
// represent non-trivial computations.
|
||||
// XXX: Arguably side-effectful options such as pre- and post-increment and -decrement are not
|
||||
// trivial.
|
||||
return matches(unary.getExpression());
|
||||
}
|
||||
|
||||
return ASTHelpers.constValue(expressionTree) == null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -8,69 +8,54 @@ import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.sun.source.tree.ReturnTree;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class RequiresComputationTest {
|
||||
final class IsLikelyTrivialComputationTest {
|
||||
@Test
|
||||
void matches() {
|
||||
CompilationTestHelper.newInstance(MatcherTestChecker.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import java.io.OutputStream;",
|
||||
"import java.util.Comparator;",
|
||||
"import java.util.function.Predicate;",
|
||||
"",
|
||||
"class A {",
|
||||
" int negative1() {",
|
||||
" int[] arr = new int[0];",
|
||||
" return arr[0];",
|
||||
" String negative1() {",
|
||||
" return String.valueOf(1);",
|
||||
" }",
|
||||
"",
|
||||
" String negative2() {",
|
||||
" return null;",
|
||||
" return toString().toString();",
|
||||
" }",
|
||||
"",
|
||||
" boolean negative3() {",
|
||||
" return false;",
|
||||
" String negative3() {",
|
||||
" return \"foo\" + toString();",
|
||||
" }",
|
||||
"",
|
||||
" int negative4() {",
|
||||
" return 0;",
|
||||
" byte negative4() {",
|
||||
" return \"foo\".getBytes()[0];",
|
||||
" }",
|
||||
"",
|
||||
" String negative5() {",
|
||||
" return \"foo\" + \"bar\";",
|
||||
" int negative5() {",
|
||||
" int[] arr = new int[0];",
|
||||
" return arr[hashCode()];",
|
||||
" }",
|
||||
"",
|
||||
" Predicate<String> negative6() {",
|
||||
" return v -> \"foo\".equals(v);",
|
||||
" int negative6() {",
|
||||
" return 1 * 2;",
|
||||
" }",
|
||||
"",
|
||||
" A negative7() {",
|
||||
" return this;",
|
||||
" Predicate<String> negative7() {",
|
||||
" return toString()::equals;",
|
||||
" }",
|
||||
"",
|
||||
" Predicate<String> negative8() {",
|
||||
" return \"foo\"::equals;",
|
||||
" String negative8() {",
|
||||
" return (toString());",
|
||||
" }",
|
||||
"",
|
||||
" OutputStream negative9() {",
|
||||
" return System.out;",
|
||||
" Object negative9() {",
|
||||
" return (Object) toString();",
|
||||
" }",
|
||||
"",
|
||||
" A negative10() {",
|
||||
" return (this);",
|
||||
" }",
|
||||
"",
|
||||
" Object negative11() {",
|
||||
" return (Object) this;",
|
||||
" }",
|
||||
"",
|
||||
" boolean negative12() {",
|
||||
" boolean[] arr = new boolean[0];",
|
||||
" return !arr[0];",
|
||||
" }",
|
||||
"",
|
||||
" String negative13() {",
|
||||
" return \"foo\" + 0;",
|
||||
" int negative10() {",
|
||||
" return -hashCode();",
|
||||
" }",
|
||||
"",
|
||||
" String positive1() {",
|
||||
@@ -83,63 +68,68 @@ final class RequiresComputationTest {
|
||||
" return this.toString();",
|
||||
" }",
|
||||
"",
|
||||
" String positive3() {",
|
||||
" int positive3() {",
|
||||
" int[] arr = new int[0];",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return String.valueOf(1);",
|
||||
" return arr[0];",
|
||||
" }",
|
||||
"",
|
||||
" String positive4() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return toString().toString();",
|
||||
" return null;",
|
||||
" }",
|
||||
"",
|
||||
" String positive5() {",
|
||||
" boolean positive5() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return \"foo\" + toString();",
|
||||
" return false;",
|
||||
" }",
|
||||
"",
|
||||
" byte positive6() {",
|
||||
" int positive6() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return \"foo\".getBytes()[0];",
|
||||
" return 0;",
|
||||
" }",
|
||||
"",
|
||||
" int positive7() {",
|
||||
" int[] arr = new int[0];",
|
||||
" String positive7() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return arr[hashCode()];",
|
||||
" return \"foo\" + \"bar\";",
|
||||
" }",
|
||||
"",
|
||||
" Predicate<String> positive8() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return toString()::equals;",
|
||||
" return v -> \"foo\".equals(v);",
|
||||
" }",
|
||||
"",
|
||||
" Comparator<String> positive9() {",
|
||||
" A positive9() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return toString().CASE_INSENSITIVE_ORDER;",
|
||||
" return this;",
|
||||
" }",
|
||||
"",
|
||||
" String positive10() {",
|
||||
" Predicate<String> positive10() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return (toString());",
|
||||
" return \"foo\"::equals;",
|
||||
" }",
|
||||
"",
|
||||
" Object positive11() {",
|
||||
" A positive11() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return (Object) toString();",
|
||||
" return (this);",
|
||||
" }",
|
||||
"",
|
||||
" int positive12() {",
|
||||
" Object positive12() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return -hashCode();",
|
||||
" return (Object) this;",
|
||||
" }",
|
||||
"",
|
||||
" boolean positive13() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return !false;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
/** A {@link BugChecker} that simply delegates to {@link RequiresComputation}. */
|
||||
/** A {@link BugChecker} that simply delegates to {@link IsLikelyTrivialComputation}. */
|
||||
@BugPattern(
|
||||
summary = "Flags return statement expressions matched by `RequiresComputation`",
|
||||
summary = "Flags return statement expressions matched by `IsLikelyTrivialComputation`",
|
||||
severity = ERROR)
|
||||
public static final class MatcherTestChecker extends AbstractMatcherTestChecker {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -151,7 +141,7 @@ final class RequiresComputationTest {
|
||||
super(
|
||||
(expressionTree, state) ->
|
||||
state.getPath().getParentPath().getLeaf() instanceof ReturnTree
|
||||
&& new RequiresComputation().matches(expressionTree, state));
|
||||
&& new IsLikelyTrivialComputation().matches(expressionTree, state));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package tech.picnic.errorprone.refaster.test;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
|
||||
// XXX: Drop these annotations.
|
||||
@Disabled
|
||||
@SuppressWarnings("all")
|
||||
abstract class MyAbstractTest {
|
||||
@TestFactory
|
||||
Stream<DynamicTest> dynamicTests() {
|
||||
MatchInWrongMethodRules.class.getDeclaredClasses();
|
||||
|
||||
return Stream.of(
|
||||
DynamicTest.dynamicTest("A " + getClass(), () -> {}),
|
||||
DynamicTest.dynamicTest("A", () -> {}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package tech.picnic.errorprone.refaster.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.extension.Extension;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.api.extension.TestInstanceFactory;
|
||||
import org.junit.jupiter.api.extension.TestInstanceFactoryContext;
|
||||
import org.junit.jupiter.api.extension.TestInstantiationException;
|
||||
|
||||
// XXX: Drop these annotations.
|
||||
@Disabled
|
||||
@SuppressWarnings("all")
|
||||
public class MyTest extends MyAbstractTest {
|
||||
public static final class MyTestInstanceFactory implements TestInstanceFactory {
|
||||
@Override
|
||||
public Object createTestInstance(
|
||||
TestInstanceFactoryContext factoryContext, ExtensionContext extensionContext)
|
||||
throws TestInstantiationException {
|
||||
return new MySubTest();
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Name
|
||||
@Test
|
||||
void foo() {
|
||||
// Options:
|
||||
// 1. Add examples to the Refaster rule, generate the tests from those
|
||||
//
|
||||
// 2. Define as test, and use a custom test annotation, which:
|
||||
// - Generates the relevant test resources
|
||||
// - Executes them.
|
||||
// ^ Might be nicer to generate a subclass and use a `TestInstanceFactory` to instantiate that.
|
||||
// XXX: That turns out not to work.
|
||||
//
|
||||
// ^ Other option: provide super class that declares a `TestFactory` based on the subclass name.
|
||||
|
||||
}
|
||||
|
||||
// Extensions can be defined at any level. TBD how useful.
|
||||
// static final class MyExtension implements Extension, {
|
||||
//
|
||||
// }
|
||||
// @ExtendWith(MyExtension.class)
|
||||
|
||||
static class MySubTest extends MyTest {}
|
||||
|
||||
@TestFactory
|
||||
Stream<DynamicTest> dynamicTestsFromStreamInJava8() {
|
||||
|
||||
Function<String, String> resolver = s -> s;
|
||||
|
||||
List<String> domainNames =
|
||||
Arrays.asList("www.somedomain.com", "www.anotherdomain.com", "www.yetanotherdomain.com");
|
||||
List<String> outputList = Arrays.asList("154.174.10.56", "211.152.104.132", "178.144.120.156");
|
||||
|
||||
return domainNames.stream()
|
||||
.map(
|
||||
dom ->
|
||||
DynamicTest.dynamicTest(
|
||||
"Resolving: " + dom,
|
||||
() -> {
|
||||
int id = domainNames.indexOf(dom);
|
||||
|
||||
assertEquals(outputList.get(id), resolver.apply(dom));
|
||||
}));
|
||||
}
|
||||
|
||||
static final class PrimitiveOrReferenceEquality
|
||||
implements ExpressionTestCase<ImmutableSet<Boolean>> {
|
||||
public ImmutableSet<Boolean> before() {
|
||||
return ImmutableSet.of(
|
||||
RoundingMode.UP.equals(RoundingMode.DOWN),
|
||||
Objects.equals(RoundingMode.UP, RoundingMode.DOWN),
|
||||
!RoundingMode.UP.equals(RoundingMode.DOWN),
|
||||
!Objects.equals(RoundingMode.UP, RoundingMode.DOWN));
|
||||
}
|
||||
|
||||
public ImmutableSet<Boolean> after() {
|
||||
return ImmutableSet.of(
|
||||
RoundingMode.UP == RoundingMode.DOWN,
|
||||
RoundingMode.UP == RoundingMode.DOWN,
|
||||
RoundingMode.UP != RoundingMode.DOWN,
|
||||
RoundingMode.UP != RoundingMode.DOWN);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: This variant can verify that the values are equal.
|
||||
static final class PrimitiveOrReferenceEquality2 implements ExpressionTestCase2<Boolean> {
|
||||
public void before(Consumer<Boolean> sink) {
|
||||
sink.accept(RoundingMode.UP.equals(RoundingMode.DOWN));
|
||||
sink.accept(Objects.equals(RoundingMode.UP, RoundingMode.DOWN));
|
||||
sink.accept(!RoundingMode.UP.equals(RoundingMode.DOWN));
|
||||
sink.accept(!Objects.equals(RoundingMode.UP, RoundingMode.DOWN));
|
||||
}
|
||||
|
||||
public void after(Consumer<Boolean> sink) {
|
||||
sink.accept(RoundingMode.UP == RoundingMode.DOWN);
|
||||
sink.accept(RoundingMode.UP == RoundingMode.DOWN);
|
||||
sink.accept(RoundingMode.UP != RoundingMode.DOWN);
|
||||
sink.accept(RoundingMode.UP != RoundingMode.DOWN);
|
||||
}
|
||||
}
|
||||
|
||||
// With this setup we could validate equality, and (in theory) even report the exact source in
|
||||
// case of an error.
|
||||
// @Test
|
||||
void primitiveOrReferenceEquality(BiConsumer<Boolean, Boolean> testCase) {
|
||||
testCase.accept(
|
||||
RoundingMode.UP.equals(RoundingMode.DOWN), RoundingMode.UP == RoundingMode.DOWN);
|
||||
testCase.accept(
|
||||
Objects.equals(RoundingMode.UP, RoundingMode.DOWN), RoundingMode.UP == RoundingMode.DOWN);
|
||||
testCase.accept(
|
||||
!RoundingMode.UP.equals(RoundingMode.DOWN), RoundingMode.UP != RoundingMode.DOWN);
|
||||
testCase.accept(
|
||||
!Objects.equals(RoundingMode.UP, RoundingMode.DOWN), RoundingMode.UP != RoundingMode.DOWN);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// With this setup we could validate equality, and (in theory) even report the exact source in
|
||||
// case of an error.
|
||||
// @Test
|
||||
void primitiveOrReferenceEquality2(BiConsumer<Supplier<Boolean>, Supplier<Boolean>> testCase) {
|
||||
testCase.accept(
|
||||
() -> RoundingMode.UP.equals(RoundingMode.DOWN),
|
||||
() -> RoundingMode.UP == RoundingMode.DOWN);
|
||||
testCase.accept(
|
||||
() -> Objects.equals(RoundingMode.UP, RoundingMode.DOWN),
|
||||
() -> RoundingMode.UP == RoundingMode.DOWN);
|
||||
testCase.accept(
|
||||
() -> !RoundingMode.UP.equals(RoundingMode.DOWN),
|
||||
() -> RoundingMode.UP != RoundingMode.DOWN);
|
||||
testCase.accept(
|
||||
() -> !Objects.equals(RoundingMode.UP, RoundingMode.DOWN),
|
||||
() -> RoundingMode.UP != RoundingMode.DOWN);
|
||||
}
|
||||
|
||||
interface ExpressionTestCase<T> {
|
||||
T before();
|
||||
|
||||
T after();
|
||||
}
|
||||
|
||||
interface ExpressionTestCase2<T> {
|
||||
void before(Consumer<T> sink);
|
||||
|
||||
void after(Consumer<T> sink);
|
||||
}
|
||||
|
||||
// record ExpressionTestCaseX<T>(Supplier<T> before, Supplier<T> after);
|
||||
}
|
||||
@@ -4,7 +4,6 @@ releases:
|
||||
- version: 0.18.0
|
||||
compatible:
|
||||
- "2.30.0"
|
||||
- "2.31.0"
|
||||
- version: 0.17.0
|
||||
compatible:
|
||||
- "2.29.2"
|
||||
|
||||
Reference in New Issue
Block a user