diff --git a/.github/release.yml b/.github/release.yml index bbd41aea..febfe2b2 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -3,16 +3,16 @@ changelog: labels: - "ignore-changelog" categories: + - title: ":warning: Update considerations and deprecations" + labels: + - "breaking change" + - "deprecation" - title: ":rocket: New Error Prone checks and Refaster rules" labels: - "new feature" - title: ":sparkles: Improvements" labels: - "improvement" - - title: ":warning: Update considerations and deprecations" - labels: - - "breaking change" - - "deprecation" - title: ":bug: Bug fixes" labels: - "bug" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e2a26423..0a11222c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: os: [ ubuntu-22.04 ] - jdk: [ 17.0.10, 21.0.2 ] + jdk: [ 17.0.10, 21.0.2, 22.0.2 ] distribution: [ temurin ] experimental: [ false ] include: @@ -26,13 +26,15 @@ jobs: continue-on-error: ${{ matrix.experimental }} steps: - name: Install Harden-Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 with: disable-sudo: true egress-policy: block allowed-endpoints: > + api.adoptium.net:443 github.com:443 jitpack.io:443 + objects.githubusercontent.com:443 repo.maven.apache.org:443 # We run the build twice for each supported JDK: once against the # original Error Prone release, using only Error Prone checks available @@ -40,7 +42,7 @@ jobs: # additionally enabling all checks defined in this project and any Error # Prone checks available only from other artifact repositories. - name: Check out code and set up JDK and Maven - uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0 + uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0 with: java-version: ${{ matrix.jdk }} java-distribution: ${{ matrix.distribution }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7882aee6..e29160fd 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -22,30 +22,31 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Harden-Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 with: disable-sudo: true egress-policy: block allowed-endpoints: > + api.adoptium.net:443 api.github.com:443 github.com:443 objects.githubusercontent.com:443 repo.maven.apache.org:443 uploads.github.com:443 - name: Check out code and set up JDK and Maven - uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0 + uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0 with: java-version: 17.0.10 java-distribution: temurin maven-version: 3.9.6 - name: Initialize CodeQL - uses: github/codeql-action/init@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/init@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 with: languages: ${{ matrix.language }} - name: Perform minimal build if: matrix.language == 'java' run: mvn -T1C clean package -DskipTests -Dverification.skip - name: Perform CodeQL analysis - uses: github/codeql-action/analyze@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/analyze@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 with: category: /language:${{ matrix.language }} diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml index 4de5cfcf..0bd62b0b 100644 --- a/.github/workflows/deploy-website.yml +++ b/.github/workflows/deploy-website.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Harden-Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 with: disable-sudo: true egress-policy: block @@ -38,22 +38,18 @@ jobs: www.bestpractices.dev:443 www.youtube.com:443 youtrack.jetbrains.com:443 - - name: Check out code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Check out code and set up JDK and Maven + uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0 with: - persist-credentials: false - - uses: ruby/setup-ruby@d4526a55538b775af234ba4af27118ed6f8f6677 # v1.172.0 + java-version: 17.0.10 + java-distribution: temurin + maven-version: 3.9.6 + - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 with: working-directory: ./website bundler-cache: true - name: Configure Github Pages - uses: actions/configure-pages@1f0c5cde4bc74cd7e1254d0cb4de8d49e9068c7d # v4.0.0 - - name: Set up JDK - uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0 - with: - java-version: 17.0.8 - distribution: temurin - cache: maven + uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0 - name: Compile project and extract data run: mvn -T1C clean install -DskipTests -Dverification.skip -Pdocgen - name: Generate documentation @@ -81,7 +77,7 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - name: Install Harden-Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/openssf-scorecard.yml b/.github/workflows/openssf-scorecard.yml index 0b757089..ca35f293 100644 --- a/.github/workflows/openssf-scorecard.yml +++ b/.github/workflows/openssf-scorecard.yml @@ -21,31 +21,30 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Harden-Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 with: disable-sudo: true egress-policy: block allowed-endpoints: > api.github.com:443 api.osv.dev:443 + api.scorecard.dev:443 api.securityscorecards.dev:443 - fulcio.sigstore.dev:443 github.com:443 oss-fuzz-build-logs.storage.googleapis.com:443 - rekor.sigstore.dev:443 - tuf-repo-cdn.sigstore.dev:443 + *.sigstore.dev:443 www.bestpractices.dev:443 - name: Check out code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: persist-credentials: false - name: Run OpenSSF Scorecard analysis - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3 with: results_file: results.sarif results_format: sarif publish_results: ${{ github.ref == 'refs/heads/master' }} - name: Update GitHub's code scanning dashboard - uses: github/codeql-action/upload-sarif@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 with: sarif_file: results.sarif diff --git a/.github/workflows/pitest-analyze-pr.yml b/.github/workflows/pitest-analyze-pr.yml index d0763aeb..524696fa 100644 --- a/.github/workflows/pitest-analyze-pr.yml +++ b/.github/workflows/pitest-analyze-pr.yml @@ -12,15 +12,17 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Harden-Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 with: disable-sudo: true egress-policy: block allowed-endpoints: > + api.adoptium.net:443 github.com:443 + objects.githubusercontent.com:443 repo.maven.apache.org:443 - name: Check out code and set up JDK and Maven - uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0 + uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0 with: checkout-fetch-depth: 2 java-version: 17.0.10 @@ -36,7 +38,7 @@ jobs: - name: Aggregate Pitest reports run: mvn pitest-git:aggregate -DkilledEmoji=":tada:" -DmutantEmoji=":zombie:" -DtrailingText="Mutation testing report by [Pitest](https://pitest.org/). Review any surviving mutants by inspecting the line comments under [_Files changed_](${{ github.event.number }}/files)." - name: Upload Pitest reports as artifact - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: pitest-reports path: ./target/pit-reports-ci diff --git a/.github/workflows/pitest-update-pr.yml b/.github/workflows/pitest-update-pr.yml index 83937e9e..676248c5 100644 --- a/.github/workflows/pitest-update-pr.yml +++ b/.github/workflows/pitest-update-pr.yml @@ -20,22 +20,24 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Harden-Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 with: disable-sudo: true egress-policy: block allowed-endpoints: > + api.adoptium.net:443 api.github.com:443 github.com:443 + objects.githubusercontent.com:443 repo.maven.apache.org:443 - name: Check out code and set up JDK and Maven - uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0 + uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0 with: java-version: 17.0.10 java-distribution: temurin maven-version: 3.9.6 - name: Download Pitest analysis artifact - uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2 + uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4 with: workflow: ${{ github.event.workflow_run.workflow_id }} name: pitest-reports diff --git a/.github/workflows/run-integration-tests.yml b/.github/workflows/run-integration-tests.yml index 6776c89b..fb98b3d0 100644 --- a/.github/workflows/run-integration-tests.yml +++ b/.github/workflows/run-integration-tests.yml @@ -19,19 +19,21 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Harden-Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 with: disable-sudo: true egress-policy: block allowed-endpoints: > + api.adoptium.net:443 checkstyle.org:443 github.com:443 + objects.githubusercontent.com:443 oss.sonatype.org:443 raw.githubusercontent.com:443 repo.maven.apache.org:443 repository.sonatype.org:443 - name: Check out code and set up JDK and Maven - uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0 + uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0 with: checkout-ref: "refs/pull/${{ github.event.issue.number }}/head" java-version: 17.0.10 @@ -43,7 +45,7 @@ jobs: run: xvfb-run ./integration-tests/checkstyle.sh "${{ runner.temp }}/artifacts" - name: Upload artifacts on failure if: ${{ failure() }} - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: integration-test-checkstyle path: "${{ runner.temp }}/artifacts" diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 1a7722b1..47ae8109 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -19,19 +19,23 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Harden-Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 with: disable-sudo: true egress-policy: block allowed-endpoints: > + analysis-sensorcache-eu-central-1-prod.s3.amazonaws.com:443 + api.adoptium.net:443 + api.nuget.org:443 ea6ne4j2sb.execute-api.eu-central-1.amazonaws.com:443 github.com:443 + objects.githubusercontent.com:443 repo.maven.apache.org:443 sc-cleancode-sensorcache-eu-central-1-prod.s3.amazonaws.com:443 - scanner.sonarcloud.io:443 + *.sonarcloud.io:443 sonarcloud.io:443 - name: Check out code and set up JDK and Maven - uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0 + uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0 with: checkout-fetch-depth: 0 java-version: 17.0.10 diff --git a/.gitignore b/.gitignore index fbb0d366..8ed010f1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ .DS_Store .factorypath .idea +!.idea/icon.svg .project .settings target diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 00000000..3579ea9f --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.renovaterc.json b/.renovaterc.json index 9f50ffa4..1a35a0fb 100644 --- a/.renovaterc.json +++ b/.renovaterc.json @@ -12,7 +12,7 @@ "separateMinorPatch": true }, { - "matchDepNames": [ + "matchPackageNames": [ "dawidd6/action-download-artifact", "github/codeql-action", "ruby/setup-ruby" diff --git a/documentation-support/pom.xml b/documentation-support/pom.xml index 678a0128..09cb5865 100644 --- a/documentation-support/pom.xml +++ b/documentation-support/pom.xml @@ -5,7 +5,7 @@ tech.picnic.error-prone-support error-prone-support - 0.16.2-SNAPSHOT + 0.18.1-SNAPSHOT documentation-support diff --git a/error-prone-contrib/pom.xml b/error-prone-contrib/pom.xml index 509a9b81..e21a54da 100644 --- a/error-prone-contrib/pom.xml +++ b/error-prone-contrib/pom.xml @@ -5,7 +5,7 @@ tech.picnic.error-prone-support error-prone-support - 0.16.2-SNAPSHOT + 0.18.1-SNAPSHOT error-prone-contrib @@ -122,6 +122,11 @@ jakarta.servlet-api test + + javax.annotation + javax.annotation-api + provided + javax.inject javax.inject diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java index 6327e392..5a873263 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java @@ -232,7 +232,7 @@ public final class JUnitValueSource extends BugChecker implements MethodTreeMatc @Override public @Nullable Void visitReturn(ReturnTree node, @Nullable Void unused) { returnExpressions.add(node.getExpression()); - return super.visitReturn(node, unused); + return super.visitReturn(node, null); } @Override diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalAnnotationAttributeListing.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalAnnotationAttributeListing.java index e4812e17..62577e94 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalAnnotationAttributeListing.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalAnnotationAttributeListing.java @@ -192,7 +192,7 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker @Override public @Nullable Void visitIdentifier(IdentifierTree node, @Nullable Void unused) { nodes.add(ImmutableList.of(node.getName().toString())); - return super.visitIdentifier(node, unused); + return super.visitIdentifier(node, null); } @Override @@ -203,13 +203,13 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker ? STRING_ARGUMENT_SPLITTER.splitToStream(str).collect(toImmutableList()) : ImmutableList.of(String.valueOf(value))); - return super.visitLiteral(node, unused); + return super.visitLiteral(node, null); } @Override public @Nullable Void visitPrimitiveType(PrimitiveTypeTree node, @Nullable Void unused) { nodes.add(ImmutableList.of(node.getPrimitiveTypeKind().toString())); - return super.visitPrimitiveType(node, unused); + return super.visitPrimitiveType(node, null); } }.scan(array, null); diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/NonStaticImport.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/NonStaticImport.java index adbd88f7..eba7253b 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/NonStaticImport.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/NonStaticImport.java @@ -210,7 +210,7 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit } } - return super.visitIdentifier(node, unused); + return super.visitIdentifier(node, null); } }.scan(tree, null); } diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/OptionalOrElse.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/OptionalOrElse.java new file mode 100644 index 00000000..6e1a83f4 --- /dev/null +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/OptionalOrElse.java @@ -0,0 +1,135 @@ +package tech.picnic.errorprone.bugpatterns; + +import static com.google.errorprone.BugPattern.LinkType.NONE; +import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; +import static com.google.errorprone.BugPattern.StandardTags.PERFORMANCE; +import static com.google.errorprone.matchers.Matchers.instanceMethod; +import static com.google.errorprone.matchers.Matchers.staticMethod; +import static java.util.stream.Collectors.joining; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Iterables; +import com.google.errorprone.BugPattern; +import com.google.errorprone.VisitorState; +import com.google.errorprone.bugpatterns.BugChecker; +import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher; +import com.google.errorprone.fixes.SuggestedFix; +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.utils.SourceCode; + +/** + * A {@link BugChecker} that flags arguments to {@link Optional#orElse(Object)} that should be + * deferred using {@link Optional#orElseGet(Supplier)}. + * + *

The suggested fix assumes that the argument to {@code orElse} does not have side effects. If + * 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: 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 = + """ + Prefer `Optional#orElseGet` over `Optional#orElse` if the fallback requires additional \ + computation""", + linkType = NONE, + severity = WARNING, + tags = PERFORMANCE) +public final class OptionalOrElse extends BugChecker implements MethodInvocationTreeMatcher { + private static final long serialVersionUID = 1L; + private static final Matcher OPTIONAL_OR_ELSE_METHOD = + instanceMethod().onExactClass(Optional.class.getCanonicalName()).namedAnyOf("orElse"); + // XXX: Also exclude invocations of `@Placeholder`-annotated methods. + private static final Matcher REFASTER_METHOD = + staticMethod().onClass(Refaster.class.getCanonicalName()); + + /** Instantiates a new {@link OptionalOrElse} instance. */ + public OptionalOrElse() {} + + @Override + public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { + if (!OPTIONAL_OR_ELSE_METHOD.matches(tree, state)) { + return Description.NO_MATCH; + } + + ExpressionTree argument = Iterables.getOnlyElement(tree.getArguments()); + if (!requiresComputation(argument) || REFASTER_METHOD.matches(argument, state)) { + return Description.NO_MATCH; + } + + /* + * We have a match. Construct the method reference or lambda expression to be passed to the + * replacement `#orElseGet` invocation. + */ + String newArgument = + tryMethodReferenceConversion(argument, state) + .orElseGet(() -> "() -> " + SourceCode.treeToString(argument, state)); + + /* Construct the suggested fix, replacing the method invocation and its argument. */ + SuggestedFix fix = + SuggestedFix.builder() + .merge(SuggestedFixes.renameMethodInvocation(tree, "orElseGet", state)) + .replace(argument, newArgument) + .build(); + + 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 tryMethodReferenceConversion( + ExpressionTree tree, VisitorState state) { + if (!(tree instanceof MethodInvocationTree methodInvocation)) { + return Optional.empty(); + } + + if (!methodInvocation.getArguments().isEmpty()) { + return Optional.empty(); + } + + if (!(methodInvocation.getMethodSelect() instanceof MemberSelectTree memberSelect)) { + return Optional.empty(); + } + + if (requiresComputation(memberSelect.getExpression())) { + return Optional.empty(); + } + + return Optional.of( + SourceCode.treeToString(memberSelect.getExpression(), state) + + "::" + + (methodInvocation.getTypeArguments().isEmpty() + ? "" + : methodInvocation.getTypeArguments().stream() + .map(arg -> SourceCode.treeToString(arg, state)) + .collect(joining(",", "<", ">"))) + + memberSelect.getIdentifier()); + } +} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ClassRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ClassRules.java index 2890dd1b..496b1f1f 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ClassRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/ClassRules.java @@ -3,6 +3,7 @@ package tech.picnic.errorprone.refasterrules; import com.google.errorprone.refaster.Refaster; import com.google.errorprone.refaster.annotation.AfterTemplate; import com.google.errorprone.refaster.annotation.BeforeTemplate; +import java.util.function.Function; import java.util.function.Predicate; import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; @@ -37,7 +38,12 @@ final class ClassRules { } } - /** Prefer {@link Class#isInstance(Object)} method references over more verbose alternatives. */ + /** + * Prefer {@link Class#isInstance(Object)} method references over lambda expressions that require + * naming a variable. + */ + // XXX: Once the `ClassReferenceIsInstancePredicate` rule is dropped, rename this rule to just + // `ClassIsInstancePredicate`. static final class ClassLiteralIsInstancePredicate { @BeforeTemplate Predicate before() { @@ -50,7 +56,11 @@ final class ClassRules { } } - /** Prefer {@link Class#isInstance(Object)} method references over more verbose alternatives. */ + /** + * Prefer {@link Class#isInstance(Object)} method references over lambda expressions that require + * naming a variable. + */ + // XXX: Drop this rule once the `MethodReferenceUsage` rule is enabled by default. static final class ClassReferenceIsInstancePredicate { @BeforeTemplate Predicate before(Class clazz) { @@ -62,4 +72,39 @@ final class ClassRules { return clazz::isInstance; } } + + /** + * Prefer {@link Class#cast(Object)} method references over lambda expressions that require naming + * a variable. + */ + // XXX: Once the `ClassReferenceCast` rule is dropped, rename this rule to just `ClassCast`. + static final class ClassLiteralCast { + @BeforeTemplate + @SuppressWarnings("unchecked") + Function before() { + return t -> (S) t; + } + + @AfterTemplate + Function after() { + return Refaster.clazz()::cast; + } + } + + /** + * Prefer {@link Class#cast(Object)} method references over lambda expressions that require naming + * a variable. + */ + // XXX: Drop this rule once the `MethodReferenceUsage` rule is enabled by default. + static final class ClassReferenceCast { + @BeforeTemplate + Function before(Class clazz) { + return o -> clazz.cast(o); + } + + @AfterTemplate + Function after(Class clazz) { + return clazz::cast; + } + } } 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 cd341e79..4ee37189 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 @@ -8,7 +8,9 @@ import com.google.errorprone.refaster.Refaster; import com.google.errorprone.refaster.annotation.AfterTemplate; import com.google.errorprone.refaster.annotation.AlsoNegation; import com.google.errorprone.refaster.annotation.BeforeTemplate; +import com.google.errorprone.refaster.annotation.NotMatches; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -21,6 +23,7 @@ import java.util.function.Consumer; import java.util.function.IntFunction; import java.util.stream.Stream; import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; +import tech.picnic.errorprone.refaster.matchers.IsRefasterAsVarargs; /** 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 @@ -39,7 +42,7 @@ final class CollectionRules { "java:S1155" /* This violation will be rewritten. */, "LexicographicalAnnotationAttributeListing" /* `key-*` entry must remain last. */, "OptionalFirstCollectionElement" /* This is a more specific template. */, - "StreamIsEmpty" /* This is a more specific template. */, + "StreamFindAnyIsEmpty" /* This is a more specific template. */, "key-to-resolve-AnnotationUseStyle-and-TrailingComment-check-conflict" }) boolean before(Collection collection) { @@ -184,6 +187,24 @@ final class CollectionRules { } } + /** Don't unnecessarily call {@link Stream#distinct()} on an already-unique stream of elements. */ + // XXX: This rule assumes that the `Set` relies on `Object#equals`, rather than a custom + // equivalence relation. + // XXX: Expressions that drop or reorder elements from the stream, such as `.filter`, `.skip` and + // `sorted`, can similarly be simplified. Covering all cases is better done using an Error Prone + // check. + static final class SetStream { + @BeforeTemplate + Stream before(Set set) { + return set.stream().distinct(); + } + + @AfterTemplate + Stream after(Set set) { + return set.stream(); + } + } + /** Prefer {@link ArrayList#ArrayList(Collection)} over the Guava alternative. */ @SuppressWarnings( "NonApiType" /* Matching against `List` would unnecessarily constrain the rule. */) @@ -276,6 +297,23 @@ final class CollectionRules { } } + /** Prefer {@link Arrays#asList(Object[])} over more contrived alternatives. */ + // XXX: Consider moving this rule to `ImmutableListRules` and having it suggest + // `ImmutableList#copyOf`. That would retain immutability, at the cost of no longer handling + // `null`s. + static final class ArraysAsList { + // XXX: This expression produces an unmodifiable list, while the alternative doesn't. + @BeforeTemplate + List before(@NotMatches(IsRefasterAsVarargs.class) T[] array) { + return Arrays.stream(array).toList(); + } + + @AfterTemplate + List after(T[] array) { + return Arrays.asList(array); + } + } + /** Prefer calling {@link Collection#toArray()} over more contrived alternatives. */ static final class CollectionToArray { @BeforeTemplate 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 c6d537c0..932e5e90 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 @@ -16,8 +16,11 @@ import com.google.common.collect.ImmutableSet; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.refaster.Refaster; import com.google.errorprone.refaster.annotation.AfterTemplate; +import com.google.errorprone.refaster.annotation.AlsoNegation; import com.google.errorprone.refaster.annotation.BeforeTemplate; import com.google.errorprone.refaster.annotation.Matches; +import com.google.errorprone.refaster.annotation.MayOptionallyUse; +import com.google.errorprone.refaster.annotation.Placeholder; import com.google.errorprone.refaster.annotation.Repeated; import com.google.errorprone.refaster.annotation.UseImportPolicy; import java.util.Arrays; @@ -92,6 +95,24 @@ final class ComparatorRules { } } + /** Don't explicitly compare enums by their ordinal. */ + abstract static class ComparingEnum, T> { + @Placeholder(allowsIdentity = true) + abstract E toEnumFunction(@MayOptionallyUse T value); + + @BeforeTemplate + @SuppressWarnings("EnumOrdinal" /* This violation will be rewritten. */) + Comparator before() { + return comparingInt(v -> toEnumFunction(v).ordinal()); + } + + @AfterTemplate + @UseImportPolicy(STATIC_IMPORT_ALWAYS) + Comparator after() { + return comparing(v -> toEnumFunction(v)); + } + } + /** Don't explicitly create {@link Comparator}s unnecessarily. */ static final class ThenComparing> { @BeforeTemplate @@ -269,7 +290,7 @@ final class ComparatorRules { static final class MinOfPairCustomOrder { @BeforeTemplate @SuppressWarnings("java:S1067" /* The conditional operators are independent. */) - T before(T value1, T value2, Comparator cmp) { + T before(T value1, T value2, Comparator cmp) { return Refaster.anyOf( cmp.compare(value1, value2) <= 0 ? value1 : value2, cmp.compare(value1, value2) > 0 ? value2 : value1, @@ -284,7 +305,7 @@ final class ComparatorRules { } @AfterTemplate - T after(T value1, T value2, Comparator cmp) { + T after(T value1, T value2, Comparator cmp) { return Comparators.min(value1, value2, cmp); } } @@ -336,7 +357,7 @@ final class ComparatorRules { static final class MaxOfPairCustomOrder { @BeforeTemplate @SuppressWarnings("java:S1067" /* The conditional operators are independent. */) - T before(T value1, T value2, Comparator cmp) { + T before(T value1, T value2, Comparator cmp) { return Refaster.anyOf( cmp.compare(value1, value2) >= 0 ? value1 : value2, cmp.compare(value1, value2) < 0 ? value2 : value1, @@ -351,7 +372,7 @@ final class ComparatorRules { } @AfterTemplate - T after(T value1, T value2, Comparator cmp) { + T after(T value1, T value2, Comparator cmp) { return Comparators.max(value1, value2, cmp); } } @@ -419,4 +440,34 @@ final class ComparatorRules { return maxBy(naturalOrder()); } } + + /** Don't explicitly compare enums by their ordinal. */ + static final class IsLessThan> { + @BeforeTemplate + @SuppressWarnings("EnumOrdinal" /* This violation will be rewritten. */) + boolean before(E value1, E value2) { + return value1.ordinal() < value2.ordinal(); + } + + @AfterTemplate + @AlsoNegation + boolean after(E value1, E value2) { + return value1.compareTo(value2) < 0; + } + } + + /** Don't explicitly compare enums by their ordinal. */ + static final class IsLessThanOrEqualTo> { + @BeforeTemplate + @SuppressWarnings("EnumOrdinal" /* This violation will be rewritten. */) + boolean before(E value1, E value2) { + return value1.ordinal() <= value2.ordinal(); + } + + @AfterTemplate + @AlsoNegation + boolean after(E value1, E value2) { + return value1.compareTo(value2) <= 0; + } + } } 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 e47918d4..dcce6aa0 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/EqualityRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/EqualityRules.java @@ -1,5 +1,6 @@ package tech.picnic.errorprone.refasterrules; +import static java.util.function.Predicate.isEqual; import static java.util.function.Predicate.not; import com.google.errorprone.annotations.CanIgnoreReturnValue; @@ -19,9 +20,9 @@ import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; final class EqualityRules { private EqualityRules() {} - /** Prefer reference-based quality for enums. */ - // Primitive value comparisons are not listed, because Error Prone flags those out of the box. - static final class PrimitiveOrReferenceEquality> { + /** Prefer reference-based equality for enums. */ + // Primitive value comparisons are not matched, because Error Prone flags those out of the box. + static final class EnumReferenceEquality> { /** * Enums can be compared by reference. It is safe to do so even in the face of refactorings, * because if the type is ever converted to a non-enum, then Error-Prone will complain about any @@ -30,8 +31,9 @@ final class EqualityRules { // XXX: This Refaster rule is the topic of https://github.com/google/error-prone/issues/559. We // work around the issue by selecting the "largest replacements". See the `Refaster` check. @BeforeTemplate + @SuppressWarnings("EnumOrdinal" /* This violation will be rewritten. */) boolean before(T a, T b) { - return Refaster.anyOf(a.equals(b), Objects.equals(a, b)); + return Refaster.anyOf(a.equals(b), Objects.equals(a, b), a.ordinal() == b.ordinal()); } @AfterTemplate @@ -42,6 +44,20 @@ final class EqualityRules { } } + /** Prefer reference-based equality for enums. */ + static final class EnumReferenceEqualityLambda> { + @BeforeTemplate + Predicate before(T e) { + return Refaster.anyOf(isEqual(e), e::equals); + } + + @AfterTemplate + @SuppressWarnings("java:S1698" /* Reference comparison is valid for enums. */) + Predicate after(T e) { + return v -> v == e; + } + } + /** Prefer {@link Object#equals(Object)} over the equivalent lambda function. */ // XXX: As it stands, this rule is a special case of what `MethodReferenceUsage` tries to achieve. // If/when `MethodReferenceUsage` becomes production ready, we should simply drop this check. diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/FileRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/FileRules.java index bc230e0a..3a45fe1e 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/FileRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/FileRules.java @@ -2,12 +2,15 @@ package tech.picnic.errorprone.refasterrules; import static java.nio.charset.StandardCharsets.UTF_8; +import com.google.errorprone.refaster.Refaster; import com.google.errorprone.refaster.annotation.AfterTemplate; import com.google.errorprone.refaster.annotation.BeforeTemplate; +import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.attribute.FileAttribute; import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; /** Refaster rules related to expressions dealing with files. */ @@ -40,4 +43,24 @@ final class FileRules { return Files.readString(path); } } + + /** + * Prefer {@link Files#createTempFile(String, String, FileAttribute[])} over alternatives that + * create files with more liberal permissions. + */ + static final class FilesCreateTempFileToFile { + @BeforeTemplate + @SuppressWarnings("java:S5443" /* This violation will be rewritten. */) + File before(String prefix, String suffix) throws IOException { + return Refaster.anyOf( + File.createTempFile(prefix, suffix), File.createTempFile(prefix, suffix, null)); + } + + @AfterTemplate + @SuppressWarnings( + "java:S5443" /* On POSIX systems the file will only have user read-write permissions. */) + File after(String prefix, String suffix) throws IOException { + return Files.createTempFile(prefix, suffix).toFile(); + } + } } diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/JUnitToAssertJRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/JUnitToAssertJRules.java index 23c3bc16..e7915237 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/JUnitToAssertJRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/JUnitToAssertJRules.java @@ -302,16 +302,22 @@ import tech.picnic.errorprone.refaster.annotation.TypeMigration; final class JUnitToAssertJRules { private JUnitToAssertJRules() {} - static final class ThrowNewAssertionError { + static final class Fail { @BeforeTemplate - void before() { - Assertions.fail(); + T before() { + return Assertions.fail(); } + // XXX: Add `@UseImportPolicy(STATIC_IMPORT_ALWAYS)` once + // https://github.com/google/error-prone/pull/3584 is resolved. Until that time, statically + // importing AssertJ's `fail` is likely to clash with an existing static import of JUnit's + // `fail`. Note that combining Error Prone's `RemoveUnusedImports` and + // `UnnecessarilyFullyQualified` checks and our `StaticImport` check will anyway cause the + // method to be imported statically if possible; just in a less efficient manner. @AfterTemplate @DoNotCall - void after() { - throw new AssertionError(); + T after() { + return fail(); } } @@ -321,12 +327,7 @@ final class JUnitToAssertJRules { return Assertions.fail(message); } - // XXX: Add `@UseImportPolicy(STATIC_IMPORT_ALWAYS)` once - // https://github.com/google/error-prone/pull/3584 is resolved. Until that time, statically - // importing AssertJ's `fail` is likely to clash with an existing static import of JUnit's - // `fail`. Note that combining Error Prone's `RemoveUnusedImports` and - // `UnnecessarilyFullyQualified` checks and our `StaticImport` check will anyway cause the - // method to be imported statically if possible; just in a less efficient manner. + // XXX: Add `@UseImportPolicy(STATIC_IMPORT_ALWAYS)`. See `Fail` comment. @AfterTemplate T after(String message) { return fail(message); @@ -339,28 +340,24 @@ final class JUnitToAssertJRules { return Assertions.fail(message, throwable); } - // XXX: Add `@UseImportPolicy(STATIC_IMPORT_ALWAYS)` once - // https://github.com/google/error-prone/pull/3584 is resolved. Until that time, statically - // importing AssertJ's `fail` is likely to clash with an existing static import of JUnit's - // `fail`. Note that combining Error Prone's `RemoveUnusedImports` and - // `UnnecessarilyFullyQualified` checks and our `StaticImport` check will anyway cause the - // method to be imported statically if possible; just in a less efficient manner. + // XXX: Add `@UseImportPolicy(STATIC_IMPORT_ALWAYS)`. See `Fail` comment. @AfterTemplate T after(String message, Throwable throwable) { return fail(message, throwable); } } - static final class FailWithThrowable { + static final class FailWithThrowable { @BeforeTemplate - void before(Throwable throwable) { - Assertions.fail(throwable); + T before(Throwable throwable) { + return Assertions.fail(throwable); } + // XXX: Add `@UseImportPolicy(STATIC_IMPORT_ALWAYS)`. See `Fail` comment. @AfterTemplate @DoNotCall - void after(Throwable throwable) { - throw new AssertionError(throwable); + T after(Throwable throwable) { + return fail(throwable); } } 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 1491b18e..9f73ae38 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 @@ -27,6 +27,19 @@ import tech.picnic.errorprone.refaster.matchers.IsLikelyTrivialComputation; final class OptionalRules { private OptionalRules() {} + /** Prefer {@link Optional#empty()} over the more contrived alternative. */ + static final class OptionalEmpty { + @BeforeTemplate + Optional before() { + return Optional.ofNullable(null); + } + + @AfterTemplate + Optional after() { + return Optional.empty(); + } + } + static final class OptionalOfNullable { // XXX: Refaster should be smart enough to also rewrite occurrences in which there are // parentheses around the null check, but that's currently not the case. Try to fix that. @@ -360,7 +373,7 @@ final class OptionalRules { /** Prefer {@link Optional#or(Supplier)} over more verbose alternatives. */ static final class OptionalOrOtherOptional { @BeforeTemplate - @SuppressWarnings("NestedOptionals" /* Auto-fix for the `NestedOptionals` check. */) + @SuppressWarnings("NestedOptionals") Optional before(Optional optional1, Optional optional2) { // XXX: Note that rewriting the first and third variant will change the code's behavior if // `optional2` has side-effects. @@ -386,9 +399,13 @@ final class OptionalRules { */ static final class OptionalIdentity { @BeforeTemplate + @SuppressWarnings("NestedOptionals") Optional before(Optional optional, Comparator comparator) { return Refaster.anyOf( optional.or(Refaster.anyOf(() -> Optional.empty(), Optional::empty)), + optional + .map(Optional::of) + .orElseGet(Refaster.anyOf(() -> Optional.empty(), Optional::empty)), optional.stream().findFirst(), optional.stream().findAny(), optional.stream().min(comparator), @@ -442,9 +459,7 @@ final class OptionalRules { static final class OptionalStream { @BeforeTemplate Stream before(Optional optional) { - return Refaster.anyOf( - optional.map(Stream::of).orElse(Stream.empty()), - optional.map(Stream::of).orElseGet(Stream::empty)); + return optional.map(Stream::of).orElseGet(Stream::empty); } @AfterTemplate 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 57cb55a6..f29c0029 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 @@ -3,7 +3,6 @@ package tech.picnic.errorprone.refasterrules; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.MoreCollectors.toOptional; -import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS; import static java.util.Comparator.naturalOrder; import static java.util.Comparator.reverseOrder; @@ -34,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -41,6 +41,7 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collector; +import java.util.stream.Stream; import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; @@ -52,7 +53,6 @@ import reactor.util.context.Context; import reactor.util.function.Tuple2; import tech.picnic.errorprone.refaster.annotation.Description; import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; -import tech.picnic.errorprone.refaster.annotation.Severity; import tech.picnic.errorprone.refaster.matchers.IsEmpty; import tech.picnic.errorprone.refaster.matchers.IsIdentityOperation; import tech.picnic.errorprone.refaster.matchers.ThrowsCheckedException; @@ -380,30 +380,23 @@ final class ReactorRules { } /** - * Prefer {@link Flux#take(long, boolean)} over {@link Flux#take(long)}. + * Prefer {@link Flux#take(long)} over {@link Flux#take(long, boolean)} where relevant. * *

In Reactor versions prior to 3.5.0, {@code Flux#take(long)} makes an unbounded request - * upstream, and is equivalent to {@code Flux#take(long, false)}. In 3.5.0, the behavior of {@code - * Flux#take(long)} will change to that of {@code Flux#take(long, true)}. - * - *

The intent with this Refaster rule is to get the new behavior before upgrading to Reactor - * 3.5.0. + * upstream, and is equivalent to {@code Flux#take(long, false)}. From version 3.5.0 onwards, the + * behavior of {@code Flux#take(long)} instead matches {@code Flux#take(long, true)}. */ - // XXX: Drop this rule some time after upgrading to Reactor 3.6.0, or introduce a way to apply - // this rule only when an older version of Reactor is on the classpath. - // XXX: Once Reactor 3.6.0 is out, introduce a rule that rewrites code in the opposite direction. @Description( - "Prior to Reactor 3.5.0, `take(n)` requests and unbounded number of elements upstream.") - @Severity(WARNING) + "From Reactor 3.5.0 onwards, `take(n)` no longer requests an unbounded number of elements upstream.") static final class FluxTake { @BeforeTemplate Flux before(Flux flux, long n) { - return flux.take(n); + return flux.take(n, /* limitRequest= */ true); } @AfterTemplate Flux after(Flux flux, long n) { - return flux.take(n, /* limitRequest= */ true); + return flux.take(n); } } @@ -489,15 +482,20 @@ final class ReactorRules { } /** Prefer {@link Flux#just(Object)} over more contrived alternatives. */ - static final class FluxJust { + static final class FluxJust { @BeforeTemplate - Flux before(int start) { - return Flux.range(start, 1); + Flux before(int value) { + return Flux.range(value, 1); + } + + @BeforeTemplate + Flux before(T value) { + return Mono.just(value).repeat().take(1); } @AfterTemplate - Flux after(int start) { - return Flux.just(start); + Flux after(T value) { + return Flux.just(value); } } @@ -566,6 +564,7 @@ final class ReactorRules { @Matches(IsIdentityOperation.class) Function> identityOperation) { return Refaster.anyOf( + flux.concatMap(function, 0), flux.flatMap(function, 1), flux.flatMapSequential(function, 1), flux.map(function).concatMap(identityOperation)); @@ -1206,10 +1205,17 @@ final class ReactorRules { } /** Prefer {@link Flux#fromIterable(Iterable)} over less efficient alternatives. */ + // XXX: Once the `FluxFromStreamSupplier` rule is constrained using + // `@NotMatches(IsIdentityOperation.class)`, this rule should also cover + // `Flux.fromStream(collection.stream())`. static final class FluxFromIterable { + // XXX: Once the `MethodReferenceUsage` check is generally enabled, drop the second + // `Refaster.anyOf` variant. @BeforeTemplate Flux before(Collection collection) { - return Flux.fromStream(collection.stream()); + return Flux.fromStream( + Refaster.>>anyOf( + collection::stream, () -> collection.stream())); } @AfterTemplate @@ -1912,4 +1918,60 @@ final class ReactorRules { return step.verifyTimeout(duration); } } + + /** + * Prefer {@link Mono#fromFuture(Supplier)} over {@link Mono#fromFuture(CompletableFuture)}, as + * the former may defer initiation of the asynchronous computation until subscription. + */ + static final class MonoFromFutureSupplier { + // XXX: Constrain the `future` parameter using `@NotMatches(IsIdentityOperation.class)` once + // `IsIdentityOperation` no longer matches nullary method invocations. + @BeforeTemplate + Mono before(CompletableFuture future) { + return Mono.fromFuture(future); + } + + @AfterTemplate + Mono after(CompletableFuture future) { + return Mono.fromFuture(() -> future); + } + } + + /** + * Prefer {@link Mono#fromFuture(Supplier, boolean)} over {@link + * Mono#fromFuture(CompletableFuture, boolean)}, as the former may defer initiation of the + * asynchronous computation until subscription. + */ + static final class MonoFromFutureSupplierBoolean { + // XXX: Constrain the `future` parameter using `@NotMatches(IsIdentityOperation.class)` once + // `IsIdentityOperation` no longer matches nullary method invocations. + @BeforeTemplate + Mono before(CompletableFuture future, boolean suppressCancel) { + return Mono.fromFuture(future, suppressCancel); + } + + @AfterTemplate + Mono after(CompletableFuture future, boolean suppressCancel) { + return Mono.fromFuture(() -> future, suppressCancel); + } + } + + /** + * 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 + * once. + */ + static final class FluxFromStreamSupplier { + // XXX: Constrain the `stream` parameter using `@NotMatches(IsIdentityOperation.class)` once + // `IsIdentityOperation` no longer matches nullary method invocations. + @BeforeTemplate + Flux before(Stream stream) { + return Flux.fromStream(stream); + } + + @AfterTemplate + Flux after(Stream stream) { + return Flux.fromStream(() -> stream); + } + } } 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 5ab7f9de..e7bea741 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StreamRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StreamRules.java @@ -25,6 +25,7 @@ import com.google.common.collect.Streams; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.refaster.Refaster; import com.google.errorprone.refaster.annotation.AfterTemplate; +import com.google.errorprone.refaster.annotation.AlsoNegation; import com.google.errorprone.refaster.annotation.BeforeTemplate; import com.google.errorprone.refaster.annotation.Matches; import com.google.errorprone.refaster.annotation.MayOptionallyUse; @@ -256,7 +257,7 @@ final class StreamRules { // XXX: This rule assumes that any matched `Collector` does not perform any filtering. // (Perhaps we could add a `@Matches` guard that validates that the collector expression does not // contain a `Collectors#filtering` call. That'd still not be 100% accurate, though.) - static final class StreamIsEmpty, M extends Map> { + static final class StreamFindAnyIsEmpty, M extends Map> { @BeforeTemplate boolean before(Stream stream, Collector collector) { return Refaster.anyOf( @@ -274,20 +275,20 @@ final class StreamRules { } @AfterTemplate + @AlsoNegation boolean after(Stream stream) { return stream.findAny().isEmpty(); } } - /** In order to test whether a stream has any element, simply try to find one. */ - static final class StreamIsNotEmpty { + /** + * Prefer {@link Stream#findAny()} over {@link Stream#findFirst()} if one only cares whether the + * stream is nonempty. + */ + static final class StreamFindAnyIsPresent { @BeforeTemplate boolean before(Stream stream) { - return Refaster.anyOf( - stream.count() != 0, - stream.count() > 0, - stream.count() >= 1, - stream.findFirst().isPresent()); + return stream.findFirst().isPresent(); } @AfterTemplate diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/TestNGToAssertJRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/TestNGToAssertJRules.java index e9662898..987812ac 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/TestNGToAssertJRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/TestNGToAssertJRules.java @@ -161,8 +161,9 @@ final class TestNGToAssertJRules { @AfterTemplate @DoNotCall + @UseImportPolicy(STATIC_IMPORT_ALWAYS) void after() { - throw new AssertionError(); + fail(); } } diff --git a/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/OptionalOrElseTest.java b/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/OptionalOrElseTest.java new file mode 100644 index 00000000..620a532a --- /dev/null +++ b/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/OptionalOrElseTest.java @@ -0,0 +1,135 @@ +package tech.picnic.errorprone.bugpatterns; + +import com.google.errorprone.BugCheckerRefactoringTestHelper; +import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode; +import com.google.errorprone.CompilationTestHelper; +import org.junit.jupiter.api.Test; + +final class OptionalOrElseTest { + @Test + void identification() { + CompilationTestHelper.newInstance(OptionalOrElse.class, getClass()) + .addSourceLines( + "A.java", + "import com.google.errorprone.refaster.Refaster;", + "import java.util.Optional;", + "", + "class A {", + " private final Optional optional = Optional.empty();", + " private final String string = optional.toString();", + "", + " void m() {", + " Optional.empty().orElse(null);", + " optional.orElse(null);", + " optional.orElse(\"constant\");", + " optional.orElse(\"constant\" + 0);", + " optional.orElse(Boolean.TRUE);", + " optional.orElse(string);", + " optional.orElse(this.string);", + " optional.orElse(Refaster.anyOf(\"constant\", \"another\"));", + "", + " // BUG: Diagnostic contains:", + " Optional.empty().orElse(string + \"constant\");", + " // BUG: Diagnostic contains:", + " optional.orElse(string + \"constant\");", + " // BUG: Diagnostic contains:", + " optional.orElse(\"constant\".toString());", + " // BUG: Diagnostic contains:", + " optional.orElse(string.toString());", + " // BUG: Diagnostic contains:", + " optional.orElse(this.string.toString());", + " // BUG: Diagnostic contains:", + " optional.orElse(String.valueOf(42));", + " // BUG: Diagnostic contains:", + " optional.orElse(string.toString().length());", + " // BUG: Diagnostic contains:", + " optional.orElse(\"constant\".equals(string));", + " // BUG: Diagnostic contains:", + " optional.orElse(string.equals(string));", + " // BUG: Diagnostic contains:", + " optional.orElse(this.string.equals(string));", + " // BUG: Diagnostic contains:", + " optional.orElse(foo());", + " // BUG: Diagnostic contains:", + " optional.orElse(this.foo());", + " // BUG: Diagnostic contains:", + " optional.orElse(new Object() {});", + " // BUG: Diagnostic contains:", + " optional.orElse(new int[0].length);", + " }", + "", + " private T foo() {", + " return null;", + " }", + "}") + .doTest(); + } + + @Test + void replacement() { + BugCheckerRefactoringTestHelper.newInstance(OptionalOrElse.class, getClass()) + .addInputLines( + "A.java", + "import java.util.Optional;", + "", + "class A {", + " private final Optional optional = Optional.empty();", + " private final String string = optional.toString();", + "", + " void m() {", + " optional.orElse(string + \"constant\");", + " optional.orElse(\"constant\".toString());", + " optional.orElse(string.toString());", + " optional.orElse(this.string.toString());", + " optional.orElse(String.valueOf(42));", + " optional.orElse(string.toString().length());", + " optional.orElse(string.equals(string));", + " optional.orElse(foo());", + " optional.orElse(this.foo());", + " optional.orElse(this.bar());", + " optional.orElse(new Object() {});", + " optional.orElse(new int[0].length);", + " }", + "", + " private T foo() {", + " return null;", + " }", + "", + " private T bar() {", + " return null;", + " }", + "}") + .addOutputLines( + "A.java", + "import java.util.Optional;", + "", + "class A {", + " private final Optional optional = Optional.empty();", + " private final String string = optional.toString();", + "", + " void m() {", + " optional.orElseGet(() -> string + \"constant\");", + " optional.orElseGet(\"constant\"::toString);", + " optional.orElseGet(string::toString);", + " optional.orElseGet(this.string::toString);", + " optional.orElseGet(() -> String.valueOf(42));", + " optional.orElseGet(() -> string.toString().length());", + " optional.orElseGet(() -> string.equals(string));", + " optional.orElseGet(() -> foo());", + " optional.orElseGet(this::foo);", + " optional.orElseGet(this::bar);", + " optional.orElseGet(() -> new Object() {});", + " optional.orElseGet(() -> new int[0].length);", + " }", + "", + " private T foo() {", + " return null;", + " }", + "", + " private T bar() {", + " return null;", + " }", + "}") + .doTest(TestMode.TEXT_MATCH); + } +} diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/ClassRulesTestInput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/ClassRulesTestInput.java index 67826082..ec960733 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/ClassRulesTestInput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/ClassRulesTestInput.java @@ -1,26 +1,34 @@ package tech.picnic.errorprone.refasterrules.input; import com.google.common.collect.ImmutableSet; -import java.io.IOException; +import java.util.function.Function; import java.util.function.Predicate; import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase; final class ClassRulesTest implements RefasterRuleCollectionTestCase { - boolean testClassIsInstance() throws IOException { + boolean testClassIsInstance() { return CharSequence.class.isAssignableFrom("foo".getClass()); } - ImmutableSet testInstanceof() throws IOException { + ImmutableSet testInstanceof() { Class clazz = CharSequence.class; return ImmutableSet.of(CharSequence.class.isInstance("foo"), clazz.isInstance("bar")); } - Predicate testClassLiteralIsInstancePredicate() throws IOException { + Predicate testClassLiteralIsInstancePredicate() { return s -> s instanceof CharSequence; } - Predicate testClassReferenceIsInstancePredicate() throws IOException { + Predicate testClassReferenceIsInstancePredicate() { Class clazz = CharSequence.class; return s -> clazz.isInstance(s); } + + Function testClassLiteralCast() { + return i -> (Integer) i; + } + + Function testClassReferenceCast() { + return i -> Integer.class.cast(i); + } } diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/CollectionRulesTestInput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/CollectionRulesTestInput.java index 94a816f4..e39e5516 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/CollectionRulesTestInput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/CollectionRulesTestInput.java @@ -6,9 +6,11 @@ import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; +import java.util.List; import java.util.Optional; import java.util.TreeSet; import java.util.stream.Stream; @@ -70,6 +72,10 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase { } } + Stream testSetStream() { + return ImmutableSet.of(1).stream().distinct(); + } + ArrayList testNewArrayListFromCollection() { return Lists.newArrayList(ImmutableList.of("foo")); } @@ -94,6 +100,10 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase { return ImmutableSet.of(1).asList().toString(); } + List testArraysAsList() { + return Arrays.stream(new String[0]).toList(); + } + ImmutableSet testCollectionToArray() { return ImmutableSet.of( ImmutableSet.of(1).toArray(new Object[1]), diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/ComparatorRulesTestInput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/ComparatorRulesTestInput.java index e0a98019..1cf07553 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/ComparatorRulesTestInput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/ComparatorRulesTestInput.java @@ -9,6 +9,7 @@ import static java.util.stream.Collectors.minBy; import com.google.common.collect.Comparators; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import java.math.RoundingMode; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; @@ -54,6 +55,10 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase { Comparator.comparing(s -> "foo", Comparator.comparingInt(String::length))); } + Comparator testComparingEnum() { + return Comparator.comparingInt(s -> RoundingMode.valueOf(s).ordinal()); + } + Comparator testThenComparing() { return Comparator.naturalOrder().thenComparing(Comparator.comparing(String::isEmpty)); } @@ -173,4 +178,16 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase { Collector> testMaxByNaturalOrder() { return minBy(reverseOrder()); } + + ImmutableSet testIsLessThan() { + return ImmutableSet.of( + RoundingMode.UP.ordinal() < RoundingMode.DOWN.ordinal(), + RoundingMode.UP.ordinal() >= RoundingMode.DOWN.ordinal()); + } + + ImmutableSet testIsLessThanOrEqualTo() { + return ImmutableSet.of( + RoundingMode.UP.ordinal() <= RoundingMode.DOWN.ordinal(), + RoundingMode.UP.ordinal() > RoundingMode.DOWN.ordinal()); + } } diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/EqualityRulesTestInput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/EqualityRulesTestInput.java index f8b01733..edad4fe2 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/EqualityRulesTestInput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/EqualityRulesTestInput.java @@ -1,5 +1,6 @@ package tech.picnic.errorprone.refasterrules.input; +import static java.util.function.Predicate.isEqual; import static java.util.function.Predicate.not; import com.google.common.collect.BoundType; @@ -14,15 +15,21 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase; final class EqualityRulesTest implements RefasterRuleCollectionTestCase { @Override public ImmutableSet elidedTypesAndStaticImports() { - return ImmutableSet.of(Objects.class, Optional.class, not(null)); + return ImmutableSet.of(Objects.class, Optional.class, isEqual(null), not(null)); } - ImmutableSet testPrimitiveOrReferenceEquality() { + ImmutableSet testEnumReferenceEquality() { return ImmutableSet.of( RoundingMode.UP.equals(RoundingMode.DOWN), Objects.equals(RoundingMode.UP, RoundingMode.DOWN), + RoundingMode.UP.ordinal() == RoundingMode.DOWN.ordinal(), !RoundingMode.UP.equals(RoundingMode.DOWN), - !Objects.equals(RoundingMode.UP, RoundingMode.DOWN)); + !Objects.equals(RoundingMode.UP, RoundingMode.DOWN), + RoundingMode.UP.ordinal() != RoundingMode.DOWN.ordinal()); + } + + ImmutableSet> testEnumReferenceEqualityLambda() { + return ImmutableSet.of(isEqual(RoundingMode.DOWN), RoundingMode.UP::equals); } boolean testEqualsPredicate() { diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/FileRulesTestInput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/FileRulesTestInput.java index d9bcc029..e46d52a7 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/FileRulesTestInput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/FileRulesTestInput.java @@ -1,5 +1,7 @@ package tech.picnic.errorprone.refasterrules.input; +import com.google.common.collect.ImmutableSet; +import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -14,4 +16,9 @@ final class FileRulesTest implements RefasterRuleCollectionTestCase { String testFilesReadString() throws IOException { return Files.readString(Paths.get("foo"), StandardCharsets.UTF_8); } + + ImmutableSet testFilesCreateTempFileToFile() throws IOException { + return ImmutableSet.of( + File.createTempFile("foo", "bar"), File.createTempFile("baz", "qux", null)); + } } diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/JUnitToAssertJRulesTestInput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/JUnitToAssertJRulesTestInput.java index 46eb0332..d417332f 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/JUnitToAssertJRulesTestInput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/JUnitToAssertJRulesTestInput.java @@ -32,8 +32,8 @@ final class JUnitToAssertJRulesTest implements RefasterRuleCollectionTestCase { (Runnable) () -> assertTrue(true)); } - void testThrowNewAssertionError() { - Assertions.fail(); + Object testFail() { + return Assertions.fail(); } Object testFailWithMessage() { @@ -44,8 +44,8 @@ final class JUnitToAssertJRulesTest implements RefasterRuleCollectionTestCase { return Assertions.fail("foo", new IllegalStateException()); } - void testFailWithThrowable() { - Assertions.fail(new IllegalStateException()); + Object testFailWithThrowable() { + return Assertions.fail(new IllegalStateException()); } void testAssertThatIsTrue() { diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/OptionalRulesTestInput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/OptionalRulesTestInput.java index 5b9adc94..8369dce2 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/OptionalRulesTestInput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/OptionalRulesTestInput.java @@ -13,6 +13,10 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase { return ImmutableSet.of(Streams.class); } + Optional testOptionalEmpty() { + return Optional.ofNullable(null); + } + ImmutableSet> testOptionalOfNullable() { return ImmutableSet.of( toString() == null ? Optional.empty() : Optional.of(toString()), @@ -120,10 +124,12 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase { return ImmutableSet.of( Optional.of("foo").or(() -> Optional.empty()), Optional.of("bar").or(Optional::empty), - Optional.of("baz").stream().findFirst(), - Optional.of("qux").stream().findAny(), - Optional.of("quux").stream().min(String::compareTo), - Optional.of("quuz").stream().max(String::compareTo)); + Optional.of("baz").map(Optional::of).orElseGet(() -> Optional.empty()), + Optional.of("qux").map(Optional::of).orElseGet(Optional::empty), + Optional.of("quux").stream().findFirst(), + Optional.of("quuz").stream().findAny(), + Optional.of("corge").stream().min(String::compareTo), + Optional.of("grault").stream().max(String::compareTo)); } ImmutableSet> testOptionalFilter() { @@ -136,9 +142,7 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase { return Optional.of(1).stream().map(String::valueOf).findAny(); } - ImmutableSet> testOptionalStream() { - return ImmutableSet.of( - Optional.of("foo").map(Stream::of).orElse(Stream.empty()), - Optional.of("bar").map(Stream::of).orElseGet(Stream::empty)); + Stream testOptionalStream() { + return Optional.of("foo").map(Stream::of).orElseGet(Stream::empty); } } diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/ReactorRulesTestInput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/ReactorRulesTestInput.java index 965a7ef9..857297e3 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/ReactorRulesTestInput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/ReactorRulesTestInput.java @@ -21,7 +21,9 @@ import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; +import java.util.stream.Stream; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.math.MathFlux; @@ -142,7 +144,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase { } Flux testFluxTake() { - return Flux.just(1, 2, 3).take(1); + return Flux.just(1, 2, 3).take(1, true); } Mono testMonoDefaultIfEmpty() { @@ -182,8 +184,8 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase { Flux.range(0, 0)); } - Flux testFluxJust() { - return Flux.range(0, 1); + ImmutableSet> testFluxJust() { + return ImmutableSet.of(Flux.range(0, 1), Mono.just(2).repeat().take(1)); } ImmutableSet> testMonoIdentity() { @@ -207,11 +209,12 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase { ImmutableSet> testFluxConcatMap() { return ImmutableSet.of( - Flux.just(1).flatMap(Mono::just, 1), - Flux.just(2).flatMapSequential(Mono::just, 1), - Flux.just(3).map(Mono::just).concatMap(identity()), - Flux.just(4).map(Mono::just).concatMap(v -> v), - Flux.just(5).map(Mono::just).concatMap(v -> Mono.empty())); + Flux.just(1).concatMap(Mono::just, 0), + Flux.just(2).flatMap(Mono::just, 1), + Flux.just(3).flatMapSequential(Mono::just, 1), + Flux.just(4).map(Mono::just).concatMap(identity()), + Flux.just(5).map(Mono::just).concatMap(v -> v), + Flux.just(6).map(Mono::just).concatMap(v -> Mono.empty())); } ImmutableSet> testFluxConcatMapWithPrefetch() { @@ -432,8 +435,10 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase { Flux.just(ImmutableList.of("bar")).concatMap(Flux::fromIterable, 2)); } - Flux testFluxFromIterable() { - return Flux.fromStream(ImmutableList.of("foo").stream()); + ImmutableSet> testFluxFromIterable() { + return ImmutableSet.of( + Flux.fromStream(ImmutableList.of("foo")::stream), + Flux.fromStream(() -> ImmutableList.of("bar").stream())); } ImmutableSet> testFluxCountMapMathToIntExact() { @@ -650,4 +655,16 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase { Duration testStepVerifierLastStepVerifyTimeout() { return Mono.empty().as(StepVerifier::create).expectTimeout(Duration.ZERO).verify(); } + + Mono testMonoFromFutureSupplier() { + return Mono.fromFuture(CompletableFuture.completedFuture(null)); + } + + Mono testMonoFromFutureSupplierBoolean() { + return Mono.fromFuture(CompletableFuture.completedFuture(null), true); + } + + Flux testFluxFromStreamSupplier() { + return Flux.fromStream(Stream.of(1)); + } } diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/StreamRulesTestInput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/StreamRulesTestInput.java index fb5b00c0..8c39dcae 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/StreamRulesTestInput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/input/StreamRulesTestInput.java @@ -120,7 +120,7 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase { Stream.of("bar").map(String::length).findFirst()); } - ImmutableSet testStreamIsEmpty() { + ImmutableSet testStreamFindAnyIsEmpty() { return ImmutableSet.of( Stream.of(1).count() == 0, Stream.of(2).count() <= 0, @@ -131,15 +131,14 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase { Stream.of(7).collect(collectingAndThen(toImmutableList(), ImmutableList::isEmpty)), Stream.of(8).collect(collectingAndThen(toImmutableMap(k -> k, v -> v), Map::isEmpty)), Stream.of(9) - .collect(collectingAndThen(toImmutableMap(k -> k, v -> v), ImmutableMap::isEmpty))); + .collect(collectingAndThen(toImmutableMap(k -> k, v -> v), ImmutableMap::isEmpty)), + Stream.of(10).count() != 0, + Stream.of(11).count() > 0, + Stream.of(12).count() >= 1); } - ImmutableSet testStreamIsNotEmpty() { - return ImmutableSet.of( - Stream.of(1).count() != 0, - Stream.of(2).count() > 0, - Stream.of(3).count() >= 1, - Stream.of(4).findFirst().isPresent()); + boolean testStreamFindAnyIsPresent() { + return Stream.of(1).findFirst().isPresent(); } ImmutableSet> testStreamMin() { diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/ClassRulesTestOutput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/ClassRulesTestOutput.java index 013a4f8d..ff4665b4 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/ClassRulesTestOutput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/ClassRulesTestOutput.java @@ -1,26 +1,34 @@ package tech.picnic.errorprone.refasterrules.output; import com.google.common.collect.ImmutableSet; -import java.io.IOException; +import java.util.function.Function; import java.util.function.Predicate; import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase; final class ClassRulesTest implements RefasterRuleCollectionTestCase { - boolean testClassIsInstance() throws IOException { + boolean testClassIsInstance() { return CharSequence.class.isInstance("foo"); } - ImmutableSet testInstanceof() throws IOException { + ImmutableSet testInstanceof() { Class clazz = CharSequence.class; return ImmutableSet.of("foo" instanceof CharSequence, clazz.isInstance("bar")); } - Predicate testClassLiteralIsInstancePredicate() throws IOException { + Predicate testClassLiteralIsInstancePredicate() { return CharSequence.class::isInstance; } - Predicate testClassReferenceIsInstancePredicate() throws IOException { + Predicate testClassReferenceIsInstancePredicate() { Class clazz = CharSequence.class; return clazz::isInstance; } + + Function testClassLiteralCast() { + return Integer.class::cast; + } + + Function testClassReferenceCast() { + return Integer.class::cast; + } } diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/CollectionRulesTestOutput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/CollectionRulesTestOutput.java index ff0d12e7..e0a90f5e 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/CollectionRulesTestOutput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/CollectionRulesTestOutput.java @@ -6,9 +6,11 @@ import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; +import java.util.List; import java.util.Optional; import java.util.TreeSet; import java.util.stream.Stream; @@ -62,6 +64,10 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase { new HashSet().removeAll(ImmutableSet.of(2)); } + Stream testSetStream() { + return ImmutableSet.of(1).stream(); + } + ArrayList testNewArrayListFromCollection() { return new ArrayList<>(ImmutableList.of("foo")); } @@ -86,6 +92,10 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase { return ImmutableSet.of(1).toString(); } + List testArraysAsList() { + return Arrays.asList(new String[0]); + } + ImmutableSet testCollectionToArray() { return ImmutableSet.of( ImmutableSet.of(1).toArray(), ImmutableSet.of(2).toArray(), ImmutableSet.of(3).toArray()); diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/ComparatorRulesTestOutput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/ComparatorRulesTestOutput.java index 63c23b42..a6bc24a1 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/ComparatorRulesTestOutput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/ComparatorRulesTestOutput.java @@ -1,5 +1,6 @@ package tech.picnic.errorprone.refasterrules.output; +import static java.util.Comparator.comparing; import static java.util.Comparator.naturalOrder; import static java.util.Comparator.reverseOrder; import static java.util.function.Function.identity; @@ -9,6 +10,7 @@ import static java.util.stream.Collectors.minBy; import com.google.common.collect.Comparators; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import java.math.RoundingMode; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; @@ -52,6 +54,10 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase { Comparator.comparing(s -> "foo", Comparator.comparingInt(String::length))); } + Comparator testComparingEnum() { + return comparing(s -> RoundingMode.valueOf(s)); + } + Comparator testThenComparing() { return Comparator.naturalOrder().thenComparing(String::isEmpty); } @@ -163,4 +169,16 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase { Collector> testMaxByNaturalOrder() { return maxBy(naturalOrder()); } + + ImmutableSet testIsLessThan() { + return ImmutableSet.of( + RoundingMode.UP.compareTo(RoundingMode.DOWN) < 0, + RoundingMode.UP.compareTo(RoundingMode.DOWN) >= 0); + } + + ImmutableSet testIsLessThanOrEqualTo() { + return ImmutableSet.of( + RoundingMode.UP.compareTo(RoundingMode.DOWN) <= 0, + RoundingMode.UP.compareTo(RoundingMode.DOWN) > 0); + } } diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/EqualityRulesTestOutput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/EqualityRulesTestOutput.java index 531ab4a4..bce1681c 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/EqualityRulesTestOutput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/EqualityRulesTestOutput.java @@ -1,5 +1,6 @@ package tech.picnic.errorprone.refasterrules.output; +import static java.util.function.Predicate.isEqual; import static java.util.function.Predicate.not; import com.google.common.collect.BoundType; @@ -14,17 +15,23 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase; final class EqualityRulesTest implements RefasterRuleCollectionTestCase { @Override public ImmutableSet elidedTypesAndStaticImports() { - return ImmutableSet.of(Objects.class, Optional.class, not(null)); + return ImmutableSet.of(Objects.class, Optional.class, isEqual(null), not(null)); } - ImmutableSet testPrimitiveOrReferenceEquality() { + ImmutableSet testEnumReferenceEquality() { return ImmutableSet.of( RoundingMode.UP == RoundingMode.DOWN, RoundingMode.UP == RoundingMode.DOWN, + RoundingMode.UP == RoundingMode.DOWN, + RoundingMode.UP != RoundingMode.DOWN, RoundingMode.UP != RoundingMode.DOWN, RoundingMode.UP != RoundingMode.DOWN); } + ImmutableSet> testEnumReferenceEqualityLambda() { + return ImmutableSet.of(v -> v == RoundingMode.DOWN, v -> v == RoundingMode.UP); + } + boolean testEqualsPredicate() { // XXX: When boxing is involved this rule seems to break. Example: // Stream.of(1).anyMatch(e -> Integer.MIN_VALUE.equals(e)); diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/FileRulesTestOutput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/FileRulesTestOutput.java index e7b2208f..c9331f81 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/FileRulesTestOutput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/FileRulesTestOutput.java @@ -1,5 +1,7 @@ package tech.picnic.errorprone.refasterrules.output; +import com.google.common.collect.ImmutableSet; +import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -14,4 +16,9 @@ final class FileRulesTest implements RefasterRuleCollectionTestCase { String testFilesReadString() throws IOException { return Files.readString(Paths.get("foo")); } + + ImmutableSet testFilesCreateTempFileToFile() throws IOException { + return ImmutableSet.of( + Files.createTempFile("foo", "bar").toFile(), Files.createTempFile("baz", "qux").toFile()); + } } diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/JUnitToAssertJRulesTestOutput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/JUnitToAssertJRulesTestOutput.java index 9f102edd..1a8ea264 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/JUnitToAssertJRulesTestOutput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/JUnitToAssertJRulesTestOutput.java @@ -35,8 +35,8 @@ final class JUnitToAssertJRulesTest implements RefasterRuleCollectionTestCase { (Runnable) () -> assertTrue(true)); } - void testThrowNewAssertionError() { - throw new AssertionError(); + Object testFail() { + return org.assertj.core.api.Assertions.fail(); } Object testFailWithMessage() { @@ -47,8 +47,8 @@ final class JUnitToAssertJRulesTest implements RefasterRuleCollectionTestCase { return org.assertj.core.api.Assertions.fail("foo", new IllegalStateException()); } - void testFailWithThrowable() { - throw new AssertionError(new IllegalStateException()); + Object testFailWithThrowable() { + return org.assertj.core.api.Assertions.fail(new IllegalStateException()); } void testAssertThatIsTrue() { diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/OptionalRulesTestOutput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/OptionalRulesTestOutput.java index b411d1ee..1d248ad0 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/OptionalRulesTestOutput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/OptionalRulesTestOutput.java @@ -15,6 +15,10 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase { return ImmutableSet.of(Streams.class); } + Optional testOptionalEmpty() { + return Optional.empty(); + } + ImmutableSet> testOptionalOfNullable() { return ImmutableSet.of(Optional.ofNullable(toString()), Optional.ofNullable(toString())); } @@ -120,7 +124,9 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase { Optional.of("baz"), Optional.of("qux"), Optional.of("quux"), - Optional.of("quuz")); + Optional.of("quuz"), + Optional.of("corge"), + Optional.of("grault")); } ImmutableSet> testOptionalFilter() { @@ -132,7 +138,7 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase { return Optional.of(1).map(String::valueOf); } - ImmutableSet> testOptionalStream() { - return ImmutableSet.of(Optional.of("foo").stream(), Optional.of("bar").stream()); + Stream testOptionalStream() { + return Optional.of("foo").stream(); } } diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/ReactorRulesTestOutput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/ReactorRulesTestOutput.java index ecc984d6..18a208bc 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/ReactorRulesTestOutput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/ReactorRulesTestOutput.java @@ -23,7 +23,9 @@ import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; +import java.util.stream.Stream; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.function.TupleUtils; @@ -147,7 +149,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase { } Flux testFluxTake() { - return Flux.just(1, 2, 3).take(1, true); + return Flux.just(1, 2, 3).take(1); } Mono testMonoDefaultIfEmpty() { @@ -186,8 +188,8 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase { Flux.empty()); } - Flux testFluxJust() { - return Flux.just(0); + ImmutableSet> testFluxJust() { + return ImmutableSet.of(Flux.just(0), Flux.just(2)); } ImmutableSet> testMonoIdentity() { @@ -214,7 +216,8 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase { Flux.just(2).concatMap(Mono::just), Flux.just(3).concatMap(Mono::just), Flux.just(4).concatMap(Mono::just), - Flux.just(5).map(Mono::just).concatMap(v -> Mono.empty())); + Flux.just(5).concatMap(Mono::just), + Flux.just(6).map(Mono::just).concatMap(v -> Mono.empty())); } ImmutableSet> testFluxConcatMapWithPrefetch() { @@ -427,8 +430,9 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase { Flux.just(ImmutableList.of("bar")).concatMapIterable(identity(), 2)); } - Flux testFluxFromIterable() { - return Flux.fromIterable(ImmutableList.of("foo")); + ImmutableSet> testFluxFromIterable() { + return ImmutableSet.of( + Flux.fromIterable(ImmutableList.of("foo")), Flux.fromIterable(ImmutableList.of("bar"))); } ImmutableSet> testFluxCountMapMathToIntExact() { @@ -631,4 +635,16 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase { Duration testStepVerifierLastStepVerifyTimeout() { return Mono.empty().as(StepVerifier::create).verifyTimeout(Duration.ZERO); } + + Mono testMonoFromFutureSupplier() { + return Mono.fromFuture(() -> CompletableFuture.completedFuture(null)); + } + + Mono testMonoFromFutureSupplierBoolean() { + return Mono.fromFuture(() -> CompletableFuture.completedFuture(null), true); + } + + Flux testFluxFromStreamSupplier() { + return Flux.fromStream(() -> Stream.of(1)); + } } diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/StreamRulesTestOutput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/StreamRulesTestOutput.java index 27aeeedf..5de1b618 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/StreamRulesTestOutput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/StreamRulesTestOutput.java @@ -121,7 +121,7 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase { Stream.of("bar").findFirst().map(String::length)); } - ImmutableSet testStreamIsEmpty() { + ImmutableSet testStreamFindAnyIsEmpty() { return ImmutableSet.of( Stream.of(1).findAny().isEmpty(), Stream.of(2).findAny().isEmpty(), @@ -131,15 +131,14 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase { Stream.of(6).findAny().isEmpty(), Stream.of(7).findAny().isEmpty(), Stream.of(8).findAny().isEmpty(), - Stream.of(9).findAny().isEmpty()); + Stream.of(9).findAny().isEmpty(), + !Stream.of(10).findAny().isEmpty(), + !Stream.of(11).findAny().isEmpty(), + !Stream.of(12).findAny().isEmpty()); } - ImmutableSet testStreamIsNotEmpty() { - return ImmutableSet.of( - Stream.of(1).findAny().isPresent(), - Stream.of(2).findAny().isPresent(), - Stream.of(3).findAny().isPresent(), - Stream.of(4).findAny().isPresent()); + boolean testStreamFindAnyIsPresent() { + return Stream.of(1).findAny().isPresent(); } ImmutableSet> testStreamMin() { diff --git a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/TestNGToAssertJRulesTestOutput.java b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/TestNGToAssertJRulesTestOutput.java index 11588584..c55b2329 100644 --- a/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/TestNGToAssertJRulesTestOutput.java +++ b/error-prone-contrib/src/test/resources/tech/picnic/errorprone/refasterrules/output/TestNGToAssertJRulesTestOutput.java @@ -41,7 +41,7 @@ final class TestNGToAssertJRulesTest implements RefasterRuleCollectionTestCase { } void testFail() { - throw new AssertionError(); + fail(); } void testFailWithMessage() { diff --git a/error-prone-experimental/pom.xml b/error-prone-experimental/pom.xml index 965e0875..3af0c9e8 100644 --- a/error-prone-experimental/pom.xml +++ b/error-prone-experimental/pom.xml @@ -5,7 +5,7 @@ tech.picnic.error-prone-support error-prone-support - 0.16.2-SNAPSHOT + 0.18.1-SNAPSHOT error-prone-experimental diff --git a/error-prone-guidelines/pom.xml b/error-prone-guidelines/pom.xml index a283915f..2d1ba3f3 100644 --- a/error-prone-guidelines/pom.xml +++ b/error-prone-guidelines/pom.xml @@ -5,7 +5,7 @@ tech.picnic.error-prone-support error-prone-support - 0.16.2-SNAPSHOT + 0.18.1-SNAPSHOT error-prone-guidelines diff --git a/error-prone-utils/pom.xml b/error-prone-utils/pom.xml index 5b7fedb1..1c6cffe2 100644 --- a/error-prone-utils/pom.xml +++ b/error-prone-utils/pom.xml @@ -5,7 +5,7 @@ tech.picnic.error-prone-support error-prone-support - 0.16.2-SNAPSHOT + 0.18.1-SNAPSHOT error-prone-utils diff --git a/integration-tests/checkstyle-expected-changes.patch b/integration-tests/checkstyle-expected-changes.patch index 412e42f4..efb017e9 100644 --- a/integration-tests/checkstyle-expected-changes.patch +++ b/integration-tests/checkstyle-expected-changes.patch @@ -53686,6 +53686,15 @@ final ModuleFactory moduleFactory = TestUtil.getPackageObjectFactory(); final Path path = Paths.get(XdocUtil.DIRECTORY_PATH + "/config.xml"); +@@ -1081,7 +1083,7 @@ public class XdocsPagesTest { + Optional.ofNullable(field) + .map(nonNullField -> nonNullField.getAnnotation(XdocsPropertyType.class)) + .map(propertyType -> propertyType.value().getDescription()) +- .orElse(fieldClass.getSimpleName()); ++ .orElseGet(fieldClass::getSimpleName); + final String expectedValue = + getModulePropertyExpectedValue(sectionName, propertyName, field, fieldClass, instance); + @@ -1364,7 +1366,7 @@ public class XdocsPagesTest { final Object[] array = (Object[]) value; valuesStream = Arrays.stream(array); diff --git a/integration-tests/checkstyle.sh b/integration-tests/checkstyle.sh index 0b1f5a5e..d238b262 100755 --- a/integration-tests/checkstyle.sh +++ b/integration-tests/checkstyle.sh @@ -2,205 +2,35 @@ set -e -u -o pipefail -integration_test_root="$(cd "$(dirname -- "${0}")" && pwd)" -error_prone_support_root="${integration_test_root}/.." -repos_root="${integration_test_root}/.repos" - test_name="$(basename "${0}" .sh)" -project=checkstyle -repository=https://github.com/checkstyle/checkstyle.git -revision=checkstyle-10.14.0 - -if [ "${#}" -gt 2 ] || ([ "${#}" = 2 ] && [ "${1:---sync}" != '--sync' ]); then - echo "Usage: ${0} [--sync] []" - exit 1 -fi -do_sync="$([ "${#}" = 0 ] || [ "${1:-}" != '--sync' ] || echo 1)" -report_directory="$([ "${#}" = 0 ] || ([ -z "${do_sync}" ] && echo "${1}") || ([ "${#}" = 1 ] || echo "${2}"))" - -if [ -n "${report_directory}" ]; then - mkdir -p "${report_directory}" -else - report_directory="$(mktemp -d)" - trap 'rm -rf -- "${report_directory}"' INT TERM HUP EXIT -fi - -case "$(uname -s)" in - Linux*) - grep_command=grep - sed_command=sed - ;; - Darwin*) - grep_command=ggrep - sed_command=gsed - ;; - *) - echo "Unsupported distribution $(uname -s) for this script." - exit 1 - ;; -esac - +project='checkstyle' +repository='https://github.com/checkstyle/checkstyle.git' +revision='checkstyle-10.14.0' # XXX: Configure Renovate to manage the AssertJ version declared here. -shared_build_flags=" - -Perror-prone-compile,error-prone-test-compile - -Dassertj.version=3.24.2 - -Derror-prone.version=$( - mvn -f "${error_prone_support_root}" help:evaluate -Dexpression=version.error-prone -q -DforceStdout - ) - -Derror-prone-support.version=$( - mvn -f "${error_prone_support_root}" help:evaluate -Dexpression=project.version -q -DforceStdout - ) - -DadditionalSourceDirectories=\${project.basedir}\${file.separator}src\${file.separator}it\${file.separator}java,\${project.basedir}\${file.separator}src\${file.separator}xdocs-examples\${file.separator}java - " - -# XXX: Configure Renovate to manage the fmt-maven-plugin version declared here. -# XXX: Once GitHub actions uses Maven 3.9.2+, we can inline this variable with -# version reference `${fmt.version}`, and `-Dfmt.version=2.21.1` added to -# `shared_build_flags`. -format_goal='com.spotify.fmt:fmt-maven-plugin:2.21.1:format' - -error_prone_shared_flags='-XepExcludedPaths:(\Q${project.basedir}${file.separator}src${file.separator}\E(it|test|xdocs-examples)\Q${file.separator}resources\E|\Q${project.build.directory}${file.separator}\E).*' - -error_prone_patch_flags="${error_prone_shared_flags} -XepPatchLocation:IN_PLACE -XepPatchChecks:$( - find "${error_prone_support_root}" \ - -path "*/META-INF/services/com.google.errorprone.bugpatterns.BugChecker" \ - -not -path "*/error-prone-experimental/*" \ - -not -path "*/error-prone-guidelines/*" \ - -print0 \ - | xargs -0 "${grep_command}" -hoP '[^.]+$' \ - | paste -s -d ',' - -)" - -error_prone_validation_flags="${error_prone_shared_flags} -XepDisableAllChecks $( - find "${error_prone_support_root}" \ - -path "*/META-INF/services/com.google.errorprone.bugpatterns.BugChecker" \ - -not -path "*/error-prone-experimental/*" \ - -not -path "*/error-prone-guidelines/*" \ - -print0 \ - | xargs -0 "${grep_command}" -hoP '[^.]+$' \ - | "${sed_command}" -r 's,(.*),-Xep:\1:WARN,' \ - | paste -s -d ' ' - -)" - -echo "Shared build flags: ${shared_build_flags}" -echo "Error Prone patch flags: ${error_prone_patch_flags}" -echo "Error Prone validation flags: ${error_prone_validation_flags}" - -mkdir -p "${repos_root}" - -# Make sure that the targeted tag of the project's Git repository is checked -# out. -project_root="${repos_root}/${project}" -if [ ! -d "${project_root}" ]; then - # The repository has not yet been cloned; create a shallow clone. - git clone --branch "${revision}" --depth 1 "${repository}" "${project_root}" -else - # The repository does already appear to exist. Try to check out the requested - # tag if possible, and fetch it otherwise. - # - # Under certain circumstances this does not cause the relevant tag to be - # created, so if necessary we manually create it. - git -C "${project_root}" checkout --force "${revision}" 2>/dev/null \ - || ( - git -C "${project_root}" fetch --depth 1 "${repository}" "${revision}" \ - && git -C "${project_root}" checkout --force FETCH_HEAD \ - && (git -C "${project_root}" tag "${revision}" || true) - ) -fi - -pushd "${project_root}" - -# Make sure that Git is sufficiently configured to enable committing to the -# project's Git repository. -git config user.email || git config user.email "integration-test@example.com" -git config user.name || git config user.name "Integration Test" - -# Prepare the code for analysis by (a) applying the minimal set of changes -# required to run Error Prone with Error Prone Support and (b) formatting the -# code using the same method by which it will be formatted after each -# compilation round. The initial formatting operation ensures that subsequent -# modifications can be rendered in a clean manner. -git clean -fdx -git apply < "${integration_test_root}/${test_name}-init.patch" -git commit -m 'dependency: Introduce Error Prone Support' . -mvn ${shared_build_flags} "${format_goal}" -git commit -m 'minor: Reformat using Google Java Format' . -diff_base="$(git rev-parse HEAD)" - -# Apply Error Prone Support-suggested changes until a fixed point is reached. -function apply_patch() { - local extra_build_args="${1}" - - mvn ${shared_build_flags} ${extra_build_args} \ - package "${format_goal}" \ - -Derror-prone.configuration-args="${error_prone_patch_flags}" \ - -DskipTests - - if ! git diff --exit-code; then - git commit -m 'minor: Apply patches' . - - # Changes were applied, so another compilation round may apply yet more - # changes. For performance reasons we perform incremental compilation, - # enabled using a misleading flag. (See - # https://issues.apache.org/jira/browse/MCOMPILER-209 for details.) - apply_patch '-Dmaven.compiler.useIncrementalCompilation=false' - elif [ "${extra_build_args}" != 'clean' ]; then - # No changes were applied. We'll attempt one more round in which all files - # are recompiled, because there are cases in which violations are missed - # during incremental compilation. - apply_patch 'clean' - fi -} -apply_patch '' - -# Run one more full build and log the output. -# -# By also running the tests, we validate that the (majority of) applied changes -# are behavior preserving. Some tests are skipped: +additional_build_flags='-Dassertj.version=3.24.2' +additional_source_directories='${project.basedir}${file.separator}src${file.separator}it${file.separator}java,${project.basedir}${file.separator}src${file.separator}xdocs-examples${file.separator}java' +patch_error_prone_flags='' +validation_error_prone_flags='' +# Validation skips some tests: # - The `metadataFilesGenerationAllFiles` test is skipped because it makes line # number assertions that will fail when the code is formatted or patched. -# - The `allCheckSectionJavaDocs` test is skipped because is validates that +# - The `allCheckSectionJavaDocs` test is skipped because it validates that # Javadoc has certain closing tags that are removed by Google Java Format. -validation_build_log="${report_directory}/${test_name}-validation-build-log.txt" -mvn ${shared_build_flags} \ - clean package \ - -Derror-prone.configuration-args="${error_prone_validation_flags}" \ - -Dtest=' - !MetadataGeneratorUtilTest#metadataFilesGenerationAllFiles, - !XdocsJavaDocsTest#allCheckSectionJavaDocs' \ - | tee "${validation_build_log}" \ - || failure=1 +validation_build_flags='-Dtest=!MetadataGeneratorUtilTest#metadataFilesGenerationAllFiles,!XdocsJavaDocsTest#allCheckSectionJavaDocs' -# Collect the applied changes. -expected_changes="${integration_test_root}/${test_name}-expected-changes.patch" -actual_changes="${report_directory}/${test_name}-changes.patch" -(git diff "${diff_base}"..HEAD | "${grep_command}" -vP '^(diff|index)' || true) > "${actual_changes}" - -# Collect the warnings reported by Error Prone Support checks. -expected_warnings="${integration_test_root}/${test_name}-expected-warnings.txt" -actual_warnings="${report_directory}/${test_name}-validation-build-warnings.txt" -("${grep_command}" -oP "(?<=^\\Q[WARNING] ${PWD}/\\E).*" "${validation_build_log}" | "${grep_command}" -P '\] \[' || true) | LC_ALL=C sort > "${actual_warnings}" - -# Persist or validate the applied changes and reported warnings. -if [ -n "${do_sync}" ]; then - echo 'Saving changes...' - cp "${actual_changes}" "${expected_changes}" - cp "${actual_warnings}" "${expected_warnings}" -else - echo 'Inspecting changes...' - # XXX: This "diff of diffs" also contains vacuous sections, introduced due to - # line offset differences. Try to omit those from the final output. - if ! diff -u "${expected_changes}" "${actual_changes}"; then - echo 'There are unexpected changes. Inspect the preceding output for details.' - failure=1 - fi - echo 'Inspecting emitted warnings...' - if ! diff -u "${expected_warnings}" "${actual_warnings}"; then - echo 'Diagnostics output changed. Inspect the preceding output for details.' - failure=1 - fi -fi - -if [ -n "${failure:-}" ]; then +if [ "${#}" -gt 2 ] || ([ "${#}" = 2 ] && [ "${1:---sync}" != '--sync' ]); then + >&2 echo "Usage: ${0} [--sync] []" exit 1 fi + +"$(dirname "${0}")/run-integration-test.sh" \ + "${test_name}" \ + "${project}" \ + "${repository}" \ + "${revision}" \ + "${additional_build_flags}" \ + "${additional_source_directories}" \ + "${patch_error_prone_flags}" \ + "${validation_error_prone_flags}" \ + "${validation_build_flags}" \ + $@ diff --git a/integration-tests/run-integration-test.sh b/integration-tests/run-integration-test.sh new file mode 100755 index 00000000..9944a7b1 --- /dev/null +++ b/integration-tests/run-integration-test.sh @@ -0,0 +1,210 @@ +#!/usr/bin/env bash + +# Integration test framework for Maven builds. +# +# This script is not meant to be invoked manually. Instead it should be invoked +# through one of the top-level integration test scripts, such as +# `checkstyle.sh`. + +set -e -u -o pipefail + +integration_test_root="$(cd "$(dirname -- "${0}")" && pwd)" +error_prone_support_root="${integration_test_root}/.." +repos_root="${integration_test_root}/.repos" + +if [ "${#}" -lt 9 ] || [ "${#}" -gt 11 ] || ([ "${#}" = 11 ] && [ "${10:---sync}" != '--sync' ]); then + >&2 echo "Usage: $(basename "${0}") [--sync] []" + exit 1 +fi + +test_name="${1}" +project="${2}" +repository="${3}" +revision="${4}" +additional_build_flags="${5}" +additional_source_directories="${6}" +patch_error_prone_flags="${7}" +validation_error_prone_flags="${8}" +validation_build_flags="${9}" +do_sync="$([ "${#}" = 9 ] || [ "${10:-}" != '--sync' ] || echo 1)" +report_directory="$([ "${#}" = 9 ] || ([ -z "${do_sync}" ] && echo "${10}") || ([ "${#}" = 10 ] || echo "${11}"))" + +if [ -n "${report_directory}" ]; then + mkdir -p "${report_directory}" +else + report_directory="$(mktemp -d)" + trap 'rm -rf -- "${report_directory}"' INT TERM HUP EXIT +fi + +case "$(uname -s)" in + Linux*) + grep_command=grep + sed_command=sed + ;; + Darwin*) + grep_command=ggrep + sed_command=gsed + ;; + *) + echo "Unsupported distribution $(uname -s) for this script." + exit 1 + ;; +esac + +shared_build_flags=" + -Perror-prone-compile,error-prone-test-compile + -Derror-prone.version=$( + mvn -f "${error_prone_support_root}" help:evaluate -Dexpression=version.error-prone -q -DforceStdout + ) + -Derror-prone-support.version=$( + mvn -f "${error_prone_support_root}" help:evaluate -Dexpression=project.version -q -DforceStdout + ) + -DadditionalSourceDirectories=${additional_source_directories} + ${additional_build_flags} + " + +# XXX: Configure Renovate to manage the fmt-maven-plugin version declared here. +# XXX: Once GitHub actions uses Maven 3.9.2+, we can inline this variable with +# version reference `${fmt.version}`, and `-Dfmt.version=2.21.1` added to +# `shared_build_flags`. +format_goal='com.spotify.fmt:fmt-maven-plugin:2.21.1:format' + +error_prone_shared_flags='-XepExcludedPaths:(\Q${project.basedir}${file.separator}src${file.separator}\E(it|test|xdocs-examples)\Q${file.separator}resources\E|\Q${project.build.directory}${file.separator}\E).*' + +error_prone_patch_flags="${error_prone_shared_flags} -XepPatchLocation:IN_PLACE -XepPatchChecks:$( + find "${error_prone_support_root}" \ + -path "*/META-INF/services/com.google.errorprone.bugpatterns.BugChecker" \ + -not -path "*/error-prone-experimental/*" \ + -not -path "*/error-prone-guidelines/*" \ + -print0 \ + | xargs -0 "${grep_command}" -hoP '[^.]+$' \ + | paste -s -d ',' - +) ${patch_error_prone_flags}" + +error_prone_validation_flags="${error_prone_shared_flags} -XepDisableAllChecks $( + find "${error_prone_support_root}" \ + -path "*/META-INF/services/com.google.errorprone.bugpatterns.BugChecker" \ + -not -path "*/error-prone-experimental/*" \ + -not -path "*/error-prone-guidelines/*" \ + -print0 \ + | xargs -0 "${grep_command}" -hoP '[^.]+$' \ + | "${sed_command}" -r 's,(.*),-Xep:\1:WARN,' \ + | paste -s -d ' ' - +) ${validation_error_prone_flags}" + +echo "Shared build flags: ${shared_build_flags}" +echo "Error Prone patch flags: ${error_prone_patch_flags}" +echo "Error Prone validation flags: ${error_prone_validation_flags}" + +mkdir -p "${repos_root}" + +# Make sure that the targeted tag of the project's Git repository is checked +# out. +project_root="${repos_root}/${project}" +if [ ! -d "${project_root}" ]; then + # The repository has not yet been cloned; create a shallow clone. + git clone --branch "${revision}" --depth 1 "${repository}" "${project_root}" +else + # The repository does already appear to exist. Try to check out the requested + # tag if possible, and fetch it otherwise. + # + # Under certain circumstances this does not cause the relevant tag to be + # created, so if necessary we manually create it. + git -C "${project_root}" checkout --force "${revision}" 2>/dev/null \ + || ( + git -C "${project_root}" fetch --depth 1 "${repository}" "${revision}" \ + && git -C "${project_root}" checkout --force FETCH_HEAD \ + && (git -C "${project_root}" tag "${revision}" || true) + ) +fi + +pushd "${project_root}" + +# Make sure that Git is sufficiently configured to enable committing to the +# project's Git repository. +git config user.email || git config user.email 'integration-test@example.com' +git config user.name || git config user.name 'Integration Test' + +# Prepare the code for analysis by (a) applying the minimal set of changes +# required to run Error Prone with Error Prone Support and (b) formatting the +# code using the same method by which it will be formatted after each +# compilation round. The initial formatting operation ensures that subsequent +# modifications can be rendered in a clean manner. +git clean -fdx +git apply < "${integration_test_root}/${test_name}-init.patch" +git commit -m 'dependency: Introduce Error Prone Support' . +mvn ${shared_build_flags} "${format_goal}" +git commit -m 'minor: Reformat using Google Java Format' . +diff_base="$(git rev-parse HEAD)" + +# Apply Error Prone Support-suggested changes until a fixed point is reached. +function apply_patch() { + local extra_build_args="${1}" + + mvn ${shared_build_flags} ${extra_build_args} \ + package "${format_goal}" \ + -Derror-prone.configuration-args="${error_prone_patch_flags}" \ + -DskipTests + + if ! git diff --exit-code; then + git commit -m 'minor: Apply patches' . + + # Changes were applied, so another compilation round may apply yet more + # changes. For performance reasons we perform incremental compilation, + # enabled using a misleading flag. (See + # https://issues.apache.org/jira/browse/MCOMPILER-209 for details.) + apply_patch '-Dmaven.compiler.useIncrementalCompilation=false' + elif [ "${extra_build_args}" != 'clean' ]; then + # No changes were applied. We'll attempt one more round in which all files + # are recompiled, because there are cases in which violations are missed + # during incremental compilation. + apply_patch 'clean' + fi +} +apply_patch '' + +# Run one more full build and log the output. +# +# By also running the tests, we validate that the (majority of) applied changes +# are behavior preserving. +validation_build_log="${report_directory}/${test_name}-validation-build-log.txt" +mvn ${shared_build_flags} \ + clean package \ + -Derror-prone.configuration-args="${error_prone_validation_flags}" \ + ${validation_build_flags} \ + | tee "${validation_build_log}" \ + || failure=1 + +# Collect the applied changes. +expected_changes="${integration_test_root}/${test_name}-expected-changes.patch" +actual_changes="${report_directory}/${test_name}-changes.patch" +(git diff "${diff_base}"..HEAD | "${grep_command}" -vP '^(diff|index)' || true) > "${actual_changes}" + +# Collect the warnings reported by Error Prone Support checks. +expected_warnings="${integration_test_root}/${test_name}-expected-warnings.txt" +actual_warnings="${report_directory}/${test_name}-validation-build-warnings.txt" +("${grep_command}" -oP "(?<=^\\Q[WARNING] ${PWD}/\\E).*" "${validation_build_log}" | "${grep_command}" -P '\] \[' || true) | LC_ALL=C sort > "${actual_warnings}" + +# Persist or validate the applied changes and reported warnings. +if [ -n "${do_sync}" ]; then + echo 'Saving changes...' + cp "${actual_changes}" "${expected_changes}" + cp "${actual_warnings}" "${expected_warnings}" +else + echo 'Inspecting changes...' + # XXX: This "diff of diffs" also contains vacuous sections, introduced due to + # line offset differences. Try to omit those from the final output. + if ! diff -u "${expected_changes}" "${actual_changes}"; then + echo 'There are unexpected changes. Inspect the preceding output for details.' + failure=1 + fi + echo 'Inspecting emitted warnings...' + if ! diff -u "${expected_warnings}" "${actual_warnings}"; then + echo 'Diagnostics output changed. Inspect the preceding output for details.' + failure=1 + fi +fi + +if [ -n "${failure:-}" ]; then + exit 1 +fi diff --git a/pom.xml b/pom.xml index dd875716..a1a6d182 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ tech.picnic.error-prone-support error-prone-support - 0.16.2-SNAPSHOT + 0.18.1-SNAPSHOT pom Picnic :: Error Prone Support @@ -148,7 +148,7 @@ com.google.errorprone - 2024-03-15T12:04:44Z + 2024-08-11T13:05:54Z UTF-8 1.1.1 - 1.10.4 + 1.11.0 ${version.error-prone-orig} v${version.error-prone-orig}-picnic-1 - 2.26.1 - 0.1.22 + 2.30.0 + 0.1.25 1.0 17 3.9.5 - 5.11.0 + 5.12.0 1.0.1 - 0.10.24 + 0.11.1 1.1.4 - 1.6.3 + 1.12.3 3.2.3 @@ -296,7 +296,7 @@ com.fasterxml.jackson jackson-bom - 2.17.0 + 2.17.2 pom import @@ -328,7 +328,7 @@ com.google.googlejavaformat google-java-format - 1.21.0 + 1.23.0 com.google.guava @@ -338,14 +338,14 @@ com.google.guava guava-bom - 33.1.0-jre + 33.2.1-jre pom import com.google.truth truth - 1.4.2 + 1.4.4 com.jakewharton.nopen @@ -365,7 +365,7 @@ io.projectreactor reactor-bom - 2023.0.4 + 2023.0.8 pom import @@ -382,12 +382,17 @@ io.swagger.core.v3 swagger-annotations - 2.2.20 + 2.2.22 jakarta.servlet jakarta.servlet-api - 6.0.0 + 6.1.0 + + + javax.annotation + javax.annotation-api + 1.3.2 javax.inject @@ -412,7 +417,7 @@ net.bytebuddy byte-buddy - 1.14.12 + 1.14.18 false + + + + compile-recipes + + compile + + + + + -Xlint:-options + + 8 + 8 + + **/*Recipes.java + + ${project.build.directory}/openrewrite-recipes + + + org.apache.maven.plugins maven-dependency-plugin - 3.6.1 + 3.7.1 + + **/*Recipe$*.class + **/*Recipe.class + **/*Recipes.class + + + + + + create-openrewrite-recipes-jar + + jar + + + ${project.build.directory}/openrewrite-recipes + recipes + + **/*Recipe$*.class + **/*Recipe.class + **/*Recipes.class + + + create-test-jar @@ -1119,7 +1193,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.6.3 + 3.8.0 --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED @@ -1148,7 +1222,7 @@ org.apache.maven.plugins maven-release-plugin - 3.0.1 + 3.1.1 true release @@ -1175,7 +1249,7 @@ org.apache.maven.plugins maven-source-plugin - 3.3.0 + 3.3.1 generate-source-jar @@ -1189,7 +1263,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.5 + 3.3.1 **/*Test.java @@ -1205,7 +1279,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.5.0 + 3.6.0 org.codehaus.mojo @@ -1301,6 +1375,7 @@ GPL-2.0-with-classpath-exception | CDDL/GPLv2+CE + | CDDL + GPLv2 with classpath exception | GNU General Public License, version 2 (GPL2), with the classpath exception | GNU General Public License, version 2, with the Classpath Exception | GPL2 w/ CPE @@ -1335,7 +1410,7 @@ org.codehaus.mojo tidy-maven-plugin - 1.2.0 + 1.3.0 check-pom @@ -1348,7 +1423,7 @@ org.codehaus.mojo versions-maven-plugin - 2.16.2 + 2.17.1 never @@ -1356,7 +1431,7 @@ org.gaul modernizer-maven-plugin - 2.8.0 + 2.9.0 @@ -1471,7 +1546,7 @@ org.sonarsource.scanner.maven sonar-maven-plugin - 3.11.0.3922 + 4.0.0.4121 diff --git a/refaster-compiler/pom.xml b/refaster-compiler/pom.xml index e88dcac0..10d3e45b 100644 --- a/refaster-compiler/pom.xml +++ b/refaster-compiler/pom.xml @@ -5,7 +5,7 @@ tech.picnic.error-prone-support error-prone-support - 0.16.2-SNAPSHOT + 0.18.1-SNAPSHOT refaster-compiler 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 f62efde1..e9cd1a44 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 @@ -21,7 +21,6 @@ import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.Name; import java.io.IOException; import java.io.ObjectOutput; import java.io.ObjectOutputStream; @@ -112,7 +111,7 @@ final class RefasterRuleCompilerTaskListener implements TaskListener { return (sym != null && sym.getQualifiedName() .contentEquals(BeforeTemplate.class.getCanonicalName())) - || super.visitAnnotation(node, unused); + || super.visitAnnotation(node, null); } @Override @@ -137,10 +136,10 @@ final class RefasterRuleCompilerTaskListener implements TaskListener { return enclosingPackage == null ? "" : enclosingPackage.toString(); } - private static CharSequence toSimpleFlatName(ClassSymbol symbol) { - Name flatName = symbol.flatName(); + private static String toSimpleFlatName(ClassSymbol symbol) { + String flatName = symbol.flatName().toString(); int lastDot = flatName.lastIndexOf((byte) '.'); - return lastDot < 0 ? flatName : flatName.subSequence(lastDot + 1, flatName.length()); + return lastDot < 0 ? flatName : flatName.substring(lastDot + 1); } private static void outputCodeTransformer(CodeTransformer codeTransformer, FileObject target) diff --git a/refaster-runner/pom.xml b/refaster-runner/pom.xml index ddacd89c..95bbb789 100644 --- a/refaster-runner/pom.xml +++ b/refaster-runner/pom.xml @@ -5,7 +5,7 @@ tech.picnic.error-prone-support error-prone-support - 0.16.2-SNAPSHOT + 0.18.1-SNAPSHOT refaster-runner @@ -58,6 +58,11 @@ guava provided + + javax.annotation + javax.annotation-api + test + javax.inject javax.inject 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 274b9b57..94abfc23 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 @@ -166,7 +166,7 @@ public final class Refaster extends BugChecker implements CompilationUnitTreeMat "Refaster Rule", description.getLink(), String.join(": ", description.checkName, description.getRawMessage())) - .overrideSeverity(severityOverride.orElse(description.severity())) + .overrideSeverity(severityOverride.orElseGet(description::severity)) .addAllFixes(description.fixes) .build(); } diff --git a/refaster-support/pom.xml b/refaster-support/pom.xml index 2f8104db..019a2dc1 100644 --- a/refaster-support/pom.xml +++ b/refaster-support/pom.xml @@ -5,7 +5,7 @@ tech.picnic.error-prone-support error-prone-support - 0.16.2-SNAPSHOT + 0.18.1-SNAPSHOT refaster-support diff --git a/refaster-test-support/pom.xml b/refaster-test-support/pom.xml index 0bbfb907..c75151b0 100644 --- a/refaster-test-support/pom.xml +++ b/refaster-test-support/pom.xml @@ -5,7 +5,7 @@ tech.picnic.error-prone-support error-prone-support - 0.16.2-SNAPSHOT + 0.18.1-SNAPSHOT refaster-test-support @@ -50,6 +50,11 @@ com.google.guava guava + + javax.annotation + javax.annotation-api + test + javax.inject javax.inject 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 3342fe8b..7db64fb6 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 @@ -337,7 +337,7 @@ public final class RefasterRuleCollection extends BugChecker implements Compilat return indexedMatches.subRangeMap(Range.closedOpen(startPosition, endPosition)); } - private ImmutableListMultimap getUnexpectedMatchesByLineNumber( + private static ImmutableListMultimap getUnexpectedMatchesByLineNumber( ImmutableRangeMap matches, String ruleUnderTest, VisitorState state) { LineMap lineMap = state.getPath().getCompilationUnit().getLineMap(); return matches.asMapOfRanges().entrySet().stream() diff --git a/website/_data/compatibility.yml b/website/_data/compatibility.yml index 29ddbe66..9c6acf5b 100644 --- a/website/_data/compatibility.yml +++ b/website/_data/compatibility.yml @@ -1,8 +1,31 @@ # An overview of Error Prone Support releases, along with compatible Error # Prone releases. This data was generated by `generate-version-compatibility-overview.sh`. releases: + - version: 0.18.0 + compatible: + - "2.30.0" + - version: 0.17.0 + compatible: + - "2.29.2" + - "2.29.1" + - "2.29.0" + - "2.28.0" + - "2.27.1" + - "2.27.0" + - "2.26.1" + - "2.26.0" + - "2.25.0" + - "2.24.1" + - "2.24.0" + - "2.23.0" - version: 0.16.1 compatible: + - "2.29.2" + - "2.29.1" + - "2.29.0" + - "2.28.0" + - "2.27.1" + - "2.27.0" - "2.26.1" - "2.26.0" - "2.25.0" @@ -11,6 +34,12 @@ releases: - "2.23.0" - version: 0.16.0 compatible: + - "2.29.2" + - "2.29.1" + - "2.29.0" + - "2.28.0" + - "2.27.1" + - "2.27.0" - "2.26.1" - "2.26.0" - "2.25.0" @@ -19,6 +48,12 @@ releases: - "2.23.0" - version: 0.15.0 compatible: + - "2.29.2" + - "2.29.1" + - "2.29.0" + - "2.28.0" + - "2.27.1" + - "2.27.0" - "2.26.1" - "2.26.0" - "2.25.0" @@ -27,6 +62,12 @@ releases: - "2.23.0" - version: 0.14.0 compatible: + - "2.29.2" + - "2.29.1" + - "2.29.0" + - "2.28.0" + - "2.27.1" + - "2.27.0" - "2.26.1" - "2.26.0" - "2.25.0"