mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 15:49:33 +00:00
Compare commits
2 Commits
sschroever
...
werli/mong
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7c1a3d0ed | ||
|
|
c7606e893e |
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -42,9 +42,9 @@ Please replace this sentence with log output, if applicable.
|
||||
<!-- Please complete the following information: -->
|
||||
|
||||
- Operating system (e.g. MacOS Monterey).
|
||||
- Java version (i.e. `java --version`, e.g. `17.0.13`).
|
||||
- Error Prone version (e.g. `2.35.1`).
|
||||
- Error Prone Support version (e.g. `0.19.0`).
|
||||
- Java version (i.e. `java --version`, e.g. `17.0.10`).
|
||||
- Error Prone version (e.g. `2.25.0`).
|
||||
- Error Prone Support version (e.g. `0.15.0`).
|
||||
|
||||
### Additional context
|
||||
|
||||
|
||||
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
@@ -9,24 +9,24 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-24.04 ]
|
||||
jdk: [ 17.0.13, 21.0.5, 23.0.1 ]
|
||||
os: [ ubuntu-22.04 ]
|
||||
jdk: [ 17.0.10, 21.0.2 ]
|
||||
distribution: [ temurin ]
|
||||
experimental: [ false ]
|
||||
include:
|
||||
- os: macos-14
|
||||
jdk: 17.0.13
|
||||
jdk: 17.0.10
|
||||
distribution: temurin
|
||||
experimental: false
|
||||
- os: windows-2022
|
||||
jdk: 17.0.13
|
||||
jdk: 17.0.10
|
||||
distribution: temurin
|
||||
experimental: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
@@ -42,11 +42,11 @@ 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@9a27433d289dd99d73851f653607c39d3444e8ba # v1.17.0
|
||||
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
|
||||
with:
|
||||
java-version: ${{ matrix.jdk }}
|
||||
java-distribution: ${{ matrix.distribution }}
|
||||
maven-version: 3.9.9
|
||||
maven-version: 3.9.6
|
||||
- name: Display build environment details
|
||||
run: mvn --version
|
||||
- name: Build project against vanilla Error Prone, compile Javadoc
|
||||
@@ -54,6 +54,6 @@ jobs:
|
||||
- name: Build project with self-check against Error Prone fork
|
||||
run: mvn -T1C clean verify -Perror-prone-fork -Pnon-maven-central -Pself-check -s settings.xml
|
||||
- name: Remove installed project artifacts
|
||||
run: mvn dependency:purge-local-repository -DmanualInclude='${project.groupId}' -DresolutionFuzziness=groupId
|
||||
run: mvn build-helper:remove-project-artifact
|
||||
|
||||
# XXX: Enable Codecov once we "go public".
|
||||
|
||||
14
.github/workflows/codeql.yml
vendored
14
.github/workflows/codeql.yml
vendored
@@ -19,10 +19,10 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
@@ -34,19 +34,19 @@ jobs:
|
||||
repo.maven.apache.org:443
|
||||
uploads.github.com:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@9a27433d289dd99d73851f653607c39d3444e8ba # v1.17.0
|
||||
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
|
||||
with:
|
||||
java-version: 17.0.13
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.9
|
||||
maven-version: 3.9.6
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.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@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
|
||||
uses: github/codeql-action/analyze@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
|
||||
with:
|
||||
category: /language:${{ matrix.language }}
|
||||
|
||||
12
.github/workflows/deploy-website.yml
vendored
12
.github/workflows/deploy-website.yml
vendored
@@ -9,10 +9,10 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
@@ -39,10 +39,10 @@ jobs:
|
||||
www.youtube.com:443
|
||||
youtrack.jetbrains.com:443
|
||||
- name: Check out code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: ruby/setup-ruby@a2bbe5b1b236842c1cb7dd11e8e3b51e0a616acc # v1.202.0
|
||||
- uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0
|
||||
with:
|
||||
working-directory: ./website
|
||||
bundler-cache: true
|
||||
@@ -68,13 +68,13 @@ jobs:
|
||||
permissions:
|
||||
id-token: write
|
||||
pages: write
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
|
||||
17
.github/workflows/openssf-scorecard.yml
vendored
17
.github/workflows/openssf-scorecard.yml
vendored
@@ -18,36 +18,35 @@ jobs:
|
||||
contents: read
|
||||
security-events: write
|
||||
id-token: write
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.deps.dev:443
|
||||
api.github.com:443
|
||||
api.osv.dev:443
|
||||
api.scorecard.dev:443
|
||||
api.securityscorecards.dev:443
|
||||
fulcio.sigstore.dev:443
|
||||
github.com:443
|
||||
index.docker.io:443
|
||||
oss-fuzz-build-logs.storage.googleapis.com:443
|
||||
repo.maven.apache.org:443
|
||||
*.sigstore.dev:443
|
||||
rekor.sigstore.dev:443
|
||||
tuf-repo-cdn.sigstore.dev:443
|
||||
www.bestpractices.dev:443
|
||||
- name: Check out code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run OpenSSF Scorecard analysis
|
||||
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
|
||||
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@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
|
||||
uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
12
.github/workflows/pitest-analyze-pr.yml
vendored
12
.github/workflows/pitest-analyze-pr.yml
vendored
@@ -9,10 +9,10 @@ permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
analyze-pr:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
@@ -22,12 +22,12 @@ jobs:
|
||||
objects.githubusercontent.com:443
|
||||
repo.maven.apache.org:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@9a27433d289dd99d73851f653607c39d3444e8ba # v1.17.0
|
||||
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
|
||||
with:
|
||||
checkout-fetch-depth: 2
|
||||
java-version: 17.0.13
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.9
|
||||
maven-version: 3.9.6
|
||||
- name: Run Pitest
|
||||
# By running with features `+GIT(from[HEAD~1]), +gitci`, Pitest only
|
||||
# analyzes lines changed in the associated pull request, as GitHub
|
||||
@@ -38,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@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
with:
|
||||
name: pitest-reports
|
||||
path: ./target/pit-reports-ci
|
||||
|
||||
10
.github/workflows/pitest-update-pr.yml
vendored
10
.github/workflows/pitest-update-pr.yml
vendored
@@ -17,10 +17,10 @@ jobs:
|
||||
checks: write
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
@@ -31,11 +31,11 @@ jobs:
|
||||
objects.githubusercontent.com:443
|
||||
repo.maven.apache.org:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@9a27433d289dd99d73851f653607c39d3444e8ba # v1.17.0
|
||||
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
|
||||
with:
|
||||
java-version: 17.0.13
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.9
|
||||
maven-version: 3.9.6
|
||||
- name: Download Pitest analysis artifact
|
||||
uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4
|
||||
with:
|
||||
|
||||
36
.github/workflows/run-integration-tests.yml
vendored
36
.github/workflows/run-integration-tests.yml
vendored
@@ -1,9 +1,9 @@
|
||||
# If requested by means of a pull request comment, runs integration tests
|
||||
# against the project, using the code found on the pull request branch.
|
||||
# XXX: Review whether then build matrix should also vary JDK or OS versions.
|
||||
# XXX: Support `/integration-test [name...]` comment syntax to specify the
|
||||
# subset of integration tests to run.
|
||||
# See this example of a dynamic build matrix:
|
||||
# XXX: Generalize this to a matrix build of multiple integration tests,
|
||||
# possibly using multiple JDK or OS versions.
|
||||
# XXX: Investigate whether the comment can specify which integration tests run
|
||||
# run. See this example of a dynamic build matrix:
|
||||
# https://docs.github.com/en/actions/learn-github-actions/expressions#example-returning-a-json-object
|
||||
name: "Integration tests"
|
||||
on:
|
||||
@@ -16,48 +16,38 @@ jobs:
|
||||
name: On-demand integration test
|
||||
if: |
|
||||
github.event.issue.pull_request && contains(github.event.comment.body, '/integration-test')
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
integration-test: [ "checkstyle", "metrics", "prometheus-java-client" ]
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
# XXX: After updating the validation build flags in
|
||||
# `integration-tests/prometheus-java-client.sh`, review whether the
|
||||
# Docker domains specified here can be dropped.
|
||||
api.adoptium.net:443
|
||||
auth.docker.io:443
|
||||
checkstyle.org:443
|
||||
example.com:80
|
||||
github.com:443
|
||||
objects.githubusercontent.com:443
|
||||
oss.sonatype.org:443
|
||||
production.cloudflare.docker.com:443
|
||||
raw.githubusercontent.com:443
|
||||
registry-1.docker.io: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@9a27433d289dd99d73851f653607c39d3444e8ba # v1.17.0
|
||||
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
|
||||
with:
|
||||
checkout-ref: "refs/pull/${{ github.event.issue.number }}/head"
|
||||
java-version: 17.0.13
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.9
|
||||
maven-version: 3.9.6
|
||||
- name: Install project to local Maven repository
|
||||
run: mvn -T1C install -DskipTests -Dverification.skip
|
||||
- name: Run integration test
|
||||
run: xvfb-run "./integration-tests/${{ matrix.integration-test }}.sh" "${{ runner.temp }}/artifacts"
|
||||
run: xvfb-run ./integration-tests/checkstyle.sh "${{ runner.temp }}/artifacts"
|
||||
- name: Upload artifacts on failure
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
with:
|
||||
name: "integration-test-${{ matrix.integration-test }}"
|
||||
name: integration-test-checkstyle
|
||||
path: "${{ runner.temp }}/artifacts"
|
||||
- name: Remove installed project artifacts
|
||||
run: mvn dependency:purge-local-repository -DmanualInclude='${project.groupId}' -DresolutionFuzziness=groupId
|
||||
run: mvn build-helper:remove-project-artifact
|
||||
|
||||
14
.github/workflows/sonarcloud.yml
vendored
14
.github/workflows/sonarcloud.yml
vendored
@@ -16,31 +16,29 @@ jobs:
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
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
|
||||
*.sonarcloud.io:443
|
||||
scanner.sonarcloud.io:443
|
||||
sonarcloud.io:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@9a27433d289dd99d73851f653607c39d3444e8ba # v1.17.0
|
||||
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
|
||||
with:
|
||||
checkout-fetch-depth: 0
|
||||
java-version: 17.0.13
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.9
|
||||
maven-version: 3.9.6
|
||||
- name: Create missing `test` directory
|
||||
# XXX: Drop this step in favour of actually having a test.
|
||||
run: mkdir refaster-compiler/src/test
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
|
||||
|
||||
@@ -3,24 +3,11 @@
|
||||
"extends": [
|
||||
"helpers:pinGitHubActionDigests"
|
||||
],
|
||||
"customManagers": [
|
||||
{
|
||||
"customType": "regex",
|
||||
"fileMatch": [
|
||||
"^integration-tests/.*(-init\\.patch|\\.sh)$"
|
||||
],
|
||||
"matchStrings": [
|
||||
"\\b(?<packageName>[a-z0-9_.-]+?:[a-z0-9_.-]+?):(?<currentValue>[^:]+?):[a-zA-Z0-9_-]+\\b",
|
||||
"<version>(?<currentValue>.*?)<!-- Renovate: (?<packageName>.*?) --></version>"
|
||||
],
|
||||
"datasourceTemplate": "maven"
|
||||
}
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchPackageNames": [
|
||||
"/^org\\.springframework:spring-framework-bom$/",
|
||||
"/^org\\.springframework\\.boot:spring-boot[a-z-]*$/"
|
||||
"matchPackagePatterns": [
|
||||
"^org\\.springframework:spring-framework-bom$",
|
||||
"^org\\.springframework\\.boot:spring-boot[a-z-]*$"
|
||||
],
|
||||
"separateMinorPatch": true
|
||||
},
|
||||
|
||||
@@ -217,7 +217,7 @@ Other highly relevant commands:
|
||||
- `mvn fmt:format` formats the code using
|
||||
[`google-java-format`][google-java-format].
|
||||
- [`./run-full-build.sh`][script-run-full-build] builds the project twice,
|
||||
where the second pass validates compatibility with Picnic's [Error Prone
|
||||
where the second pass validates compatbility with Picnic's [Error Prone
|
||||
fork][error-prone-fork-repo] and compliance of the code with any rules
|
||||
defined within this project. (Consider running this before [opening a pull
|
||||
request][contributing-pull-request], as the PR checks also perform this
|
||||
@@ -302,7 +302,7 @@ channel; please see our [security policy][security] for details.
|
||||
[refaster]: https://errorprone.info/docs/refaster
|
||||
[refaster-rules-bigdecimal]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/BigDecimalRules.java
|
||||
[refaster-rules]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/
|
||||
[reproducible-builds-badge]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/jvm-repo-rebuild/reproducible-central/master/content/tech/picnic/error-prone-support/error-prone-support/badge.json
|
||||
[reproducible-builds-badge]: https://img.shields.io/badge/Reproducible_Builds-ok-success?labelColor=1e5b96
|
||||
[reproducible-builds-report]: https://github.com/jvm-repo-rebuild/reproducible-central/blob/master/content/tech/picnic/error-prone-support/error-prone-support/README.md
|
||||
[script-apply-error-prone-suggestions]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/apply-error-prone-suggestions.sh
|
||||
[script-run-branch-mutation-tests]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/run-branch-mutation-tests.sh
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.19.2-SNAPSHOT</version>
|
||||
<version>0.16.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>documentation-support</artifactId>
|
||||
@@ -33,15 +33,6 @@
|
||||
<artifactId>error_prone_test_helpers</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>error-prone-utils</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>refaster-test-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
|
||||
@@ -27,7 +27,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.BugPatternTestCases;
|
||||
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.TestCases;
|
||||
|
||||
/**
|
||||
* An {@link Extractor} that describes how to extract data from classes that test a {@code
|
||||
@@ -40,7 +40,7 @@ import tech.picnic.errorprone.documentation.BugPatternTestExtractor.BugPatternTe
|
||||
@Immutable
|
||||
@AutoService(Extractor.class)
|
||||
@SuppressWarnings("rawtypes" /* See https://github.com/google/auto/issues/870. */)
|
||||
public final class BugPatternTestExtractor implements Extractor<BugPatternTestCases> {
|
||||
public final class BugPatternTestExtractor implements Extractor<TestCases> {
|
||||
/** Instantiates a new {@link BugPatternTestExtractor} instance. */
|
||||
public BugPatternTestExtractor() {}
|
||||
|
||||
@@ -50,7 +50,7 @@ public final class BugPatternTestExtractor implements Extractor<BugPatternTestCa
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BugPatternTestCases> tryExtract(ClassTree tree, VisitorState state) {
|
||||
public Optional<TestCases> tryExtract(ClassTree tree, VisitorState state) {
|
||||
BugPatternTestCollector collector = new BugPatternTestCollector();
|
||||
|
||||
collector.scan(tree, state);
|
||||
@@ -59,7 +59,7 @@ public final class BugPatternTestExtractor implements Extractor<BugPatternTestCa
|
||||
.filter(not(ImmutableList::isEmpty))
|
||||
.map(
|
||||
tests ->
|
||||
new AutoValue_BugPatternTestExtractor_BugPatternTestCases(
|
||||
new AutoValue_BugPatternTestExtractor_TestCases(
|
||||
state.getPath().getCompilationUnit().getSourceFile().toUri(),
|
||||
ASTHelpers.getSymbol(tree).className(),
|
||||
tests));
|
||||
@@ -95,10 +95,10 @@ public final class BugPatternTestExtractor implements Extractor<BugPatternTestCa
|
||||
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper.ExpectOutput")
|
||||
.namedAnyOf("addOutputLines", "expectUnchanged");
|
||||
|
||||
private final List<BugPatternTestCase> collectedBugPatternTestCases = new ArrayList<>();
|
||||
private final List<TestCase> collectedTestCases = new ArrayList<>();
|
||||
|
||||
private ImmutableList<BugPatternTestCase> getCollectedTests() {
|
||||
return ImmutableList.copyOf(collectedBugPatternTestCases);
|
||||
private ImmutableList<TestCase> getCollectedTests() {
|
||||
return ImmutableList.copyOf(collectedTestCases);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,14 +110,14 @@ public final class BugPatternTestExtractor implements Extractor<BugPatternTestCa
|
||||
classUnderTest -> {
|
||||
List<TestEntry> entries = new ArrayList<>();
|
||||
if (isReplacementTest) {
|
||||
extractReplacementBugPatternTestCases(node, entries, state);
|
||||
extractReplacementTestCases(node, entries, state);
|
||||
} else {
|
||||
extractIdentificationBugPatternTestCases(node, entries, state);
|
||||
extractIdentificationTestCases(node, entries, state);
|
||||
}
|
||||
|
||||
if (!entries.isEmpty()) {
|
||||
collectedBugPatternTestCases.add(
|
||||
new AutoValue_BugPatternTestExtractor_BugPatternTestCase(
|
||||
collectedTestCases.add(
|
||||
new AutoValue_BugPatternTestExtractor_TestCase(
|
||||
classUnderTest, ImmutableList.copyOf(entries).reverse()));
|
||||
}
|
||||
});
|
||||
@@ -140,7 +140,7 @@ public final class BugPatternTestExtractor implements Extractor<BugPatternTestCa
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
private static void extractIdentificationBugPatternTestCases(
|
||||
private static void extractIdentificationTestCases(
|
||||
MethodInvocationTree tree, List<TestEntry> sink, VisitorState state) {
|
||||
if (IDENTIFICATION_SOURCE_LINES.matches(tree, state)) {
|
||||
String path = ASTHelpers.constValue(tree.getArguments().get(0), String.class);
|
||||
@@ -155,11 +155,11 @@ public final class BugPatternTestExtractor implements Extractor<BugPatternTestCa
|
||||
|
||||
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
|
||||
if (receiver instanceof MethodInvocationTree methodInvocation) {
|
||||
extractIdentificationBugPatternTestCases(methodInvocation, sink, state);
|
||||
extractIdentificationTestCases(methodInvocation, sink, state);
|
||||
}
|
||||
}
|
||||
|
||||
private static void extractReplacementBugPatternTestCases(
|
||||
private static void extractReplacementTestCases(
|
||||
MethodInvocationTree tree, List<TestEntry> sink, VisitorState state) {
|
||||
if (REPLACEMENT_OUTPUT_SOURCE_LINES.matches(tree, state)) {
|
||||
/*
|
||||
@@ -185,7 +185,7 @@ public final class BugPatternTestExtractor implements Extractor<BugPatternTestCa
|
||||
|
||||
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
|
||||
if (receiver instanceof MethodInvocationTree methodInvocation) {
|
||||
extractReplacementBugPatternTestCases(methodInvocation, sink, state);
|
||||
extractReplacementTestCases(methodInvocation, sink, state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,26 +208,24 @@ public final class BugPatternTestExtractor implements Extractor<BugPatternTestCa
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
@JsonDeserialize(as = AutoValue_BugPatternTestExtractor_BugPatternTestCases.class)
|
||||
abstract static class BugPatternTestCases {
|
||||
static BugPatternTestCases create(
|
||||
URI source, String testClass, ImmutableList<BugPatternTestCase> testCases) {
|
||||
return new AutoValue_BugPatternTestExtractor_BugPatternTestCases(
|
||||
source, testClass, testCases);
|
||||
@JsonDeserialize(as = AutoValue_BugPatternTestExtractor_TestCases.class)
|
||||
abstract static class TestCases {
|
||||
static TestCases create(URI source, String testClass, ImmutableList<TestCase> testCases) {
|
||||
return new AutoValue_BugPatternTestExtractor_TestCases(source, testClass, testCases);
|
||||
}
|
||||
|
||||
abstract URI source();
|
||||
|
||||
abstract String testClass();
|
||||
|
||||
abstract ImmutableList<BugPatternTestCase> testCases();
|
||||
abstract ImmutableList<TestCase> testCases();
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
@JsonDeserialize(as = AutoValue_BugPatternTestExtractor_BugPatternTestCase.class)
|
||||
abstract static class BugPatternTestCase {
|
||||
static BugPatternTestCase create(String classUnderTest, ImmutableList<TestEntry> entries) {
|
||||
return new AutoValue_BugPatternTestExtractor_BugPatternTestCase(classUnderTest, entries);
|
||||
@JsonDeserialize(as = AutoValue_BugPatternTestExtractor_TestCase.class)
|
||||
abstract static class TestCase {
|
||||
static TestCase create(String classUnderTest, ImmutableList<TestEntry> entries) {
|
||||
return new AutoValue_BugPatternTestExtractor_TestCase(classUnderTest, entries);
|
||||
}
|
||||
|
||||
abstract String classUnderTest();
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ServiceLoader;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
@@ -86,6 +87,6 @@ final class DocumentationGeneratorTaskListener implements TaskListener {
|
||||
}
|
||||
|
||||
private static String getSimpleClassName(URI path) {
|
||||
return Path.of(path).getFileName().toString().replace(".java", "");
|
||||
return Paths.get(path).getFileName().toString().replace(".java", "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
package tech.picnic.errorprone.documentation;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.VerifyException;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.FormatMethod;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.net.URI;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import tech.picnic.errorprone.documentation.RefasterRuleCollectionTestExtractor.RefasterTestCases;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* An {@link Extractor} that describes how to extract data from Refaster rule input and output test
|
||||
* classes.
|
||||
*/
|
||||
// XXX: Drop this extractor if/when the Refaster test framework is reimplemented such that tests can
|
||||
// be located alongside rules, rather than in two additional resource files as currently required by
|
||||
// `RefasterRuleCollection`.
|
||||
@Immutable
|
||||
@AutoService(Extractor.class)
|
||||
@SuppressWarnings("rawtypes" /* See https://github.com/google/auto/issues/870. */)
|
||||
public final class RefasterRuleCollectionTestExtractor implements Extractor<RefasterTestCases> {
|
||||
private static final Matcher<ClassTree> IS_REFASTER_RULE_COLLECTION_TEST_CASE =
|
||||
isSubtypeOf("tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase");
|
||||
private static final Pattern TEST_CLASS_NAME_PATTERN = Pattern.compile("(.*)Test");
|
||||
private static final Pattern TEST_CLASS_FILE_NAME_PATTERN =
|
||||
Pattern.compile(".*(Input|Output)\\.java");
|
||||
private static final Pattern TEST_METHOD_NAME_PATTERN = Pattern.compile("test(.*)");
|
||||
private static final String LINE_SEPARATOR = "\n";
|
||||
private static final Splitter LINE_SPLITTER = Splitter.on(LINE_SEPARATOR);
|
||||
|
||||
/** Instantiates a new {@link RefasterRuleCollectionTestExtractor} instance. */
|
||||
public RefasterRuleCollectionTestExtractor() {}
|
||||
|
||||
@Override
|
||||
public String identifier() {
|
||||
return "refaster-rule-collection-test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RefasterTestCases> tryExtract(ClassTree tree, VisitorState state) {
|
||||
if (!IS_REFASTER_RULE_COLLECTION_TEST_CASE.matches(tree, state)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
URI sourceFile = state.getPath().getCompilationUnit().getSourceFile().toUri();
|
||||
return Optional.of(
|
||||
RefasterTestCases.create(
|
||||
sourceFile,
|
||||
getRuleCollectionName(tree),
|
||||
isInputFile(sourceFile),
|
||||
getRefasterTestCases(tree, state)));
|
||||
}
|
||||
|
||||
private static String getRuleCollectionName(ClassTree tree) {
|
||||
String className = tree.getSimpleName().toString();
|
||||
|
||||
// XXX: Instead of throwing an error here, it'd be nicer to have a bug checker validate key
|
||||
// aspects of `RefasterRuleCollectionTestCase` subtypes.
|
||||
return tryExtractPatternGroup(className, TEST_CLASS_NAME_PATTERN)
|
||||
.orElseThrow(
|
||||
violation(
|
||||
"Refaster rule collection test class name '%s' does not match '%s'",
|
||||
className, TEST_CLASS_NAME_PATTERN));
|
||||
}
|
||||
|
||||
private static boolean isInputFile(URI sourceFile) {
|
||||
String path = sourceFile.getPath();
|
||||
|
||||
// XXX: Instead of throwing an error here, it'd be nicer to have a bug checker validate key
|
||||
// aspects of `RefasterRuleCollectionTestCase` subtypes.
|
||||
return "Input"
|
||||
.equals(
|
||||
tryExtractPatternGroup(path, TEST_CLASS_FILE_NAME_PATTERN)
|
||||
.orElseThrow(
|
||||
violation(
|
||||
"Refaster rule collection test file name '%s' does not match '%s'",
|
||||
path, TEST_CLASS_FILE_NAME_PATTERN)));
|
||||
}
|
||||
|
||||
private static ImmutableList<RefasterTestCase> getRefasterTestCases(
|
||||
ClassTree tree, VisitorState state) {
|
||||
return tree.getMembers().stream()
|
||||
.filter(MethodTree.class::isInstance)
|
||||
.map(MethodTree.class::cast)
|
||||
.flatMap(m -> tryExtractRefasterTestCase(m, state).stream())
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
private static Optional<RefasterTestCase> tryExtractRefasterTestCase(
|
||||
MethodTree method, VisitorState state) {
|
||||
return tryExtractPatternGroup(method.getName().toString(), TEST_METHOD_NAME_PATTERN)
|
||||
.map(name -> RefasterTestCase.create(name, getFormattedSource(method, state)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source code for the specified method.
|
||||
*
|
||||
* @implNote This operation attempts to trim leading whitespace, such that the start and end of
|
||||
* the method declaration are aligned. The implemented heuristic assumes that the code is
|
||||
* formatted using Google Java Format.
|
||||
*/
|
||||
// XXX: Leading Javadoc and other comments are currently not extracted. Consider fixing this.
|
||||
private static String getFormattedSource(MethodTree method, VisitorState state) {
|
||||
String source = SourceCode.treeToString(method, state);
|
||||
int finalNewline = source.lastIndexOf(LINE_SEPARATOR);
|
||||
if (finalNewline < 0) {
|
||||
return source;
|
||||
}
|
||||
|
||||
int indentation = Math.max(0, source.lastIndexOf(' ') - finalNewline);
|
||||
String prefixToStrip = " ".repeat(indentation);
|
||||
|
||||
return LINE_SPLITTER
|
||||
.splitToStream(source)
|
||||
.map(line -> line.startsWith(prefixToStrip) ? line.substring(indentation) : line)
|
||||
.collect(joining(LINE_SEPARATOR));
|
||||
}
|
||||
|
||||
private static Optional<String> tryExtractPatternGroup(String input, Pattern pattern) {
|
||||
java.util.regex.Matcher matcher = pattern.matcher(input);
|
||||
return matcher.matches() ? Optional.of(matcher.group(1)) : Optional.empty();
|
||||
}
|
||||
|
||||
@FormatMethod
|
||||
private static Supplier<VerifyException> violation(String format, Object... args) {
|
||||
return () -> new VerifyException(String.format(format, args));
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
@JsonDeserialize(as = AutoValue_RefasterRuleCollectionTestExtractor_RefasterTestCases.class)
|
||||
abstract static class RefasterTestCases {
|
||||
static RefasterTestCases create(
|
||||
URI source,
|
||||
String ruleCollection,
|
||||
boolean isInput,
|
||||
ImmutableList<RefasterTestCase> testCases) {
|
||||
return new AutoValue_RefasterRuleCollectionTestExtractor_RefasterTestCases(
|
||||
source, ruleCollection, isInput, testCases);
|
||||
}
|
||||
|
||||
abstract URI source();
|
||||
|
||||
abstract String ruleCollection();
|
||||
|
||||
abstract boolean isInput();
|
||||
|
||||
abstract ImmutableList<RefasterTestCase> testCases();
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
@JsonDeserialize(as = AutoValue_RefasterRuleCollectionTestExtractor_RefasterTestCase.class)
|
||||
abstract static class RefasterTestCase {
|
||||
static RefasterTestCase create(String name, String content) {
|
||||
return new AutoValue_RefasterRuleCollectionTestExtractor_RefasterTestCase(name, content);
|
||||
}
|
||||
|
||||
abstract String name();
|
||||
|
||||
abstract String content();
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,10 @@ import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.BugPatternTestCase;
|
||||
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.BugPatternTestCases;
|
||||
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.IdentificationTestEntry;
|
||||
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.ReplacementTestEntry;
|
||||
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.TestCase;
|
||||
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.TestCases;
|
||||
|
||||
final class BugPatternTestExtractorTest {
|
||||
@Test
|
||||
@@ -269,11 +269,11 @@ final class BugPatternTestExtractorTest {
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"SingleFileCompilationTestHelperTest",
|
||||
BugPatternTestCases.create(
|
||||
TestCases.create(
|
||||
URI.create("file:///SingleFileCompilationTestHelperTest.java"),
|
||||
"SingleFileCompilationTestHelperTest",
|
||||
ImmutableList.of(
|
||||
BugPatternTestCase.create(
|
||||
TestCase.create(
|
||||
"SingleFileCompilationTestHelperTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
IdentificationTestEntry.create(
|
||||
@@ -302,11 +302,11 @@ final class BugPatternTestExtractorTest {
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"SingleFileCompilationTestHelperWithSetArgsTest",
|
||||
BugPatternTestCases.create(
|
||||
TestCases.create(
|
||||
URI.create("file:///SingleFileCompilationTestHelperWithSetArgsTest.java"),
|
||||
"SingleFileCompilationTestHelperWithSetArgsTest",
|
||||
ImmutableList.of(
|
||||
BugPatternTestCase.create(
|
||||
TestCase.create(
|
||||
"SingleFileCompilationTestHelperWithSetArgsTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
IdentificationTestEntry.create(
|
||||
@@ -335,11 +335,11 @@ final class BugPatternTestExtractorTest {
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"MultiFileCompilationTestHelperTest",
|
||||
BugPatternTestCases.create(
|
||||
TestCases.create(
|
||||
URI.create("file:///MultiFileCompilationTestHelperTest.java"),
|
||||
"MultiFileCompilationTestHelperTest",
|
||||
ImmutableList.of(
|
||||
BugPatternTestCase.create(
|
||||
TestCase.create(
|
||||
"MultiFileCompilationTestHelperTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
IdentificationTestEntry.create(
|
||||
@@ -370,11 +370,11 @@ final class BugPatternTestExtractorTest {
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"SingleFileBugCheckerRefactoringTestHelperTest",
|
||||
BugPatternTestCases.create(
|
||||
TestCases.create(
|
||||
URI.create("file:///SingleFileBugCheckerRefactoringTestHelperTest.java"),
|
||||
"SingleFileBugCheckerRefactoringTestHelperTest",
|
||||
ImmutableList.of(
|
||||
BugPatternTestCase.create(
|
||||
TestCase.create(
|
||||
"SingleFileBugCheckerRefactoringTestHelperTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
ReplacementTestEntry.create(
|
||||
@@ -408,12 +408,12 @@ final class BugPatternTestExtractorTest {
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest",
|
||||
BugPatternTestCases.create(
|
||||
TestCases.create(
|
||||
URI.create(
|
||||
"file:///SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest.java"),
|
||||
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest",
|
||||
ImmutableList.of(
|
||||
BugPatternTestCase.create(
|
||||
TestCase.create(
|
||||
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
ReplacementTestEntry.create(
|
||||
@@ -444,11 +444,11 @@ final class BugPatternTestExtractorTest {
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"MultiFileBugCheckerRefactoringTestHelperTest",
|
||||
BugPatternTestCases.create(
|
||||
TestCases.create(
|
||||
URI.create("file:///MultiFileBugCheckerRefactoringTestHelperTest.java"),
|
||||
"MultiFileBugCheckerRefactoringTestHelperTest",
|
||||
ImmutableList.of(
|
||||
BugPatternTestCase.create(
|
||||
TestCase.create(
|
||||
"MultiFileBugCheckerRefactoringTestHelperTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
ReplacementTestEntry.create(
|
||||
@@ -484,16 +484,16 @@ final class BugPatternTestExtractorTest {
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"CompilationAndBugCheckerRefactoringTestHelpersTest",
|
||||
BugPatternTestCases.create(
|
||||
TestCases.create(
|
||||
URI.create("file:///CompilationAndBugCheckerRefactoringTestHelpersTest.java"),
|
||||
"CompilationAndBugCheckerRefactoringTestHelpersTest",
|
||||
ImmutableList.of(
|
||||
BugPatternTestCase.create(
|
||||
TestCase.create(
|
||||
"CompilationAndBugCheckerRefactoringTestHelpersTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
IdentificationTestEntry.create(
|
||||
"A.java", "// BUG: Diagnostic contains:\nclass A {}\n"))),
|
||||
BugPatternTestCase.create(
|
||||
TestCase.create(
|
||||
"CompilationAndBugCheckerRefactoringTestHelpersTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
ReplacementTestEntry.create(
|
||||
@@ -532,17 +532,17 @@ final class BugPatternTestExtractorTest {
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest",
|
||||
BugPatternTestCases.create(
|
||||
TestCases.create(
|
||||
URI.create(
|
||||
"file:///CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.java"),
|
||||
"pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest",
|
||||
ImmutableList.of(
|
||||
BugPatternTestCase.create(
|
||||
TestCase.create(
|
||||
"pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.CustomTestChecker",
|
||||
ImmutableList.of(
|
||||
IdentificationTestEntry.create(
|
||||
"A.java", "// BUG: Diagnostic contains:\nclass A {}\n"))),
|
||||
BugPatternTestCase.create(
|
||||
TestCase.create(
|
||||
"pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.CustomTestChecker2",
|
||||
ImmutableList.of(
|
||||
ReplacementTestEntry.create(
|
||||
@@ -550,9 +550,9 @@ final class BugPatternTestExtractorTest {
|
||||
}
|
||||
|
||||
private static void verifyGeneratedFileContent(
|
||||
Path outputDirectory, String testClass, BugPatternTestCases expected) {
|
||||
Path outputDirectory, String testClass, TestCases expected) {
|
||||
assertThat(outputDirectory.resolve(String.format("bugpattern-test-%s.json", testClass)))
|
||||
.exists()
|
||||
.returns(expected, path -> Json.read(path, BugPatternTestCases.class));
|
||||
.returns(expected, path -> Json.read(path, TestCases.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import static org.junit.jupiter.api.condition.OS.WINDOWS;
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.errorprone.VisitorState;
|
||||
@@ -40,8 +41,7 @@ final class DocumentationGeneratorTaskListenerTest {
|
||||
entry ->
|
||||
AclEntry.newBuilder(entry)
|
||||
.setPermissions(
|
||||
Sets.difference(
|
||||
entry.permissions(), Sets.immutableEnumSet(ADD_SUBDIRECTORY)))
|
||||
Sets.difference(entry.permissions(), ImmutableSet.of(ADD_SUBDIRECTORY)))
|
||||
.build())
|
||||
.collect(toImmutableList()));
|
||||
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
package tech.picnic.errorprone.documentation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import com.google.common.base.VerifyException;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import tech.picnic.errorprone.documentation.RefasterRuleCollectionTestExtractor.RefasterTestCase;
|
||||
import tech.picnic.errorprone.documentation.RefasterRuleCollectionTestExtractor.RefasterTestCases;
|
||||
|
||||
final class RefasterRuleCollectionTestExtractorTest {
|
||||
@Test
|
||||
void noRefasterRuleTest(@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory, "NoRefasterRuleTest.java", "public final class NoRefasterRuleTest {}");
|
||||
|
||||
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidTestClassName(@TempDir Path outputDirectory) {
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"InvalidTestClassNameInput.java",
|
||||
"import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;",
|
||||
"",
|
||||
"final class InvalidTestClassName implements RefasterRuleCollectionTestCase {}"))
|
||||
.cause()
|
||||
.isInstanceOf(VerifyException.class)
|
||||
.hasMessage(
|
||||
"Refaster rule collection test class name 'InvalidTestClassName' does not match '(.*)Test'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidFileName(@TempDir Path outputDirectory) {
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"InvalidFileNameTest.java",
|
||||
"import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;",
|
||||
"",
|
||||
"final class InvalidFileNameTest implements RefasterRuleCollectionTestCase {}"))
|
||||
.cause()
|
||||
.isInstanceOf(VerifyException.class)
|
||||
.hasMessage(
|
||||
"Refaster rule collection test file name '/InvalidFileNameTest.java' does not match '.*(Input|Output)\\.java'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyRefasterRuleCollectionTestInput(@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"EmptyRefasterRuleCollectionTestInput.java",
|
||||
"import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;",
|
||||
"",
|
||||
"final class EmptyRefasterRuleCollectionTest implements RefasterRuleCollectionTestCase {}");
|
||||
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"EmptyRefasterRuleCollectionTestInput",
|
||||
RefasterTestCases.create(
|
||||
URI.create("file:///EmptyRefasterRuleCollectionTestInput.java"),
|
||||
"EmptyRefasterRuleCollection",
|
||||
/* isInput= */ true,
|
||||
ImmutableList.of()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void singletonRefasterRuleCollectionTestOutput(@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"SingletonRefasterRuleCollectionTestOutput.java",
|
||||
"import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;",
|
||||
"",
|
||||
"final class SingletonRefasterRuleCollectionTest implements RefasterRuleCollectionTestCase {",
|
||||
" int testMyRule() {",
|
||||
" return 42;",
|
||||
" }",
|
||||
"}");
|
||||
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"SingletonRefasterRuleCollectionTestOutput",
|
||||
RefasterTestCases.create(
|
||||
URI.create("file:///SingletonRefasterRuleCollectionTestOutput.java"),
|
||||
"SingletonRefasterRuleCollection",
|
||||
/* isInput= */ false,
|
||||
ImmutableList.of(
|
||||
RefasterTestCase.create(
|
||||
"MyRule",
|
||||
"""
|
||||
int testMyRule() {
|
||||
return 42;
|
||||
}"""))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void complexRefasterRuleCollectionTestOutput(@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"pkg/ComplexRefasterRuleCollectionTestInput.java",
|
||||
"package pkg;",
|
||||
"",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;",
|
||||
"",
|
||||
"final class ComplexRefasterRuleCollectionTest implements RefasterRuleCollectionTestCase {",
|
||||
" private static final String IGNORED_CONSTANT = \"constant\";",
|
||||
"",
|
||||
" @Override",
|
||||
" public ImmutableSet<Object> elidedTypesAndStaticImports() {",
|
||||
" return ImmutableSet.of();",
|
||||
" }",
|
||||
"",
|
||||
" /** Javadoc. */",
|
||||
" String testFirstRule() {",
|
||||
" return \"Don't panic\";",
|
||||
" }",
|
||||
"",
|
||||
" // Comment.",
|
||||
" String testSecondRule() {",
|
||||
" return \"Carry a towel\";",
|
||||
" }",
|
||||
"",
|
||||
" void testEmptyRule() {}",
|
||||
"}");
|
||||
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"ComplexRefasterRuleCollectionTestInput",
|
||||
RefasterTestCases.create(
|
||||
URI.create("file:///pkg/ComplexRefasterRuleCollectionTestInput.java"),
|
||||
"ComplexRefasterRuleCollection",
|
||||
/* isInput= */ true,
|
||||
ImmutableList.of(
|
||||
RefasterTestCase.create(
|
||||
"FirstRule",
|
||||
"""
|
||||
String testFirstRule() {
|
||||
return "Don't panic";
|
||||
}"""),
|
||||
RefasterTestCase.create(
|
||||
"SecondRule",
|
||||
"""
|
||||
String testSecondRule() {
|
||||
return "Carry a towel";
|
||||
}"""),
|
||||
RefasterTestCase.create("EmptyRule", "void testEmptyRule() {}"))));
|
||||
}
|
||||
|
||||
private static void verifyGeneratedFileContent(
|
||||
Path outputDirectory, String testIdentifier, RefasterTestCases expected) {
|
||||
assertThat(
|
||||
outputDirectory.resolve(
|
||||
String.format("refaster-rule-collection-test-%s.json", testIdentifier)))
|
||||
.exists()
|
||||
.returns(expected, path -> Json.read(path, RefasterTestCases.class));
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.19.2-SNAPSHOT</version>
|
||||
<version>0.16.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>error-prone-contrib</artifactId>
|
||||
@@ -55,12 +55,7 @@
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>refaster-support</artifactId>
|
||||
<!-- XXX: One would expect this to be a `provided` dependency (as
|
||||
Refaster rules are interpreted by the `refaster-runner` module),
|
||||
but the `OptionalOrElseGet` bug checker defined by this module
|
||||
depends on the `RequiresComputation` matcher that
|
||||
`refaster-support` primarily exposes for use by Refaster rules.
|
||||
Review this setup. (Should the matchers be moved elsewhere?) -->
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
@@ -72,15 +67,6 @@
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto.service</groupId>
|
||||
<artifactId>auto-service-annotations</artifactId>
|
||||
@@ -96,11 +82,6 @@
|
||||
<artifactId>guava</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
@@ -141,11 +122,6 @@
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
@@ -166,22 +142,11 @@
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.gaul</groupId>
|
||||
<artifactId>modernizer-maven-plugin</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<!-- XXX: Consider making optional? Ship only the definitions? -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.immutables</groupId>
|
||||
<artifactId>value-annotations</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jooq</groupId>
|
||||
<artifactId>jooq</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jspecify</groupId>
|
||||
<artifactId>jspecify</artifactId>
|
||||
@@ -209,10 +174,15 @@
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>bson</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongodb-driver-core</artifactId>
|
||||
<scope>test</scope>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
@@ -226,7 +196,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
<artifactId>rewrite-java-17</artifactId>
|
||||
<artifactId>rewrite-java-11</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -317,55 +287,6 @@
|
||||
<arg>-Xplugin:DocumentationGenerator -XoutputDirectory=${project.build.directory}/docs</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
<executions>
|
||||
<!-- The Refaster input/output test classes used by
|
||||
`RefasterRuleCollection` are modelled as classpath
|
||||
resources, and thus not subject to the default test
|
||||
compilation step. These two custom compilation steps
|
||||
serve two purposes:
|
||||
- To provide early feedback in case of syntax errors.
|
||||
- To enable the `DocumentationGenerator` compiler
|
||||
plugin to extract documentation metadata from them.
|
||||
Note that the input and output files must be compiled
|
||||
separately and to distinct output directories, as they
|
||||
define the same set of class names. -->
|
||||
<!-- XXX: Drop these executions if/when the Refaster
|
||||
test framework is reimplemented such that tests can be
|
||||
located alongside rules, rather than in two additional
|
||||
resource files. -->
|
||||
<execution>
|
||||
<id>compile-refaster-test-input</id>
|
||||
<goals>
|
||||
<goal>testCompile</goal>
|
||||
</goals>
|
||||
<phase>process-test-resources</phase>
|
||||
<configuration>
|
||||
<compileSourceRoots>
|
||||
<compileSourceRoot>${project.basedir}/src/test/resources</compileSourceRoot>
|
||||
</compileSourceRoots>
|
||||
<testIncludes>
|
||||
<testInclude>**/*Input.java</testInclude>
|
||||
</testIncludes>
|
||||
<outputDirectory>${project.build.directory}/refaster-test-input</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>compile-refaster-test-output</id>
|
||||
<goals>
|
||||
<goal>testCompile</goal>
|
||||
</goals>
|
||||
<phase>process-test-resources</phase>
|
||||
<configuration>
|
||||
<compileSourceRoots>
|
||||
<compileSourceRoot>${project.basedir}/src/test/resources</compileSourceRoot>
|
||||
</compileSourceRoots>
|
||||
<testIncludes>
|
||||
<testInclude>**/*Output.java</testInclude>
|
||||
</testIncludes>
|
||||
<outputDirectory>${project.build.directory}/refaster-test-output</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
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.LambdaExpressionTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.LambdaExpressionTree;
|
||||
import com.sun.source.tree.TypeCastTree;
|
||||
import com.sun.source.tree.VariableTree;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags lambda expressions that can be replaced with a method reference
|
||||
* of the form {@code T.class::cast}.
|
||||
*/
|
||||
// XXX: Consider folding this logic into the `MethodReferenceUsage` check of the
|
||||
// `error-prone-experimental` module.
|
||||
// XXX: This check and its tests are structurally nearly identical to `IsInstanceLambdaUsage`.
|
||||
// Unless folded into `MethodReferenceUsage`, consider merging the two.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Prefer `Class::cast` method reference over equivalent lambda expression",
|
||||
link = BUG_PATTERNS_BASE_URL + "ClassCastLambdaUsage",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class ClassCastLambdaUsage extends BugChecker implements LambdaExpressionTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Instantiates a new {@link ClassCastLambdaUsage} instance. */
|
||||
public ClassCastLambdaUsage() {}
|
||||
|
||||
@Override
|
||||
public Description matchLambdaExpression(LambdaExpressionTree tree, VisitorState state) {
|
||||
if (tree.getParameters().size() != 1 || !(tree.getBody() instanceof TypeCastTree typeCast)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
Type type = ASTHelpers.getType(typeCast);
|
||||
if (type == null
|
||||
|| type.isParameterized()
|
||||
|| type.isPrimitive()
|
||||
|| type.getKind() == TypeKind.TYPEVAR) {
|
||||
/*
|
||||
* The method reference syntax does not support casting to parameterized types, and type
|
||||
* variables aren't supported either. Additionally, `Class#cast` does not support the same
|
||||
* range of type conversions between (boxed) primitive types as the cast operator.
|
||||
*/
|
||||
// XXX: Depending on the declared type of the value being cast, in some cases we _can_ rewrite
|
||||
// primitive casts. Add support for this.
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
VariableTree param = Iterables.getOnlyElement(tree.getParameters());
|
||||
if (!ASTHelpers.getSymbol(param).equals(ASTHelpers.getSymbol(typeCast.getExpression()))) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
return describeMatch(
|
||||
tree,
|
||||
SuggestedFix.replace(
|
||||
tree, SourceCode.treeToString(typeCast.getType(), state) + ".class::cast"));
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.STYLE;
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
import static com.google.errorprone.matchers.Matchers.hasModifier;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.ErrorProneFlags;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.VariableTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.VariableTree;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.inject.Inject;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.utils.Flags;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags static constants that do not follow the upper snake case naming
|
||||
* convention.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Constant variables should adhere to the `UPPER_SNAKE_CASE` naming convention",
|
||||
link = BUG_PATTERNS_BASE_URL + "ConstantNaming",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = STYLE)
|
||||
@SuppressWarnings("java:S2160" /* Super class equality definition suffices. */)
|
||||
public final class ConstantNaming extends BugChecker implements VariableTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<VariableTree> IS_CONSTANT =
|
||||
allOf(hasModifier(Modifier.STATIC), hasModifier(Modifier.FINAL));
|
||||
private static final Matcher<VariableTree> IS_PRIVATE = hasModifier(Modifier.PRIVATE);
|
||||
private static final Pattern SNAKE_CASE = Pattern.compile("([a-z])([A-Z])");
|
||||
private static final ImmutableSet<String> DEFAULT_EXEMPTED_NAMES =
|
||||
ImmutableSet.of("serialVersionUID");
|
||||
|
||||
/**
|
||||
* Flag using which constant names that must not be flagged (in addition to those defined by
|
||||
* {@link #DEFAULT_EXEMPTED_NAMES}) can be specified.
|
||||
*/
|
||||
private static final String ADDITIONAL_EXEMPTED_NAMES_FLAG =
|
||||
"CanonicalConstantNaming:ExemptedNames";
|
||||
|
||||
private final ImmutableSet<String> exemptedNames;
|
||||
|
||||
/** Instantiates a default {@link ConstantNaming} instance. */
|
||||
public ConstantNaming() {
|
||||
this(ErrorProneFlags.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a customized {@link ConstantNaming}.
|
||||
*
|
||||
* @param flags Any provided command line flags.
|
||||
*/
|
||||
@Inject
|
||||
ConstantNaming(ErrorProneFlags flags) {
|
||||
exemptedNames =
|
||||
Sets.union(DEFAULT_EXEMPTED_NAMES, Flags.getSet(flags, ADDITIONAL_EXEMPTED_NAMES_FLAG))
|
||||
.immutableCopy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description matchVariable(VariableTree tree, VisitorState state) {
|
||||
String variableName = tree.getName().toString();
|
||||
if (!IS_CONSTANT.matches(tree, state) || exemptedNames.contains(variableName)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
String replacement = toUpperSnakeCase(variableName);
|
||||
if (replacement.equals(variableName)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
Description.Builder description = buildDescription(tree);
|
||||
if (!IS_PRIVATE.matches(tree, state)) {
|
||||
description.setMessage(
|
||||
"%s; consider renaming to '%s', though note that this is not a private constant"
|
||||
.formatted(message(), replacement));
|
||||
} else if (isVariableNameInUse(replacement, state)) {
|
||||
description.setMessage(
|
||||
"%s; consider renaming to '%s', though note that a variable with this name is already declared"
|
||||
.formatted(message(), replacement));
|
||||
} else {
|
||||
description.addFix(SuggestedFixes.renameVariable(tree, replacement, state));
|
||||
}
|
||||
|
||||
return description.build();
|
||||
}
|
||||
|
||||
private static String toUpperSnakeCase(String variableName) {
|
||||
return SNAKE_CASE.matcher(variableName).replaceAll("$1_$2").toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private static boolean isVariableNameInUse(String name, VisitorState state) {
|
||||
return Boolean.TRUE.equals(
|
||||
new TreeScanner<Boolean, @Nullable Void>() {
|
||||
@Override
|
||||
public Boolean visitVariable(VariableTree tree, @Nullable Void unused) {
|
||||
return ASTHelpers.getSymbol(tree).getSimpleName().contentEquals(name)
|
||||
|| super.visitVariable(tree, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean reduce(Boolean r1, Boolean r2) {
|
||||
return Boolean.TRUE.equals(r1) || Boolean.TRUE.equals(r2);
|
||||
}
|
||||
}.scan(state.getPath().getCompilationUnit(), null));
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public final class EmptyMethod extends BugChecker implements MethodTreeMatcher {
|
||||
}
|
||||
|
||||
private static boolean isInPossibleTestHelperClass(VisitorState state) {
|
||||
return Optional.ofNullable(state.findEnclosing(ClassTree.class))
|
||||
return Optional.ofNullable(ASTHelpers.findEnclosingNode(state.getPath(), ClassTree.class))
|
||||
.map(ClassTree::getSimpleName)
|
||||
.filter(name -> name.toString().contains("Test"))
|
||||
.isPresent();
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.PERFORMANCE;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.staticMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.symbolMatcher;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMultiset;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableTable;
|
||||
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.util.ASTHelpers;
|
||||
import com.google.errorprone.util.Visibility;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
||||
import com.sun.tools.javac.code.Symbol.VarSymbol;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.Element;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags single-argument method invocations with an iterable of explicitly
|
||||
* enumerated values, for which a semantically equivalent varargs variant (appears to) exists as
|
||||
* well.
|
||||
*
|
||||
* <p>This check drops selected {@link ImmutableSet#of} and {@link Set#of} invocations, with the
|
||||
* assumption that these operations do not deduplicate the collection of explicitly enumerated
|
||||
* values. It also drops {@link ImmutableMultiset#of} and {@link Set#of} invocations, with the
|
||||
* assumption that these do not materially impact iteration order.
|
||||
*
|
||||
* <p>This checker attempts to identify {@link Iterable}-accepting methods for which a varargs
|
||||
* overload exists, and suggests calling the varargs overload instead. This is an imperfect
|
||||
* heuristic, but it e.g. allows invocations of <a
|
||||
* href="https://immutables.github.io/immutable.html#copy-methods">Immutables-generated {@code
|
||||
* with*}</a> methods to be simplified.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Iterable creation can be avoided by using a varargs alternative method",
|
||||
link = BUG_PATTERNS_BASE_URL + "ExplicitArgumentEnumeration",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = {PERFORMANCE, SIMPLIFICATION})
|
||||
public final class ExplicitArgumentEnumeration extends BugChecker
|
||||
implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> EXPLICIT_ITERABLE_CREATOR =
|
||||
anyOf(
|
||||
staticMethod()
|
||||
.onClassAny(
|
||||
ImmutableList.class.getCanonicalName(),
|
||||
ImmutableMultiset.class.getCanonicalName(),
|
||||
ImmutableSet.class.getCanonicalName(),
|
||||
List.class.getCanonicalName(),
|
||||
Set.class.getCanonicalName())
|
||||
.named("of"),
|
||||
allOf(
|
||||
staticMethod()
|
||||
.onClassAny(
|
||||
ImmutableList.class.getCanonicalName(),
|
||||
ImmutableMultiset.class.getCanonicalName(),
|
||||
ImmutableSet.class.getCanonicalName())
|
||||
.named("copyOf"),
|
||||
symbolMatcher(
|
||||
(symbol, state) ->
|
||||
state
|
||||
.getSymtab()
|
||||
.arrayClass
|
||||
.equals(((MethodSymbol) symbol).params().get(0).type.tsym))),
|
||||
staticMethod().onClass(Arrays.class.getCanonicalName()).named("asList"));
|
||||
private static final Matcher<ExpressionTree> IMMUTABLE_COLLECTION_BUILDER =
|
||||
instanceMethod().onDescendantOf(ImmutableCollection.Builder.class.getCanonicalName());
|
||||
private static final Matcher<ExpressionTree> OBJECT_ENUMERABLE_ASSERT =
|
||||
instanceMethod().onDescendantOf("org.assertj.core.api.ObjectEnumerableAssert");
|
||||
private static final Matcher<ExpressionTree> STEP_VERIFIER_STEP =
|
||||
instanceMethod().onDescendantOf("reactor.test.StepVerifier.Step");
|
||||
private static final ImmutableTable<Matcher<ExpressionTree>, String, String> ALTERNATIVE_METHODS =
|
||||
ImmutableTable.<Matcher<ExpressionTree>, String, String>builder()
|
||||
.put(IMMUTABLE_COLLECTION_BUILDER, "addAll", "add")
|
||||
.put(OBJECT_ENUMERABLE_ASSERT, "containsAnyElementsOf", "containsAnyOf")
|
||||
.put(OBJECT_ENUMERABLE_ASSERT, "containsAll", "contains")
|
||||
.put(OBJECT_ENUMERABLE_ASSERT, "containsExactlyElementsOf", "containsExactly")
|
||||
.put(
|
||||
OBJECT_ENUMERABLE_ASSERT,
|
||||
"containsExactlyInAnyOrderElementsOf",
|
||||
"containsExactlyInAnyOrder")
|
||||
.put(OBJECT_ENUMERABLE_ASSERT, "containsOnlyElementsOf", "containsOnly")
|
||||
.put(OBJECT_ENUMERABLE_ASSERT, "containsOnlyOnceElementsOf", "containsOnlyOnce")
|
||||
.put(OBJECT_ENUMERABLE_ASSERT, "doesNotContainAnyElementsOf", "doesNotContain")
|
||||
.put(OBJECT_ENUMERABLE_ASSERT, "hasSameElementsAs", "containsOnly")
|
||||
.put(STEP_VERIFIER_STEP, "expectNextSequence", "expectNext")
|
||||
.build();
|
||||
|
||||
/** Instantiates a new {@link ExplicitArgumentEnumeration} instance. */
|
||||
public ExplicitArgumentEnumeration() {}
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
if (tree.getArguments().size() != 1) {
|
||||
/* Performance optimization: non-unary method invocations cannot be simplified. */
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
MethodSymbol method = ASTHelpers.getSymbol(tree);
|
||||
if (!isUnaryIterableAcceptingMethod(method, state) || isLocalOverload(method, state)) {
|
||||
/*
|
||||
* This isn't a method invocation we can simplify, or it's an invocation of a local overload.
|
||||
* The latter type of invocation we do not suggest replacing, as this is fairly likely to
|
||||
* introduce an unbounded recursive call chain.
|
||||
*/
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
ExpressionTree argument = tree.getArguments().get(0);
|
||||
if (!EXPLICIT_ITERABLE_CREATOR.matches(argument, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
return trySuggestCallingVarargsOverload(method, (MethodInvocationTree) argument, state)
|
||||
.or(() -> trySuggestCallingCustomAlternative(tree, (MethodInvocationTree) argument, state))
|
||||
.map(fix -> describeMatch(tree, fix))
|
||||
.orElse(Description.NO_MATCH);
|
||||
}
|
||||
|
||||
private static boolean isUnaryIterableAcceptingMethod(MethodSymbol method, VisitorState state) {
|
||||
List<VarSymbol> params = method.params();
|
||||
return !method.isVarArgs()
|
||||
&& params.size() == 1
|
||||
&& ASTHelpers.isSubtype(params.get(0).type, state.getSymtab().iterableType, state);
|
||||
}
|
||||
|
||||
private static boolean isLocalOverload(MethodSymbol calledMethod, VisitorState state) {
|
||||
MethodTree enclosingMethod = state.findEnclosing(MethodTree.class);
|
||||
if (enclosingMethod == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MethodSymbol callingMethod = ASTHelpers.getSymbol(enclosingMethod);
|
||||
return Objects.equals(callingMethod.getEnclosingElement(), calledMethod.getEnclosingElement())
|
||||
&& callingMethod.getSimpleName().equals(calledMethod.getSimpleName());
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix> trySuggestCallingVarargsOverload(
|
||||
MethodSymbol method, MethodInvocationTree argument, VisitorState state) {
|
||||
/*
|
||||
* Collect all overloads of the given method that we are sure to be able to call. Note that the
|
||||
* `isAtLeastAsVisible` check is conservative heuristic.
|
||||
*/
|
||||
ImmutableList<MethodSymbol> overloads =
|
||||
ASTHelpers.matchingMethods(
|
||||
method.getSimpleName(),
|
||||
m -> isAtLeastAsVisible(m, method),
|
||||
method.enclClass().type,
|
||||
state.getTypes())
|
||||
.collect(toImmutableList());
|
||||
|
||||
/*
|
||||
* If all overloads have a single parameter, and at least one of them is a varargs method, then
|
||||
* we assume that unwrapping the iterable argument will cause a suitable overload to be invoked.
|
||||
* (Note that there may be multiple varargs overloads, either with different parameter types, or
|
||||
* due to method overriding; this check does not attempt to determine which exact method or
|
||||
* overload will be invoked as a result of the suggested simplification.)
|
||||
*
|
||||
* Note that this is a (highly!) imperfect heuristic, but it is sufficient to prevent e.g.
|
||||
* unwrapping of arguments to `org.jooq.impl.DSL#row`, which can cause the expression's return
|
||||
* type to change from `RowN` to (e.g.) `Row2`.
|
||||
*/
|
||||
// XXX: There are certainly cases where it _would_ be nice to unwrap the arguments to
|
||||
// `org.jooq.impl.DSL#row(Collection<?>)`. Look into this.
|
||||
// XXX: Ideally we do check that one of the overloads accepts the unwrapped arguments.
|
||||
// XXX: Ideally we validate that eligible overloads have compatible return types.
|
||||
boolean hasLikelySuitableVarargsOverload =
|
||||
overloads.stream().allMatch(m -> m.params().size() == 1)
|
||||
&& overloads.stream().anyMatch(MethodSymbol::isVarArgs);
|
||||
|
||||
return hasLikelySuitableVarargsOverload
|
||||
? Optional.of(SourceCode.unwrapMethodInvocation(argument, state))
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix> trySuggestCallingCustomAlternative(
|
||||
MethodInvocationTree tree, MethodInvocationTree argument, VisitorState state) {
|
||||
return ALTERNATIVE_METHODS.rowMap().entrySet().stream()
|
||||
.filter(e -> e.getKey().matches(tree, state))
|
||||
.findFirst()
|
||||
.flatMap(e -> trySuggestCallingCustomAlternative(tree, argument, state, e.getValue()));
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix> trySuggestCallingCustomAlternative(
|
||||
MethodInvocationTree tree,
|
||||
MethodInvocationTree argument,
|
||||
VisitorState state,
|
||||
Map<String, String> alternatives) {
|
||||
return Optional.ofNullable(
|
||||
alternatives.get(ASTHelpers.getSymbol(tree).getSimpleName().toString()))
|
||||
.map(
|
||||
replacement ->
|
||||
SuggestedFix.builder()
|
||||
.merge(SuggestedFixes.renameMethodInvocation(tree, replacement, state))
|
||||
.merge(SourceCode.unwrapMethodInvocation(argument, state))
|
||||
.build());
|
||||
}
|
||||
|
||||
private static boolean isAtLeastAsVisible(Element symbol, Element reference) {
|
||||
return Visibility.fromModifiers(symbol.getModifiers())
|
||||
.compareTo(Visibility.fromModifiers(reference.getModifiers()))
|
||||
>= 0;
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,6 @@ import java.util.Formatter;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.utils.MoreASTHelpers;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
@@ -204,10 +203,14 @@ public final class FormatStringConcatenation extends BugChecker
|
||||
|
||||
ExpressionTree argument = ASTHelpers.stripParentheses(arguments.get(argPosition));
|
||||
return argument instanceof BinaryTree
|
||||
&& MoreASTHelpers.isStringTyped(argument, state)
|
||||
&& isStringTyped(argument, state)
|
||||
&& ASTHelpers.constValue(argument, String.class) == null;
|
||||
}
|
||||
|
||||
private static boolean isStringTyped(ExpressionTree tree, VisitorState state) {
|
||||
return ASTHelpers.isSameType(ASTHelpers.getType(tree), state.getSymtab().stringType, state);
|
||||
}
|
||||
|
||||
private static class ReplacementArgumentsConstructor
|
||||
extends SimpleTreeVisitor<@Nullable Void, VisitorState> {
|
||||
private final StringBuilder formatString = new StringBuilder();
|
||||
@@ -220,7 +223,7 @@ public final class FormatStringConcatenation extends BugChecker
|
||||
|
||||
@Override
|
||||
public @Nullable Void visitBinary(BinaryTree tree, VisitorState state) {
|
||||
if (tree.getKind() == Kind.PLUS && MoreASTHelpers.isStringTyped(tree, state)) {
|
||||
if (tree.getKind() == Kind.PLUS && isStringTyped(tree, state)) {
|
||||
tree.getLeftOperand().accept(this, state);
|
||||
tree.getRightOperand().accept(this, state);
|
||||
} else {
|
||||
|
||||
@@ -39,7 +39,6 @@ import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.code.Types;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
@@ -52,7 +51,6 @@ import tech.picnic.errorprone.utils.SourceCode;
|
||||
// is effectively the identity operation.
|
||||
// XXX: Also flag nullary instance method invocations that represent an identity conversion, such as
|
||||
// `Boolean#booleanValue()`, `Byte#byteValue()` and friends.
|
||||
// XXX: Also flag redundant round-trip conversions such as `path.toFile().toPath()`.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Avoid or clarify identity conversions",
|
||||
@@ -85,7 +83,6 @@ public final class IdentityConversion extends BugChecker implements MethodInvoca
|
||||
ImmutableSetMultimap.class.getCanonicalName(),
|
||||
ImmutableTable.class.getCanonicalName())
|
||||
.named("copyOf"),
|
||||
staticMethod().onClass(Instant.class.getCanonicalName()).namedAnyOf("from"),
|
||||
staticMethod().onClass(Matchers.class.getCanonicalName()).namedAnyOf("allOf", "anyOf"),
|
||||
staticMethod().onClass("reactor.adapter.rxjava.RxJava2Adapter"),
|
||||
staticMethod()
|
||||
|
||||
@@ -25,8 +25,6 @@ import tech.picnic.errorprone.utils.SourceCode;
|
||||
*/
|
||||
// XXX: Consider folding this logic into the `MethodReferenceUsage` check of the
|
||||
// `error-prone-experimental` module.
|
||||
// XXX: This check and its tests are structurally nearly identical to `ClassCastLambdaUsage`. Unless
|
||||
// folded into `MethodReferenceUsage`, consider merging the two.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Prefer `Class::isInstance` method reference over equivalent lambda expression",
|
||||
|
||||
@@ -16,7 +16,7 @@ import static tech.picnic.errorprone.utils.MoreJUnitMatchers.TEST_METHOD;
|
||||
import static tech.picnic.errorprone.utils.MoreMatchers.hasMetaAnnotation;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
@@ -70,7 +70,7 @@ public final class JUnitClassModifiers extends BugChecker implements ClassTreeMa
|
||||
SuggestedFixes.removeModifiers(
|
||||
tree.getModifiers(),
|
||||
state,
|
||||
Sets.immutableEnumSet(Modifier.PRIVATE, Modifier.PROTECTED, Modifier.PUBLIC))
|
||||
ImmutableSet.of(Modifier.PRIVATE, Modifier.PROTECTED, Modifier.PUBLIC))
|
||||
.ifPresent(fixBuilder::merge);
|
||||
|
||||
if (!HAS_SPRING_CONFIGURATION_ANNOTATION.matches(tree, state)) {
|
||||
|
||||
@@ -101,8 +101,6 @@ public final class JUnitMethodDeclaration extends BugChecker implements MethodTr
|
||||
.build());
|
||||
}
|
||||
|
||||
// XXX: Consider dropping leading underscores that otherwise result when canonicalizing
|
||||
// `test_some_method_name`.
|
||||
private static Optional<String> tryCanonicalizeMethodName(MethodSymbol symbol) {
|
||||
return Optional.of(symbol.getQualifiedName().toString())
|
||||
.filter(name -> name.startsWith(TEST_PREFIX))
|
||||
|
||||
@@ -14,7 +14,6 @@ import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Comparators;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.ErrorProneFlags;
|
||||
import com.google.errorprone.VisitorState;
|
||||
@@ -35,8 +34,10 @@ import com.sun.source.tree.Tree;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import com.sun.tools.javac.code.Symtab;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
@@ -162,12 +163,7 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
|
||||
/* For now we don't force sorting on numeric types. */
|
||||
return Stream.of(
|
||||
symtab.annotationType,
|
||||
symtab.booleanType,
|
||||
symtab.charType,
|
||||
symtab.classType,
|
||||
symtab.enumSym.type,
|
||||
symtab.stringType)
|
||||
symtab.annotationType, symtab.classType, symtab.enumSym.type, symtab.stringType)
|
||||
.anyMatch(t -> ASTHelpers.isSubtype(elemType, t, state));
|
||||
}
|
||||
|
||||
@@ -229,8 +225,10 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
excludedAnnotations(flags));
|
||||
}
|
||||
|
||||
private static ImmutableSet<String> excludedAnnotations(ErrorProneFlags flags) {
|
||||
return Sets.union(BLACKLISTED_ANNOTATIONS, Flags.getSet(flags, EXCLUDED_ANNOTATIONS_FLAG))
|
||||
.immutableCopy();
|
||||
private static ImmutableList<String> excludedAnnotations(ErrorProneFlags flags) {
|
||||
Set<String> exclusions = new HashSet<>();
|
||||
exclusions.addAll(Flags.getList(flags, EXCLUDED_ANNOTATIONS_FLAG));
|
||||
exclusions.addAll(BLACKLISTED_ANNOTATIONS);
|
||||
return ImmutableList.copyOf(exclusions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,371 +0,0 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableTable.toImmutableTable;
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.REFACTORING;
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
import static com.google.errorprone.predicates.TypePredicates.isDescendantOf;
|
||||
import static com.google.errorprone.predicates.TypePredicates.isExactType;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.auto.value.AutoValue.CopyAnnotations;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableTable;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.google.errorprone.annotations.Var;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.AnnotationTreeMatcher;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.IdentifierTreeMatcher;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MemberReferenceTreeMatcher;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MemberSelectTreeMatcher;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.predicates.TypePredicate;
|
||||
import com.google.errorprone.suppliers.Supplier;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
import com.sun.source.tree.MemberReferenceTree;
|
||||
import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.NewClassTree;
|
||||
import com.sun.source.util.JavacTask;
|
||||
import com.sun.tools.javac.api.BasicJavacTask;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.jvm.Target;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.Name;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags the same legacy APIs as the <a
|
||||
* href="https://github.com/gaul/modernizer-maven-plugin">Modernizer Maven Plugin</a>.
|
||||
*
|
||||
* <p>This checker is primarily useful for people who run Error Prone anyway; it obviates the need
|
||||
* for an additional source code analysis pass using another Maven plugin.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Avoid constants and methods superseded by more recent equivalents",
|
||||
link = BUG_PATTERNS_BASE_URL + "Modernizer",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = REFACTORING)
|
||||
public final class Modernizer extends BugChecker
|
||||
implements AnnotationTreeMatcher,
|
||||
IdentifierTreeMatcher,
|
||||
MemberReferenceTreeMatcher,
|
||||
MemberSelectTreeMatcher,
|
||||
NewClassTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// XXX: Load lazily?
|
||||
private final ImmutableTable<String, Matcher<ExpressionTree>, String> violations =
|
||||
loadViolations();
|
||||
|
||||
/** Instantiates a new {@link MockitoStubbing} instance. */
|
||||
public Modernizer() {}
|
||||
|
||||
@Override
|
||||
public Description matchAnnotation(AnnotationTree tree, VisitorState state) {
|
||||
// XXX: Use or drop
|
||||
// XXX: See
|
||||
// https://github.com/google/guava-beta-checker/commit/9b26aa980be7f70631921fd6695013547728eb1e;
|
||||
// we may be on the right track without this.
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description matchIdentifier(IdentifierTree tree, VisitorState state) {
|
||||
return match(tree.getName(), tree, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description matchMemberReference(MemberReferenceTree tree, VisitorState state) {
|
||||
return match(tree.getName(), tree, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description matchMemberSelect(MemberSelectTree tree, VisitorState state) {
|
||||
return match(tree.getIdentifier(), tree, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description matchNewClass(NewClassTree tree, VisitorState state) {
|
||||
Symbol createdType =
|
||||
requireNonNull(ASTHelpers.getSymbol(tree).getEnclosingElement(), "No enclosing class");
|
||||
return match(createdType.getQualifiedName(), tree, state);
|
||||
}
|
||||
|
||||
private Description match(Name identifier, ExpressionTree tree, VisitorState state) {
|
||||
return violations.row(identifier.toString()).entrySet().stream()
|
||||
.filter(e -> e.getKey().matches(tree, state))
|
||||
.findFirst()
|
||||
.map(e -> buildDescription(tree).setMessage(e.getValue()).build())
|
||||
.orElse(Description.NO_MATCH);
|
||||
}
|
||||
|
||||
private ImmutableTable<String, Matcher<ExpressionTree>, String> loadViolations() {
|
||||
InputStream resource = getClass().getResourceAsStream("/modernizer.xml");
|
||||
// XXX: Or silently skip?
|
||||
checkState(resource != null, "Modernizer configuration not found on classpath");
|
||||
|
||||
XmlMapper mapper = new XmlMapper();
|
||||
try (resource) {
|
||||
return mapper.readValue(resource, Violations.class).getViolation().stream()
|
||||
.filter(v -> v.getChecker().isPresent())
|
||||
.collect(
|
||||
toImmutableTable(
|
||||
Violation::getIdentifier,
|
||||
v -> v.getChecker().orElseThrow(),
|
||||
Violation::getComment));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to parse Modernizer configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Further simplify with Auto Value?
|
||||
@Immutable
|
||||
static final class Violations {
|
||||
@JacksonXmlElementWrapper(useWrapping = false)
|
||||
private final ImmutableList<Violation> violation;
|
||||
|
||||
@JsonCreator
|
||||
private Violations(@JsonProperty("violation") List<Violation> violation) {
|
||||
this.violation = ImmutableList.copyOf(violation);
|
||||
}
|
||||
|
||||
// XXX: Jackson relies on this naming and visibility. Ugh.
|
||||
public ImmutableList<Violation> getViolation() {
|
||||
return violation;
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@AutoValue
|
||||
abstract static class Violation {
|
||||
private static final Pattern NAME_PATTERN =
|
||||
Pattern.compile(
|
||||
"(?<type>[^.]+)(?:\\.(?<member>[^:]+):(?:\\((?<params>[^)]*)\\))?(?<return>[^()]+))?");
|
||||
|
||||
abstract Optional<Target> getTarget();
|
||||
|
||||
abstract String getIdentifier();
|
||||
|
||||
@CopyAnnotations
|
||||
@SuppressWarnings("Immutable")
|
||||
abstract Matcher<ExpressionTree> getMatcher();
|
||||
|
||||
abstract String getComment();
|
||||
|
||||
Optional<Matcher<ExpressionTree>> getChecker() {
|
||||
return getTarget().map(t -> allOf(getMatcher(), targetMatcher(t)));
|
||||
}
|
||||
|
||||
// XXX: Overkill? Not if we use auto value.
|
||||
// XXX: Modernizer also flags annotation declarations, presumably by type.
|
||||
// XXX: `ExpressionTree` is wrong here. Depends on type.
|
||||
@JsonCreator
|
||||
static Violation create(
|
||||
@JsonProperty("version") String version,
|
||||
@JsonProperty("name") String signature,
|
||||
@JsonProperty("comment") String comment) {
|
||||
Optional<Target> target = Optional.ofNullable(Target.lookup(version));
|
||||
|
||||
java.util.regex.Matcher matcher = NAME_PATTERN.matcher(signature);
|
||||
checkState(matcher.matches(), "Failed to parse signature '%s'", signature);
|
||||
|
||||
String type =
|
||||
replaceSlashes(requireNonNull(matcher.group("type"), "Signature must contain type"));
|
||||
|
||||
String member = matcher.group("member");
|
||||
if (member == null) {
|
||||
// XXX: Should not implement this interface. Something like:
|
||||
// violations.put(type, allOf(isSubtypeOf(type), versionRequirement), this.comment)
|
||||
return new AutoValue_Modernizer_Violation(target, type, (t, s) -> false, comment);
|
||||
}
|
||||
|
||||
String params = matcher.group("params");
|
||||
if (params == null) {
|
||||
return new AutoValue_Modernizer_Violation(target, member, isField(type), comment);
|
||||
}
|
||||
|
||||
ImmutableList<Supplier<Type>> parameters = parseParams(params);
|
||||
|
||||
if ("\"<init>\"".equals(member)) {
|
||||
return new AutoValue_Modernizer_Violation(
|
||||
target, type, isConstructor(type, parameters), comment);
|
||||
}
|
||||
|
||||
// XXX: Should we disallow _extension_ of this method?
|
||||
return new AutoValue_Modernizer_Violation(
|
||||
target, member, isMethod(type, parameters), comment);
|
||||
}
|
||||
|
||||
private static Matcher<ExpressionTree> targetMatcher(Target target) {
|
||||
return (tree, state) -> target.compareTo(getTargetVersion(state)) <= 0;
|
||||
}
|
||||
|
||||
private static Target getTargetVersion(VisitorState state) {
|
||||
return Target.instance(
|
||||
Optional.ofNullable(state.context.get(JavacTask.class))
|
||||
.filter(BasicJavacTask.class::isInstance)
|
||||
.map(BasicJavacTask.class::cast)
|
||||
.map(BasicJavacTask::getContext)
|
||||
.orElse(state.context));
|
||||
}
|
||||
|
||||
private static Matcher<ExpressionTree> isField(String onDescendantOf) {
|
||||
return isMember(ElementKind::isField, isDescendantOf(onDescendantOf), ImmutableList.of());
|
||||
}
|
||||
|
||||
private static Matcher<ExpressionTree> isConstructor(
|
||||
String ofClass, ImmutableList<Supplier<Type>> withParameters) {
|
||||
return isMember(k -> k == ElementKind.CONSTRUCTOR, isExactType(ofClass), withParameters);
|
||||
}
|
||||
|
||||
private static Matcher<ExpressionTree> isMethod(
|
||||
String onDescendantOf, ImmutableList<Supplier<Type>> withParameters) {
|
||||
return isMember(k -> k == ElementKind.METHOD, isDescendantOf(onDescendantOf), withParameters);
|
||||
}
|
||||
|
||||
private static Matcher<ExpressionTree> isMember(
|
||||
Predicate<ElementKind> ofKind,
|
||||
TypePredicate ownedBy,
|
||||
ImmutableList<Supplier<Type>> withParameters) {
|
||||
return (tree, state) ->
|
||||
Optional.ofNullable(ASTHelpers.getSymbol(tree))
|
||||
.filter(s -> ofKind.test(s.getKind()))
|
||||
.filter(s -> isOwnedBy(s, ownedBy, state))
|
||||
.filter(s -> hasSameParameters(s, withParameters, state))
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
private static boolean isOwnedBy(Symbol symbol, TypePredicate expected, VisitorState state) {
|
||||
Symbol owner = symbol.getEnclosingElement();
|
||||
return owner != null && expected.apply(owner.asType(), state);
|
||||
}
|
||||
|
||||
private static boolean hasSameParameters(
|
||||
Symbol method, ImmutableList<Supplier<Type>> expected, VisitorState state) {
|
||||
List<Type> actual = method.asType().getParameterTypes();
|
||||
if (actual.size() != expected.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < actual.size(); ++i) {
|
||||
if (!ASTHelpers.isSameType(actual.get(i), expected.get(i).get(state), state)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ImmutableList<Supplier<Type>> parseParams(String params) {
|
||||
ImmutableList.Builder<Supplier<Type>> types = ImmutableList.builder();
|
||||
|
||||
@Var int index = 0;
|
||||
while (index < params.length()) {
|
||||
index = parseType(params, index, types::add);
|
||||
}
|
||||
|
||||
return types.build();
|
||||
}
|
||||
|
||||
private static int parseType(String params, int index, Consumer<Supplier<Type>> sink) {
|
||||
return switch (params.charAt(index)) {
|
||||
case '[' -> parseArrayType(params, index, sink);
|
||||
case 'L' -> parseTypeReference(params, index, sink);
|
||||
default -> parsePrimitiveType(params, index, sink);
|
||||
};
|
||||
}
|
||||
|
||||
private static int parseArrayType(String params, int index, Consumer<Supplier<Type>> sink) {
|
||||
int typeIndex = index + 1;
|
||||
checkArgument(
|
||||
params.length() > typeIndex && params.charAt(index) == '[',
|
||||
"Cannot parse array type in parameter string '%s' at index %s",
|
||||
params,
|
||||
index);
|
||||
|
||||
return parseType(
|
||||
params,
|
||||
typeIndex,
|
||||
type ->
|
||||
sink.accept(s -> s.getType(type.get(s), /* isArray= */ true, ImmutableList.of())));
|
||||
}
|
||||
|
||||
private static int parsePrimitiveType(String params, int index, Consumer<Supplier<Type>> sink) {
|
||||
String primitive =
|
||||
Optional.of(params)
|
||||
.filter(p -> p.length() > index)
|
||||
.flatMap(p -> fromPrimitiveAlias(p.charAt(index)))
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format(
|
||||
"Cannot parse primitive type in parameter string '%s' at index %s",
|
||||
params, index)));
|
||||
sink.accept(s -> s.getTypeFromString(primitive));
|
||||
return index + 1;
|
||||
}
|
||||
|
||||
private static Optional<String> fromPrimitiveAlias(char alias) {
|
||||
return switch (alias) {
|
||||
case 'Z' -> Optional.of("boolean");
|
||||
case 'B' -> Optional.of("byte");
|
||||
case 'C' -> Optional.of("char");
|
||||
case 'S' -> Optional.of("short");
|
||||
case 'I' -> Optional.of("int");
|
||||
case 'J' -> Optional.of("long");
|
||||
case 'F' -> Optional.of("float");
|
||||
case 'D' -> Optional.of("double");
|
||||
default -> Optional.empty();
|
||||
};
|
||||
}
|
||||
|
||||
private static int parseTypeReference(String params, int index, Consumer<Supplier<Type>> sink) {
|
||||
int identifierIndex = index + 1;
|
||||
if (params.length() > identifierIndex && params.charAt(index) == 'L') {
|
||||
int delimiter = params.indexOf(';', identifierIndex);
|
||||
if (delimiter > index) {
|
||||
sink.accept(
|
||||
s ->
|
||||
s.getTypeFromString(
|
||||
replaceSlashes(params.substring(identifierIndex, delimiter))));
|
||||
return delimiter + 1;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Cannot parse reference type in parameter string '%s' at index %s", params, index));
|
||||
}
|
||||
|
||||
private static String replaceSlashes(String typeName) {
|
||||
return typeName.replace('/', '.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,14 @@ import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
import com.sun.source.tree.LiteralTree;
|
||||
import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import tech.picnic.errorprone.refaster.matchers.RequiresComputation;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
@@ -34,12 +36,12 @@ import tech.picnic.errorprone.utils.SourceCode;
|
||||
* it does, the suggested fix changes the program's semantics. Such fragile code must instead be
|
||||
* refactored such that the side-effectful code does not appear accidental.
|
||||
*/
|
||||
// XXX: This rule may introduce a compilation error: the `value` expression may reference a
|
||||
// non-effectively final variable, which is not allowed in the replacement lambda expression.
|
||||
// Review whether a `@Matcher` can be used to avoid this.
|
||||
// XXX: Once the `MethodReferenceUsageCheck` bug checker becomes generally usable, consider leaving
|
||||
// the method reference cleanup to that check, and express the remainder of the logic in this class
|
||||
// using a Refaster template, i.c.w. a `@NotMatches(RequiresComputation.class)` constraint.
|
||||
// XXX: Consider also implementing the inverse, in which `.orElseGet(() -> someConstant)` is
|
||||
// flagged.
|
||||
// XXX: Once the `MethodReferenceUsageCheck` becomes generally usable, consider leaving the method
|
||||
// reference cleanup to that check, and express the remainder of the logic in this class using a
|
||||
// Refaster template, i.c.w. a `@Matches` constraint that implements the `requiresComputation`
|
||||
// logic.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
@@ -49,17 +51,16 @@ import tech.picnic.errorprone.utils.SourceCode;
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = PERFORMANCE)
|
||||
public final class OptionalOrElseGet extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
public final class OptionalOrElse extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> REQUIRES_COMPUTATION = new RequiresComputation();
|
||||
private static final Matcher<ExpressionTree> OPTIONAL_OR_ELSE_METHOD =
|
||||
instanceMethod().onExactClass(Optional.class.getCanonicalName()).namedAnyOf("orElse");
|
||||
// XXX: Also exclude invocations of `@Placeholder`-annotated methods.
|
||||
private static final Matcher<ExpressionTree> REFASTER_METHOD =
|
||||
staticMethod().onClass(Refaster.class.getCanonicalName());
|
||||
|
||||
/** Instantiates a new {@link OptionalOrElseGet} instance. */
|
||||
public OptionalOrElseGet() {}
|
||||
/** Instantiates a new {@link OptionalOrElse} instance. */
|
||||
public OptionalOrElse() {}
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
@@ -68,8 +69,7 @@ public final class OptionalOrElseGet extends BugChecker implements MethodInvocat
|
||||
}
|
||||
|
||||
ExpressionTree argument = Iterables.getOnlyElement(tree.getArguments());
|
||||
if (!REQUIRES_COMPUTATION.matches(argument, state)
|
||||
|| REFASTER_METHOD.matches(argument, state)) {
|
||||
if (!requiresComputation(argument) || REFASTER_METHOD.matches(argument, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
@@ -91,6 +91,18 @@ public final class OptionalOrElseGet extends BugChecker implements MethodInvocat
|
||||
return describeMatch(tree, fix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the given expression contains anything other than a literal or a (possibly
|
||||
* dereferenced) variable or constant.
|
||||
*/
|
||||
private static boolean requiresComputation(ExpressionTree tree) {
|
||||
return !(tree instanceof IdentifierTree
|
||||
|| tree instanceof LiteralTree
|
||||
|| (tree instanceof MemberSelectTree memberSelect
|
||||
&& !requiresComputation(memberSelect.getExpression()))
|
||||
|| ASTHelpers.constValue(tree) != null);
|
||||
}
|
||||
|
||||
/** Returns the nullary method reference matching the given expression, if any. */
|
||||
private static Optional<String> tryMethodReferenceConversion(
|
||||
ExpressionTree tree, VisitorState state) {
|
||||
@@ -106,7 +118,7 @@ public final class OptionalOrElseGet extends BugChecker implements MethodInvocat
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (REQUIRES_COMPUTATION.matches(memberSelect.getExpression(), state)) {
|
||||
if (requiresComputation(memberSelect.getExpression())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -378,11 +378,11 @@ public final class RedundantStringConversion extends BugChecker
|
||||
|
||||
private static Matcher<MethodInvocationTree> createConversionMethodMatcher(
|
||||
ErrorProneFlags flags) {
|
||||
// XXX: `Flags#getSet` splits by comma, but method signatures may also contain commas. For this
|
||||
// class methods accepting more than one argument are not valid, but still: not nice.
|
||||
// XXX: ErrorProneFlags#getList splits by comma, but method signatures may also contain commas.
|
||||
// For this class methods accepting more than one argument are not valid, but still: not nice.
|
||||
return anyOf(
|
||||
WELL_KNOWN_STRING_CONVERSION_METHODS,
|
||||
new MethodMatcherFactory()
|
||||
.create(Flags.getSet(flags, EXTRA_STRING_CONVERSION_METHODS_FLAG)));
|
||||
.create(Flags.getList(flags, EXTRA_STRING_CONVERSION_METHODS_FLAG)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.LiteralTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.LiteralTree;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} that flags string constants with extraneous escaping. */
|
||||
// XXX: Also cover `\"` sequences inside text blocks. Note that this requires a more subtle
|
||||
// approach, as double-quote characters will need to remain escaped if removing the backslash would
|
||||
// create a new sequence of three or more double-quotes. (TBD whether we'd like to enforce a
|
||||
// "preferred" approach to escaping, e.g. by always escaping the last of a triplet, such that the
|
||||
// over-all number of escaped characters is minimized.)
|
||||
// XXX: Also flag `'\"'` char literals.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Inside string expressions single quotes do not need to be escaped",
|
||||
link = BUG_PATTERNS_BASE_URL + "RedundantStringEscape",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class RedundantStringEscape extends BugChecker implements LiteralTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Instantiates a new {@link RedundantStringEscape} instance. */
|
||||
public RedundantStringEscape() {}
|
||||
|
||||
@Override
|
||||
public Description matchLiteral(LiteralTree tree, VisitorState state) {
|
||||
String constant = ASTHelpers.constValue(tree, String.class);
|
||||
if (constant == null || constant.indexOf('\'') < 0) {
|
||||
/* Fast path: this isn't a string constant with a single quote. */
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
String source = SourceCode.treeToString(tree, state);
|
||||
if (!containsBannedEscapeSequence(source)) {
|
||||
/* Semi-fast path: this expression doesn't contain an escaped single quote. */
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
/* Slow path: suggest dropping the escape characters. */
|
||||
return describeMatch(tree, SuggestedFix.replace(tree, dropRedundantEscapeSequences(source)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the given string constant source expression contains an escaped single quote.
|
||||
*
|
||||
* @implNote As the input is a literal Java string expression, it will start and end with a double
|
||||
* quote; as such any found backslash will not be the string's final character.
|
||||
*/
|
||||
private static boolean containsBannedEscapeSequence(String source) {
|
||||
for (int p = source.indexOf('\\'); p != -1; p = source.indexOf('\\', p + 2)) {
|
||||
if (source.charAt(p + 1) == '\'') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplifies the given string constant source expression by dropping the backslash preceding an
|
||||
* escaped single quote.
|
||||
*
|
||||
* @implNote Note that this method does not delegate to {@link
|
||||
* SourceCode#toStringConstantExpression}, as that operation may replace other Unicode
|
||||
* characters with their associated escape sequence.
|
||||
* @implNote As the input is a literal Java string expression, it will start and end with a double
|
||||
* quote; as such any found backslash will not be the string's final character.
|
||||
*/
|
||||
private static String dropRedundantEscapeSequences(String source) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
for (int p = 0; p < source.length(); p++) {
|
||||
char c = source.charAt(p);
|
||||
if (c != '\\' || source.charAt(p + 1) != '\'') {
|
||||
result.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.ErrorProneFlags;
|
||||
import com.google.errorprone.VisitorState;
|
||||
@@ -74,10 +74,10 @@ public final class RequestParamType extends BugChecker implements VariableTreeMa
|
||||
return allOf(
|
||||
annotations(AT_LEAST_ONE, isType("org.springframework.web.bind.annotation.RequestParam")),
|
||||
anyOf(isSubtypeOf(ImmutableCollection.class), isSubtypeOf(ImmutableMap.class)),
|
||||
not(isSubtypeOfAny(Flags.getSet(flags, SUPPORTED_CUSTOM_TYPES_FLAG))));
|
||||
not(isSubtypeOfAny(Flags.getList(flags, SUPPORTED_CUSTOM_TYPES_FLAG))));
|
||||
}
|
||||
|
||||
private static Matcher<Tree> isSubtypeOfAny(ImmutableSet<String> inclusions) {
|
||||
private static Matcher<Tree> isSubtypeOfAny(ImmutableList<String> inclusions) {
|
||||
return anyOf(
|
||||
inclusions.stream()
|
||||
.map(inclusion -> isSubtypeOf(Suppliers.typeFromString(inclusion)))
|
||||
|
||||
@@ -41,7 +41,7 @@ import tech.picnic.errorprone.utils.SourceCode;
|
||||
tags = LIKELY_ERROR)
|
||||
public final class Slf4jLogStatement extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> SLF4J_MARKER = isSubtypeOf("org.slf4j.Marker");
|
||||
private static final Matcher<ExpressionTree> MARKER = isSubtypeOf("org.slf4j.Marker");
|
||||
private static final Matcher<ExpressionTree> THROWABLE = isSubtypeOf(Throwable.class);
|
||||
private static final Matcher<ExpressionTree> SLF4J_LOGGER_INVOCATION =
|
||||
instanceMethod()
|
||||
@@ -71,7 +71,7 @@ public final class Slf4jLogStatement extends BugChecker implements MethodInvocat
|
||||
* SLF4J log statements may accept a "marker" as a first argument, before the format string.
|
||||
* We ignore such markers.
|
||||
*/
|
||||
int lTrim = SLF4J_MARKER.matches(args.get(0), state) ? 1 : 0;
|
||||
int lTrim = MARKER.matches(args.get(0), state) ? 1 : 0;
|
||||
/*
|
||||
* SLF4J treats the final argument to a log statement specially if it is a `Throwabe`: it
|
||||
* will always choose to render the associated stacktrace, even if the argument has a
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.common.base.Verify.verify;
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.STYLE;
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
import static com.google.errorprone.matchers.Matchers.classLiteral;
|
||||
import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.staticMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.toType;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.CaseFormat;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.ErrorProneFlags;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.VariableTreeMatcher;
|
||||
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.util.ASTHelpers;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.ModifiersTree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import com.sun.source.tree.VariableTree;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import java.util.EnumSet;
|
||||
import javax.inject.Inject;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import tech.picnic.errorprone.utils.MoreASTHelpers;
|
||||
|
||||
/** A {@link BugChecker} that flags non-canonical SLF4J logger declarations. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "SLF4J logger declarations should follow established best-practices",
|
||||
link = BUG_PATTERNS_BASE_URL + "Slf4jLoggerDeclaration",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = STYLE)
|
||||
@SuppressWarnings("java:S2160" /* Super class equality definition suffices. */)
|
||||
public final class Slf4jLoggerDeclaration extends BugChecker implements VariableTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> IS_GET_LOGGER =
|
||||
staticMethod().onDescendantOf("org.slf4j.LoggerFactory").named("getLogger");
|
||||
private static final String CANONICAL_STATIC_LOGGER_NAME_FLAG =
|
||||
"Slf4jLoggerDeclaration:CanonicalStaticLoggerName";
|
||||
private static final String DEFAULT_CANONICAL_LOGGER_NAME = "LOG";
|
||||
private static final Matcher<ExpressionTree> IS_STATIC_ENCLOSING_CLASS_REFERENCE =
|
||||
classLiteral(Slf4jLoggerDeclaration::isEnclosingClassReference);
|
||||
private static final Matcher<ExpressionTree> IS_DYNAMIC_ENCLOSING_CLASS_REFERENCE =
|
||||
toType(
|
||||
MethodInvocationTree.class,
|
||||
allOf(
|
||||
instanceMethod().anyClass().named("getClass").withNoParameters(),
|
||||
Slf4jLoggerDeclaration::getClassReceiverIsEnclosingClassInstance));
|
||||
private static final ImmutableSet<Modifier> INSTANCE_DECLARATION_MODIFIERS =
|
||||
Sets.immutableEnumSet(Modifier.PRIVATE, Modifier.FINAL);
|
||||
private static final ImmutableSet<Modifier> STATIC_DECLARATION_MODIFIERS =
|
||||
Sets.immutableEnumSet(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL);
|
||||
|
||||
private final String canonicalStaticFieldName;
|
||||
private final String canonicalInstanceFieldName;
|
||||
|
||||
/** Instantiates a default {@link Slf4jLoggerDeclaration} instance. */
|
||||
public Slf4jLoggerDeclaration() {
|
||||
this(ErrorProneFlags.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a customized {@link Slf4jLoggerDeclaration}.
|
||||
*
|
||||
* @param flags Any provided command line flags.
|
||||
*/
|
||||
@Inject
|
||||
Slf4jLoggerDeclaration(ErrorProneFlags flags) {
|
||||
canonicalStaticFieldName =
|
||||
flags.get(CANONICAL_STATIC_LOGGER_NAME_FLAG).orElse(DEFAULT_CANONICAL_LOGGER_NAME);
|
||||
canonicalInstanceFieldName =
|
||||
CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, canonicalStaticFieldName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description matchVariable(VariableTree tree, VisitorState state) {
|
||||
ExpressionTree initializer = tree.getInitializer();
|
||||
if (!IS_GET_LOGGER.matches(initializer, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
ClassTree clazz = getEnclosingClass(state);
|
||||
ExpressionTree factoryArg =
|
||||
Iterables.getOnlyElement(((MethodInvocationTree) initializer).getArguments());
|
||||
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder();
|
||||
|
||||
if (clazz.getModifiers().getFlags().contains(Modifier.ABSTRACT)
|
||||
&& IS_DYNAMIC_ENCLOSING_CLASS_REFERENCE.matches(factoryArg, state)) {
|
||||
/*
|
||||
* While generally we prefer `Logger` declarations to be static and named after their
|
||||
* enclosing class, we allow one exception: loggers in abstract classes with a name derived
|
||||
* from `getClass()`.
|
||||
*/
|
||||
suggestModifiers(tree, INSTANCE_DECLARATION_MODIFIERS, fix, state);
|
||||
suggestRename(tree, canonicalInstanceFieldName, fix, state);
|
||||
} else {
|
||||
suggestModifiers(
|
||||
tree,
|
||||
clazz.getKind() == Kind.INTERFACE ? ImmutableSet.of() : STATIC_DECLARATION_MODIFIERS,
|
||||
fix,
|
||||
state);
|
||||
suggestRename(tree, canonicalStaticFieldName, fix, state);
|
||||
|
||||
if (!MoreASTHelpers.isStringTyped(factoryArg, state)
|
||||
&& !IS_STATIC_ENCLOSING_CLASS_REFERENCE.matches(factoryArg, state)) {
|
||||
/*
|
||||
* Loggers with a custom string name are generally "special", but those with a name derived
|
||||
* from a class other than the one that encloses it are likely in error.
|
||||
*/
|
||||
fix.merge(SuggestedFix.replace(factoryArg, clazz.getSimpleName() + ".class"));
|
||||
}
|
||||
}
|
||||
|
||||
return fix.isEmpty() ? Description.NO_MATCH : describeMatch(tree, fix.build());
|
||||
}
|
||||
|
||||
private static void suggestModifiers(
|
||||
VariableTree tree,
|
||||
ImmutableSet<Modifier> modifiers,
|
||||
SuggestedFix.Builder fixBuilder,
|
||||
VisitorState state) {
|
||||
ModifiersTree modifiersTree =
|
||||
requireNonNull(ASTHelpers.getModifiers(tree), "`VariableTree` must have modifiers");
|
||||
SuggestedFixes.addModifiers(tree, modifiersTree, state, modifiers).ifPresent(fixBuilder::merge);
|
||||
SuggestedFixes.removeModifiers(
|
||||
modifiersTree, state, Sets.difference(EnumSet.allOf(Modifier.class), modifiers))
|
||||
.ifPresent(fixBuilder::merge);
|
||||
}
|
||||
|
||||
private static void suggestRename(
|
||||
VariableTree variableTree, String name, SuggestedFix.Builder fixBuilder, VisitorState state) {
|
||||
if (!variableTree.getName().contentEquals(name)) {
|
||||
fixBuilder.merge(SuggestedFixes.renameVariable(variableTree, name, state));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isEnclosingClassReference(ExpressionTree tree, VisitorState state) {
|
||||
return ASTHelpers.getSymbol(getEnclosingClass(state)).equals(ASTHelpers.getSymbol(tree));
|
||||
}
|
||||
|
||||
private static boolean getClassReceiverIsEnclosingClassInstance(
|
||||
MethodInvocationTree getClassInvocationTree, VisitorState state) {
|
||||
ExpressionTree receiver = ASTHelpers.getReceiver(getClassInvocationTree);
|
||||
if (receiver == null) {
|
||||
/*
|
||||
* Method invocations without an explicit receiver either involve static methods (possibly
|
||||
* statically imported), or instance methods invoked on the enclosing class. As the given
|
||||
* `getClassInvocationTree` is guaranteed to be a nullary `#getClass()` invocation, the latter
|
||||
* must be the case.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
Symbol symbol = ASTHelpers.getSymbol(receiver);
|
||||
return symbol != null
|
||||
&& symbol.asType().tsym.equals(ASTHelpers.getSymbol(getEnclosingClass(state)));
|
||||
}
|
||||
|
||||
private static ClassTree getEnclosingClass(VisitorState state) {
|
||||
ClassTree clazz = state.findEnclosing(ClassTree.class);
|
||||
// XXX: Review whether we should relax this constraint in the face of so-called anonymous
|
||||
// classes. See
|
||||
// https://docs.oracle.com/en/java/javase/23/language/implicitly-declared-classes-and-instance-main-methods.html
|
||||
verify(clazz != null, "Variable not defined inside class");
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.util.Constants;
|
||||
import java.util.Formattable;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -36,9 +37,7 @@ import tech.picnic.errorprone.utils.SourceCode;
|
||||
*/
|
||||
// XXX: What about `v1 + "sep" + v2` and similar expressions? Do we want to rewrite those to
|
||||
// `String.join`, or should some `String.join` invocations be rewritten to use the `+` operator?
|
||||
// (The latter suggestion would conflict with the `FormatStringConcatenation` check, but does make
|
||||
// more sense when `"sep"` is a long string. Similarly for `String.format("%s some long text %s",
|
||||
// arg1, arg2)`.)
|
||||
// (The latter suggestion would conflict with the `FormatStringConcatenation` check.)
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Prefer `String#join` over `String#format`",
|
||||
@@ -151,7 +150,7 @@ public final class StringJoin extends BugChecker implements MethodInvocationTree
|
||||
SuggestedFix.Builder fix =
|
||||
SuggestedFix.builder()
|
||||
.replace(tree.getMethodSelect(), "String.join")
|
||||
.replace(arguments.next(), SourceCode.toStringConstantExpression(separator, state));
|
||||
.replace(arguments.next(), Constants.format(separator));
|
||||
|
||||
while (arguments.hasNext()) {
|
||||
ExpressionTree argument = arguments.next();
|
||||
|
||||
@@ -3,6 +3,9 @@ package tech.picnic.errorprone.refasterrules;
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMultiset;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
@@ -12,6 +15,7 @@ import com.google.errorprone.refaster.annotation.Matches;
|
||||
import com.google.errorprone.refaster.annotation.NotMatches;
|
||||
import com.google.errorprone.refaster.annotation.Repeated;
|
||||
import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -200,8 +204,32 @@ final class AssertJRules {
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T element) {
|
||||
return Refaster.anyOf(
|
||||
iterAssert.containsAnyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(element),
|
||||
Arrays.asList(element),
|
||||
ImmutableSet.of(element),
|
||||
ImmutableMultiset.of(element))),
|
||||
iterAssert.containsAnyOf(element),
|
||||
iterAssert.containsAll(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(element),
|
||||
Arrays.asList(element),
|
||||
ImmutableSet.of(element),
|
||||
ImmutableMultiset.of(element))),
|
||||
iterAssert.containsSequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(element),
|
||||
Arrays.asList(element),
|
||||
ImmutableSet.of(element),
|
||||
ImmutableMultiset.of(element))),
|
||||
iterAssert.containsSequence(element),
|
||||
iterAssert.containsSubsequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(element),
|
||||
Arrays.asList(element),
|
||||
ImmutableSet.of(element),
|
||||
ImmutableMultiset.of(element))),
|
||||
iterAssert.containsSubsequence(element));
|
||||
}
|
||||
|
||||
@@ -216,7 +244,20 @@ final class AssertJRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T element) {
|
||||
return iterAssert.doesNotContainSequence(element);
|
||||
return Refaster.anyOf(
|
||||
iterAssert.doesNotContainAnyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(element),
|
||||
Arrays.asList(element),
|
||||
ImmutableSet.of(element),
|
||||
ImmutableMultiset.of(element))),
|
||||
iterAssert.doesNotContainSequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(element),
|
||||
Arrays.asList(element),
|
||||
ImmutableSet.of(element),
|
||||
ImmutableMultiset.of(element))),
|
||||
iterAssert.doesNotContainSequence(element));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@@ -229,7 +270,25 @@ final class AssertJRules {
|
||||
static final class ObjectEnumerableContainsExactlyOneElement<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T element) {
|
||||
return Refaster.anyOf(
|
||||
iterAssert.containsExactlyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(element),
|
||||
Arrays.asList(element),
|
||||
ImmutableSet.of(element),
|
||||
ImmutableMultiset.of(element))),
|
||||
iterAssert.containsExactlyInAnyOrderElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(element),
|
||||
Arrays.asList(element),
|
||||
ImmutableSet.of(element),
|
||||
ImmutableMultiset.of(element))));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> before2(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, @NotMatches(IsArray.class) T element) {
|
||||
return iterAssert.containsExactlyInAnyOrder(element);
|
||||
}
|
||||
@@ -254,6 +313,42 @@ final class AssertJRules {
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsOneDistinctElement<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T element) {
|
||||
return iterAssert.hasSameElementsAs(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(element),
|
||||
Arrays.asList(element),
|
||||
ImmutableSet.of(element),
|
||||
ImmutableMultiset.of(element)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T element) {
|
||||
return iterAssert.containsOnly(element);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableIsSubsetOfOneElement<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T element) {
|
||||
return iterAssert.isSubsetOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(element),
|
||||
Arrays.asList(element),
|
||||
ImmutableSet.of(element),
|
||||
ImmutableMultiset.of(element)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T element) {
|
||||
return iterAssert.isSubsetOf(element);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Iterable
|
||||
//
|
||||
@@ -264,7 +359,6 @@ final class AssertJRules {
|
||||
Refaster.anyOf(
|
||||
assertThat(iterable).hasSize(0),
|
||||
assertThat(iterable.iterator().hasNext()).isFalse(),
|
||||
assertThat(iterable.iterator()).isExhausted(),
|
||||
assertThat(Iterables.size(iterable)).isEqualTo(0L),
|
||||
assertThat(Iterables.size(iterable)).isNotPositive());
|
||||
}
|
||||
@@ -1061,6 +1155,824 @@ final class AssertJRules {
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// BELOW: Generated code.
|
||||
|
||||
//
|
||||
// ObjectEnumerableAssert: containsAnyOf
|
||||
//
|
||||
|
||||
static final class ObjectEnumerableContainsAnyOfTwoElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.containsAnyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2),
|
||||
Arrays.asList(e1, e2),
|
||||
ImmutableSet.of(e1, e2),
|
||||
ImmutableMultiset.of(e1, e2)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.containsAnyOf(e1, e2);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsAnyOfThreeElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.containsAnyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3),
|
||||
Arrays.asList(e1, e2, e3),
|
||||
ImmutableSet.of(e1, e2, e3),
|
||||
ImmutableMultiset.of(e1, e2, e3)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.containsAnyOf(e1, e2, e3);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsAnyOfFourElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.containsAnyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4),
|
||||
Arrays.asList(e1, e2, e3, e4),
|
||||
ImmutableSet.of(e1, e2, e3, e4),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.containsAnyOf(e1, e2, e3, e4);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Add variants for 6+ elements?
|
||||
static final class ObjectEnumerableContainsAnyOfFiveElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.containsAnyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4, e5),
|
||||
Arrays.asList(e1, e2, e3, e4, e5),
|
||||
ImmutableSet.of(e1, e2, e3, e4, e5),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4, e5)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.containsAnyOf(e1, e2, e3, e4, e5);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ObjectEnumerableAssert: contains
|
||||
//
|
||||
|
||||
static final class ObjectEnumerableContainsTwoElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.containsAll(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2),
|
||||
Arrays.asList(e1, e2),
|
||||
ImmutableSet.of(e1, e2),
|
||||
ImmutableMultiset.of(e1, e2)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.contains(e1, e2);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsThreeElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.containsAll(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3),
|
||||
Arrays.asList(e1, e2, e3),
|
||||
ImmutableSet.of(e1, e2, e3),
|
||||
ImmutableMultiset.of(e1, e2, e3)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.contains(e1, e2, e3);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsFourElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.containsAll(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4),
|
||||
Arrays.asList(e1, e2, e3, e4),
|
||||
ImmutableSet.of(e1, e2, e3, e4),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.contains(e1, e2, e3, e4);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Add variants for 6+ elements?
|
||||
static final class ObjectEnumerableContainsFiveElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.containsAll(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4, e5),
|
||||
Arrays.asList(e1, e2, e3, e4, e5),
|
||||
ImmutableSet.of(e1, e2, e3, e4, e5),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4, e5)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.contains(e1, e2, e3, e4, e5);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ObjectEnumerableAssert: containsExactly
|
||||
//
|
||||
|
||||
static final class ObjectEnumerableContainsExactlyTwoElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.containsExactlyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2),
|
||||
Arrays.asList(e1, e2),
|
||||
ImmutableSet.of(e1, e2),
|
||||
ImmutableMultiset.of(e1, e2)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.containsExactly(e1, e2);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsExactlyThreeElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.containsExactlyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3),
|
||||
Arrays.asList(e1, e2, e3),
|
||||
ImmutableSet.of(e1, e2, e3),
|
||||
ImmutableMultiset.of(e1, e2, e3)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.containsExactly(e1, e2, e3);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsExactlyFourElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.containsExactlyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4),
|
||||
Arrays.asList(e1, e2, e3, e4),
|
||||
ImmutableSet.of(e1, e2, e3, e4),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.containsExactly(e1, e2, e3, e4);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Add variants for 6+ elements?
|
||||
static final class ObjectEnumerableContainsExactlyFiveElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.containsExactlyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4, e5),
|
||||
Arrays.asList(e1, e2, e3, e4, e5),
|
||||
ImmutableSet.of(e1, e2, e3, e4, e5),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4, e5)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.containsExactly(e1, e2, e3, e4, e5);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ObjectEnumerableAssert: containsExactlyInAnyOrder
|
||||
//
|
||||
|
||||
static final class ObjectEnumerableContainsExactlyInAnyOrderTwoElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.containsExactlyInAnyOrderElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2),
|
||||
Arrays.asList(e1, e2),
|
||||
ImmutableSet.of(e1, e2),
|
||||
ImmutableMultiset.of(e1, e2)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.containsExactlyInAnyOrder(e1, e2);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsExactlyInAnyOrderThreeElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.containsExactlyInAnyOrderElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3),
|
||||
Arrays.asList(e1, e2, e3),
|
||||
ImmutableSet.of(e1, e2, e3),
|
||||
ImmutableMultiset.of(e1, e2, e3)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.containsExactlyInAnyOrder(e1, e2, e3);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsExactlyInAnyOrderFourElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.containsExactlyInAnyOrderElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4),
|
||||
Arrays.asList(e1, e2, e3, e4),
|
||||
ImmutableSet.of(e1, e2, e3, e4),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.containsExactlyInAnyOrder(e1, e2, e3, e4);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Add variants for 6+ elements?
|
||||
static final class ObjectEnumerableContainsExactlyInAnyOrderFiveElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.containsExactlyInAnyOrderElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4, e5),
|
||||
Arrays.asList(e1, e2, e3, e4, e5),
|
||||
ImmutableSet.of(e1, e2, e3, e4, e5),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4, e5)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.containsExactlyInAnyOrder(e1, e2, e3, e4, e5);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ObjectEnumerableAssert: containsSequence
|
||||
//
|
||||
|
||||
static final class ObjectEnumerableContainsSequenceTwoElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.containsSequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2),
|
||||
Arrays.asList(e1, e2),
|
||||
ImmutableSet.of(e1, e2),
|
||||
ImmutableMultiset.of(e1, e2)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.containsSequence(e1, e2);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsSequenceThreeElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.containsSequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3),
|
||||
Arrays.asList(e1, e2, e3),
|
||||
ImmutableSet.of(e1, e2, e3),
|
||||
ImmutableMultiset.of(e1, e2, e3)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.containsSequence(e1, e2, e3);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsSequenceFourElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.containsSequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4),
|
||||
Arrays.asList(e1, e2, e3, e4),
|
||||
ImmutableSet.of(e1, e2, e3, e4),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.containsSequence(e1, e2, e3, e4);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Add variants for 6+ elements?
|
||||
static final class ObjectEnumerableContainsSequenceFiveElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.containsSequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4, e5),
|
||||
Arrays.asList(e1, e2, e3, e4, e5),
|
||||
ImmutableSet.of(e1, e2, e3, e4, e5),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4, e5)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.containsSequence(e1, e2, e3, e4, e5);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ObjectEnumerableAssert: containsSubsequence
|
||||
//
|
||||
|
||||
static final class ObjectEnumerableContainsSubsequenceTwoElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.containsSubsequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2),
|
||||
Arrays.asList(e1, e2),
|
||||
ImmutableSet.of(e1, e2),
|
||||
ImmutableMultiset.of(e1, e2)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.containsSubsequence(e1, e2);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsSubsequenceThreeElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.containsSubsequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3),
|
||||
Arrays.asList(e1, e2, e3),
|
||||
ImmutableSet.of(e1, e2, e3),
|
||||
ImmutableMultiset.of(e1, e2, e3)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.containsSubsequence(e1, e2, e3);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsSubsequenceFourElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.containsSubsequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4),
|
||||
Arrays.asList(e1, e2, e3, e4),
|
||||
ImmutableSet.of(e1, e2, e3, e4),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.containsSubsequence(e1, e2, e3, e4);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Add variants for 6+ elements?
|
||||
static final class ObjectEnumerableContainsSubsequenceFiveElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.containsSubsequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4, e5),
|
||||
Arrays.asList(e1, e2, e3, e4, e5),
|
||||
ImmutableSet.of(e1, e2, e3, e4, e5),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4, e5)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.containsSubsequence(e1, e2, e3, e4, e5);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ObjectEnumerableAssert: doesNotContain
|
||||
//
|
||||
|
||||
static final class ObjectEnumerableDoesNotContainTwoElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.doesNotContainAnyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2),
|
||||
Arrays.asList(e1, e2),
|
||||
ImmutableSet.of(e1, e2),
|
||||
ImmutableMultiset.of(e1, e2)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.doesNotContain(e1, e2);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableDoesNotContainThreeElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.doesNotContainAnyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3),
|
||||
Arrays.asList(e1, e2, e3),
|
||||
ImmutableSet.of(e1, e2, e3),
|
||||
ImmutableMultiset.of(e1, e2, e3)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.doesNotContain(e1, e2, e3);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableDoesNotContainFourElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.doesNotContainAnyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4),
|
||||
Arrays.asList(e1, e2, e3, e4),
|
||||
ImmutableSet.of(e1, e2, e3, e4),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.doesNotContain(e1, e2, e3, e4);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Add variants for 6+ elements?
|
||||
static final class ObjectEnumerableDoesNotContainFiveElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.doesNotContainAnyElementsOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4, e5),
|
||||
Arrays.asList(e1, e2, e3, e4, e5),
|
||||
ImmutableSet.of(e1, e2, e3, e4, e5),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4, e5)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.doesNotContain(e1, e2, e3, e4, e5);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ObjectEnumerableAssert: doesNotContainSequence
|
||||
//
|
||||
|
||||
static final class ObjectEnumerableDoesNotContainSequenceTwoElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.doesNotContainSequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2),
|
||||
Arrays.asList(e1, e2),
|
||||
ImmutableSet.of(e1, e2),
|
||||
ImmutableMultiset.of(e1, e2)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.doesNotContainSequence(e1, e2);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableDoesNotContainSequenceThreeElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.doesNotContainSequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3),
|
||||
Arrays.asList(e1, e2, e3),
|
||||
ImmutableSet.of(e1, e2, e3),
|
||||
ImmutableMultiset.of(e1, e2, e3)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.doesNotContainSequence(e1, e2, e3);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableDoesNotContainSequenceFourElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.doesNotContainSequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4),
|
||||
Arrays.asList(e1, e2, e3, e4),
|
||||
ImmutableSet.of(e1, e2, e3, e4),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.doesNotContainSequence(e1, e2, e3, e4);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Add variants for 6+ elements?
|
||||
static final class ObjectEnumerableDoesNotContainSequenceFiveElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.doesNotContainSequence(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4, e5),
|
||||
Arrays.asList(e1, e2, e3, e4, e5),
|
||||
ImmutableSet.of(e1, e2, e3, e4, e5),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4, e5)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.doesNotContainSequence(e1, e2, e3, e4, e5);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ObjectEnumerableAssert: containsOnly
|
||||
//
|
||||
|
||||
static final class ObjectEnumerableContainsOnlyTwoElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.hasSameElementsAs(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2),
|
||||
Arrays.asList(e1, e2),
|
||||
ImmutableSet.of(e1, e2),
|
||||
ImmutableMultiset.of(e1, e2)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.containsOnly(e1, e2);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsOnlyThreeElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.hasSameElementsAs(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3),
|
||||
Arrays.asList(e1, e2, e3),
|
||||
ImmutableSet.of(e1, e2, e3),
|
||||
ImmutableMultiset.of(e1, e2, e3)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.containsOnly(e1, e2, e3);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableContainsOnlyFourElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.hasSameElementsAs(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4),
|
||||
Arrays.asList(e1, e2, e3, e4),
|
||||
ImmutableSet.of(e1, e2, e3, e4),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.containsOnly(e1, e2, e3, e4);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Add variants for 6+ elements?
|
||||
static final class ObjectEnumerableContainsOnlyFiveElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.hasSameElementsAs(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4, e5),
|
||||
Arrays.asList(e1, e2, e3, e4, e5),
|
||||
ImmutableSet.of(e1, e2, e3, e4, e5),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4, e5)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.containsOnly(e1, e2, e3, e4, e5);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ObjectEnumerableAssert: isSubsetOf
|
||||
//
|
||||
|
||||
static final class ObjectEnumerableIsSubsetOfTwoElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.isSubsetOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2),
|
||||
Arrays.asList(e1, e2),
|
||||
ImmutableSet.of(e1, e2),
|
||||
ImmutableMultiset.of(e1, e2)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2) {
|
||||
return iterAssert.isSubsetOf(e1, e2);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableIsSubsetOfThreeElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.isSubsetOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3),
|
||||
Arrays.asList(e1, e2, e3),
|
||||
ImmutableSet.of(e1, e2, e3),
|
||||
ImmutableMultiset.of(e1, e2, e3)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3) {
|
||||
return iterAssert.isSubsetOf(e1, e2, e3);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ObjectEnumerableIsSubsetOfFourElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.isSubsetOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4),
|
||||
Arrays.asList(e1, e2, e3, e4),
|
||||
ImmutableSet.of(e1, e2, e3, e4),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4) {
|
||||
return iterAssert.isSubsetOf(e1, e2, e3, e4);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Add variants for 6+ elements?
|
||||
static final class ObjectEnumerableIsSubsetOfFiveElements<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
ObjectEnumerableAssert<?, S> before(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.isSubsetOf(
|
||||
Refaster.anyOf(
|
||||
ImmutableList.of(e1, e2, e3, e4, e5),
|
||||
Arrays.asList(e1, e2, e3, e4, e5),
|
||||
ImmutableSet.of(e1, e2, e3, e4, e5),
|
||||
ImmutableMultiset.of(e1, e2, e3, e4, e5)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectEnumerableAssert<?, S> after(
|
||||
ObjectEnumerableAssert<?, S> iterAssert, T e1, T e2, T e3, T e4, T e5) {
|
||||
return iterAssert.isSubsetOf(e1, e2, e3, e4, e5);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Above: Generated code.
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Organize the code below.
|
||||
|
||||
@@ -12,7 +12,6 @@ import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import org.assertj.core.api.AbstractBooleanAssert;
|
||||
import org.assertj.core.api.AbstractStringAssert;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
|
||||
@@ -70,32 +69,6 @@ final class AssertJStringRules {
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatStringContains {
|
||||
@BeforeTemplate
|
||||
AbstractBooleanAssert<?> before(String string, String substring) {
|
||||
return assertThat(string.contains(substring)).isTrue();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractStringAssert<?> after(String string, String substring) {
|
||||
return assertThat(string).contains(substring);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatStringDoesNotContain {
|
||||
@BeforeTemplate
|
||||
AbstractBooleanAssert<?> before(String string, String substring) {
|
||||
return assertThat(string.contains(substring)).isFalse();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractStringAssert<?> after(String string, String substring) {
|
||||
return assertThat(string).doesNotContain(substring);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatMatches {
|
||||
@BeforeTemplate
|
||||
AbstractAssert<?, ?> before(String string, String regex) {
|
||||
|
||||
@@ -7,10 +7,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.InstanceOfAssertFactories.throwable;
|
||||
import static org.assertj.core.api.InstanceOfAssertFactories.type;
|
||||
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.Repeated;
|
||||
@@ -19,7 +16,6 @@ import java.io.IOException;
|
||||
import org.assertj.core.api.AbstractObjectAssert;
|
||||
import org.assertj.core.api.AbstractThrowableAssert;
|
||||
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
||||
import org.assertj.core.api.ThrowableAssertAlternative;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
|
||||
/**
|
||||
@@ -35,21 +31,6 @@ import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
final class AssertJThrowingCallableRules {
|
||||
private AssertJThrowingCallableRules() {}
|
||||
|
||||
static final class AssertThatThrownByIsInstanceOf<T extends Throwable> {
|
||||
@BeforeTemplate
|
||||
void before(ThrowingCallable throwingCallable, Class<T> exceptionType) {
|
||||
Refaster.anyOf(
|
||||
assertThatThrownBy(throwingCallable).asInstanceOf(throwable(exceptionType)),
|
||||
assertThatThrownBy(throwingCallable).asInstanceOf(type(exceptionType)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(ThrowingCallable throwingCallable, Class<T> exceptionType) {
|
||||
assertThatThrownBy(throwingCallable).isInstanceOf(exceptionType);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByIllegalArgumentException {
|
||||
@BeforeTemplate
|
||||
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable) {
|
||||
@@ -80,27 +61,6 @@ final class AssertJThrowingCallableRules {
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByIllegalArgumentExceptionRootCauseHasMessage {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByIllegalArgumentException" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
|
||||
return assertThatIllegalArgumentException()
|
||||
.isThrownBy(throwingCallable)
|
||||
.havingRootCause()
|
||||
.withMessage(message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(ThrowingCallable throwingCallable, String message) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.rootCause()
|
||||
.hasMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByIllegalArgumentExceptionHasMessageParameters {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
@@ -211,27 +171,6 @@ final class AssertJThrowingCallableRules {
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByIllegalStateExceptionRootCauseHasMessage {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByIllegalStateException" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
|
||||
return assertThatIllegalStateException()
|
||||
.isThrownBy(throwingCallable)
|
||||
.havingRootCause()
|
||||
.withMessage(message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(ThrowingCallable throwingCallable, String message) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.rootCause()
|
||||
.hasMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByIllegalStateExceptionHasMessageParameters {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
@@ -340,27 +279,6 @@ final class AssertJThrowingCallableRules {
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByNullPointerExceptionRootCauseHasMessage {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByNullPointerException" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
|
||||
return assertThatNullPointerException()
|
||||
.isThrownBy(throwingCallable)
|
||||
.havingRootCause()
|
||||
.withMessage(message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(ThrowingCallable throwingCallable, String message) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
.isInstanceOf(NullPointerException.class)
|
||||
.rootCause()
|
||||
.hasMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByNullPointerExceptionHasMessageParameters {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
@@ -468,26 +386,6 @@ final class AssertJThrowingCallableRules {
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByIOExceptionRootCauseHasMessage {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatThrownByIOException" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
|
||||
return assertThatIOException()
|
||||
.isThrownBy(throwingCallable)
|
||||
.havingRootCause()
|
||||
.withMessage(message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(ThrowingCallable throwingCallable, String message) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
.isInstanceOf(IOException.class)
|
||||
.rootCause()
|
||||
.hasMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByIOExceptionHasMessageParameters {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatThrownByIOException" /* This is a more specific template. */)
|
||||
@@ -554,24 +452,24 @@ final class AssertJThrowingCallableRules {
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByAsInstanceOfThrowable<T extends Throwable> {
|
||||
static final class AssertThatThrownBy {
|
||||
@BeforeTemplate
|
||||
ThrowableAssertAlternative<T> before(
|
||||
ThrowingCallable throwingCallable, Class<T> exceptionType) {
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
ThrowingCallable throwingCallable, Class<? extends Throwable> exceptionType) {
|
||||
return assertThatExceptionOfType(exceptionType).isThrownBy(throwingCallable);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractThrowableAssert<?, T> after(ThrowingCallable throwingCallable, Class<T> exceptionType) {
|
||||
return assertThatThrownBy(throwingCallable).asInstanceOf(throwable(exceptionType));
|
||||
AbstractObjectAssert<?, ?> after(
|
||||
ThrowingCallable throwingCallable, Class<? extends Throwable> exceptionType) {
|
||||
return assertThatThrownBy(throwingCallable).isInstanceOf(exceptionType);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByHasMessage {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByAsInstanceOfThrowable" /* This is a more specific template. */)
|
||||
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
@@ -591,37 +489,9 @@ final class AssertJThrowingCallableRules {
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByRootCauseHasMessage {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByAsInstanceOfThrowable" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
String message) {
|
||||
return assertThatExceptionOfType(exceptionType)
|
||||
.isThrownBy(throwingCallable)
|
||||
.havingRootCause()
|
||||
.withMessage(message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
String message) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
.isInstanceOf(exceptionType)
|
||||
.rootCause()
|
||||
.hasMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByHasMessageParameters {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByAsInstanceOfThrowable" /* This is a more specific template. */)
|
||||
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
@@ -647,8 +517,7 @@ final class AssertJThrowingCallableRules {
|
||||
|
||||
static final class AssertThatThrownByHasMessageStartingWith {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByAsInstanceOfThrowable" /* This is a more specific template. */)
|
||||
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
@@ -672,8 +541,7 @@ final class AssertJThrowingCallableRules {
|
||||
|
||||
static final class AssertThatThrownByHasMessageContaining {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByAsInstanceOfThrowable" /* This is a more specific template. */)
|
||||
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
@@ -697,8 +565,7 @@ final class AssertJThrowingCallableRules {
|
||||
|
||||
static final class AssertThatThrownByHasMessageNotContaining {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByAsInstanceOfThrowable" /* This is a more specific template. */)
|
||||
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkElementIndex;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Sets.toImmutableEnumSet;
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static java.util.Collections.disjoint;
|
||||
import static java.util.Objects.checkIndex;
|
||||
@@ -68,6 +70,28 @@ final class AssortedRules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link Sets#toImmutableEnumSet()} when possible, as it is more efficient than {@link
|
||||
* ImmutableSet#toImmutableSet()} and produces a more compact object.
|
||||
*
|
||||
* <p><strong>Warning:</strong> this rewrite rule is not completely behavior preserving: while the
|
||||
* original code produces a set that iterates over the elements in encounter order, the
|
||||
* replacement code iterates over the elements in enum definition order.
|
||||
*/
|
||||
// XXX: ^ Consider emitting a comment warning about this fact?
|
||||
static final class StreamToImmutableEnumSet<T extends Enum<T>> {
|
||||
@BeforeTemplate
|
||||
ImmutableSet<T> before(Stream<T> stream) {
|
||||
return stream.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
ImmutableSet<T> after(Stream<T> stream) {
|
||||
return stream.collect(toImmutableEnumSet());
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Iterators#getNext(Iterator, Object)} over more contrived alternatives. */
|
||||
static final class IteratorGetNextOrDefault<T> {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -9,9 +9,7 @@ import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.sun.tools.javac.util.Constants;
|
||||
import com.sun.tools.javac.util.Convert;
|
||||
import javax.lang.model.element.Name;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/** Refaster rules related to {@link com.google.errorprone.bugpatterns.BugChecker} classes. */
|
||||
@OnlineDocumentation
|
||||
@@ -57,44 +55,16 @@ final class BugCheckerRules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link SourceCode#toStringConstantExpression(Object,
|
||||
* com.google.errorprone.VisitorState)} over alternatives that unnecessarily escape single quote
|
||||
* characters.
|
||||
*/
|
||||
/** Prefer using the {@link Constants} API over more verbose alternatives. */
|
||||
static final class ConstantsFormat {
|
||||
@BeforeTemplate
|
||||
String before(CharSequence value) {
|
||||
return Constants.format(value);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
String before(String value) {
|
||||
return String.format("\"%s\"", Convert.quote(value));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(CharSequence value) {
|
||||
return SourceCode.toStringConstantExpression(
|
||||
value, Refaster.emitCommentBefore("REPLACEME", null));
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Name#contentEquals(CharSequence)} over more verbose alternatives. */
|
||||
static final class NameContentEquals {
|
||||
@BeforeTemplate
|
||||
boolean before(Name name, CharSequence string) {
|
||||
return name.toString().equals(string.toString());
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
boolean before(Name name, String string) {
|
||||
return name.toString().equals(string);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
boolean after(Name name, CharSequence string) {
|
||||
return name.contentEquals(string);
|
||||
String after(String value) {
|
||||
return Constants.format(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
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 tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
|
||||
/** Refaster rules related to expressions dealing with {@link CharSequence}s. */
|
||||
@OnlineDocumentation
|
||||
final class CharSequenceRules {
|
||||
private CharSequenceRules() {}
|
||||
|
||||
/**
|
||||
* Prefer {@link CharSequence#isEmpty()} over alternatives that consult the char sequence's
|
||||
* length.
|
||||
*/
|
||||
// XXX: Drop this rule once we (and OpenRewrite) no longer support projects targeting Java 14 or
|
||||
// below.
|
||||
static final class CharSequenceIsEmpty {
|
||||
@BeforeTemplate
|
||||
boolean before(CharSequence charSequence) {
|
||||
return Refaster.anyOf(
|
||||
charSequence.length() == 0, charSequence.length() <= 0, charSequence.length() < 1);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@AlsoNegation
|
||||
boolean after(CharSequence charSequence) {
|
||||
return charSequence.isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ 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;
|
||||
|
||||
@@ -38,12 +37,7 @@ final class ClassRules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
/** Prefer {@link Class#isInstance(Object)} method references over more verbose alternatives. */
|
||||
static final class ClassLiteralIsInstancePredicate<T, S> {
|
||||
@BeforeTemplate
|
||||
Predicate<S> before() {
|
||||
@@ -56,11 +50,7 @@ final class ClassRules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
/** Prefer {@link Class#isInstance(Object)} method references over more verbose alternatives. */
|
||||
static final class ClassReferenceIsInstancePredicate<T, S> {
|
||||
@BeforeTemplate
|
||||
Predicate<S> before(Class<T> clazz) {
|
||||
@@ -72,21 +62,4 @@ final class ClassRules {
|
||||
return clazz::isInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<T, S> {
|
||||
@BeforeTemplate
|
||||
Function<T, S> before(Class<? extends S> clazz) {
|
||||
return o -> clazz.cast(o);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Function<T, S> after(Class<? extends S> clazz) {
|
||||
return clazz::cast;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,7 @@ 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;
|
||||
@@ -23,7 +21,6 @@ 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
|
||||
@@ -187,24 +184,6 @@ 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<T> {
|
||||
@BeforeTemplate
|
||||
Stream<?> before(Set<T> set) {
|
||||
return set.stream().distinct();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Stream<?> after(Set<T> set) {
|
||||
return set.stream();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link ArrayList#ArrayList(Collection)} over the Guava alternative. */
|
||||
@SuppressWarnings(
|
||||
"NonApiType" /* Matching against `List` would unnecessarily constrain the rule. */)
|
||||
@@ -297,23 +276,6 @@ 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<T> {
|
||||
// XXX: This expression produces an unmodifiable list, while the alternative doesn't.
|
||||
@BeforeTemplate
|
||||
List<T> before(@NotMatches(IsRefasterAsVarargs.class) T[] array) {
|
||||
return Arrays.stream(array).toList();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
List<T> after(T[] array) {
|
||||
return Arrays.asList(array);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer calling {@link Collection#toArray()} over more contrived alternatives. */
|
||||
static final class CollectionToArray<T> {
|
||||
@BeforeTemplate
|
||||
@@ -365,20 +327,18 @@ final class CollectionRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Collection#iterator()} over more contrived or less efficient alternatives. */
|
||||
static final class CollectionIterator<T> {
|
||||
@BeforeTemplate
|
||||
Iterator<T> before(Collection<T> collection) {
|
||||
return collection.stream().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't call {@link ImmutableCollection#asList()} if {@link ImmutableCollection#iterator()} is
|
||||
* called on the result; call it directly.
|
||||
*/
|
||||
static final class ImmutableCollectionIterator<T> {
|
||||
@BeforeTemplate
|
||||
Iterator<T> before(ImmutableCollection<T> collection) {
|
||||
return collection.asList().iterator();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Iterator<T> after(Collection<T> collection) {
|
||||
Iterator<T> after(ImmutableCollection<T> collection) {
|
||||
return collection.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,8 @@ 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;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Function;
|
||||
@@ -245,77 +243,18 @@ final class ComparatorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Collections#sort(List)} over more verbose alternatives. */
|
||||
static final class CollectionsSort<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
void before(List<T> collection) {
|
||||
Collections.sort(collection, naturalOrder());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
void after(List<T> collection) {
|
||||
Collections.sort(collection);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Collections#min(Collection)} over more verbose alternatives. */
|
||||
static final class CollectionsMin<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
T before(Collection<T> collection) {
|
||||
return Refaster.anyOf(
|
||||
Collections.min(collection, naturalOrder()), Collections.max(collection, reverseOrder()));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(Collection<T> collection) {
|
||||
return Collections.min(collection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid unnecessary creation of a {@link Stream} to determine the minimum of a known collection
|
||||
* of values.
|
||||
*/
|
||||
static final class MinOfArray<S, T extends S> {
|
||||
static final class MinOfVarargs<T> {
|
||||
@BeforeTemplate
|
||||
T before(T[] array, Comparator<S> cmp) {
|
||||
return Arrays.stream(array).min(cmp).orElseThrow();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(T[] array, Comparator<S> cmp) {
|
||||
return Collections.min(Arrays.asList(array), cmp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid unnecessary creation of a {@link Stream} to determine the minimum of a known collection
|
||||
* of values.
|
||||
*/
|
||||
static final class CollectionsMinWithComparator<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
T before(Collection<T> collection, Comparator<S> cmp) {
|
||||
return collection.stream().min(cmp).orElseThrow();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(Collection<T> collection, Comparator<S> cmp) {
|
||||
return Collections.min(collection, cmp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid unnecessary creation of a {@link Stream} to determine the minimum of a known collection
|
||||
* of values.
|
||||
*/
|
||||
static final class MinOfVarargs<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
T before(@Repeated T value, Comparator<S> cmp) {
|
||||
T before(@Repeated T value, Comparator<T> cmp) {
|
||||
return Stream.of(Refaster.asVarargs(value)).min(cmp).orElseThrow();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(@Repeated T value, Comparator<S> cmp) {
|
||||
T after(@Repeated T value, Comparator<T> cmp) {
|
||||
return Collections.min(Arrays.asList(value), cmp);
|
||||
}
|
||||
}
|
||||
@@ -371,64 +310,18 @@ final class ComparatorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Collections#max(Collection)} over more verbose alternatives. */
|
||||
static final class CollectionsMax<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
T before(Collection<T> collection) {
|
||||
return Refaster.anyOf(
|
||||
Collections.max(collection, naturalOrder()), Collections.min(collection, reverseOrder()));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(Collection<T> collection) {
|
||||
return Collections.max(collection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid unnecessary creation of a {@link Stream} to determine the maximum of a known collection
|
||||
* of values.
|
||||
*/
|
||||
static final class MaxOfArray<S, T extends S> {
|
||||
static final class MaxOfVarargs<T> {
|
||||
@BeforeTemplate
|
||||
T before(T[] array, Comparator<S> cmp) {
|
||||
return Arrays.stream(array).max(cmp).orElseThrow();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(T[] array, Comparator<S> cmp) {
|
||||
return Collections.max(Arrays.asList(array), cmp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid unnecessary creation of a {@link Stream} to determine the maximum of a known collection
|
||||
* of values.
|
||||
*/
|
||||
static final class CollectionsMaxWithComparator<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
T before(Collection<T> collection, Comparator<S> cmp) {
|
||||
return collection.stream().max(cmp).orElseThrow();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(Collection<T> collection, Comparator<S> cmp) {
|
||||
return Collections.max(collection, cmp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid unnecessary creation of a {@link Stream} to determine the maximum of a known collection
|
||||
* of values.
|
||||
*/
|
||||
static final class MaxOfVarargs<S, T extends S> {
|
||||
@BeforeTemplate
|
||||
T before(@Repeated T value, Comparator<S> cmp) {
|
||||
T before(@Repeated T value, Comparator<T> cmp) {
|
||||
return Stream.of(Refaster.asVarargs(value)).max(cmp).orElseThrow();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(@Repeated T value, Comparator<S> cmp) {
|
||||
T after(@Repeated T value, Comparator<T> cmp) {
|
||||
return Collections.max(Arrays.asList(value), cmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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;
|
||||
@@ -20,9 +19,9 @@ import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
final class EqualityRules {
|
||||
private EqualityRules() {}
|
||||
|
||||
/** 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<T extends Enum<T>> {
|
||||
/** 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<T extends Enum<T>> {
|
||||
/**
|
||||
* 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
|
||||
@@ -44,20 +43,6 @@ final class EqualityRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer reference-based equality for enums. */
|
||||
static final class EnumReferenceEqualityLambda<T extends Enum<T>> {
|
||||
@BeforeTemplate
|
||||
Predicate<T> before(T e) {
|
||||
return Refaster.anyOf(isEqual(e), e::equals);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("java:S1698" /* Reference comparison is valid for enums. */)
|
||||
Predicate<T> 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.
|
||||
|
||||
@@ -2,18 +2,12 @@ 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 com.google.errorprone.refaster.annotation.Repeated;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
|
||||
/** Refaster rules related to expressions dealing with files. */
|
||||
@@ -21,49 +15,6 @@ import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
final class FileRules {
|
||||
private FileRules() {}
|
||||
|
||||
/** Prefer the more idiomatic {@link Path#of(URI)} over {@link Paths#get(URI)}. */
|
||||
static final class PathOfUri {
|
||||
@BeforeTemplate
|
||||
Path before(URI uri) {
|
||||
return Paths.get(uri);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Path after(URI uri) {
|
||||
return Path.of(uri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer the more idiomatic {@link Path#of(String, String...)} over {@link Paths#get(String,
|
||||
* String...)}.
|
||||
*/
|
||||
static final class PathOfString {
|
||||
@BeforeTemplate
|
||||
Path before(String first, @Repeated String more) {
|
||||
return Paths.get(first, more);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Path after(String first, @Repeated String more) {
|
||||
return Path.of(first, more);
|
||||
}
|
||||
}
|
||||
|
||||
/** Avoid redundant conversions from {@link Path} to {@link File}. */
|
||||
// XXX: Review whether a rule such as this one is better handled by the `IdentityConversion` rule.
|
||||
static final class PathInstance {
|
||||
@BeforeTemplate
|
||||
Path before(Path path) {
|
||||
return path.toFile().toPath();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Path after(Path path) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Files#readString(Path, Charset)} over more contrived alternatives. */
|
||||
static final class FilesReadStringWithCharset {
|
||||
@BeforeTemplate
|
||||
@@ -89,56 +40,4 @@ final class FileRules {
|
||||
return Files.readString(path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Files#createTempFile(String, String, FileAttribute[])} over alternatives that
|
||||
* create files with more liberal permissions.
|
||||
*
|
||||
* <p>Note that {@link File#createTempFile} treats the given prefix as a path, and ignores all but
|
||||
* its file name. That is, the actual prefix used is derived from all characters following the
|
||||
* final file separator (if any). This is not the case with {@link Files#createTempFile}, which
|
||||
* will instead throw an {@link IllegalArgumentException} if the prefix contains any file
|
||||
* separators.
|
||||
*/
|
||||
static final class FilesCreateTempFileToFile {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings({
|
||||
"FilesCreateTempFileInCustomDirectoryToFile" /* This is a more specific template. */,
|
||||
"java:S5443" /* This violation will be rewritten. */,
|
||||
"key-to-resolve-AnnotationUseStyle-and-TrailingComment-check-conflict"
|
||||
})
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Files#createTempFile(Path, String, String, FileAttribute[])} over alternatives
|
||||
* that create files with more liberal permissions.
|
||||
*
|
||||
* <p>Note that {@link File#createTempFile} treats the given prefix as a path, and ignores all but
|
||||
* its file name. That is, the actual prefix used is derived from all characters following the
|
||||
* final file separator (if any). This is not the case with {@link Files#createTempFile}, which
|
||||
* will instead throw an {@link IllegalArgumentException} if the prefix contains any file
|
||||
* separators.
|
||||
*/
|
||||
static final class FilesCreateTempFileInCustomDirectoryToFile {
|
||||
@BeforeTemplate
|
||||
File before(File directory, String prefix, String suffix) throws IOException {
|
||||
return File.createTempFile(prefix, suffix, directory);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
File after(File directory, String prefix, String suffix) throws IOException {
|
||||
return Files.createTempFile(directory.toPath(), prefix, suffix).toFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Sets.toImmutableEnumSet;
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.Repeated;
|
||||
import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
|
||||
/**
|
||||
* Refaster rules related to expressions dealing with {@code
|
||||
* com.google.common.collect.ImmutableEnumSet}s.
|
||||
*/
|
||||
// XXX: Some of the rules defined here impact iteration order. That's a rather subtle change. Should
|
||||
// we emit a comment warning about this fact? (This may produce a lot of noise. A bug checker could
|
||||
// in some cases determine whether iteration order is important.)
|
||||
// XXX: Consider replacing the `SetsImmutableEnumSet[N]` Refaster rules with a bug checker, such
|
||||
// that call to `ImmutableSet#of(Object, Object, Object, Object, Object, Object, Object[])` with
|
||||
// enum-typed values can also be rewritten.
|
||||
@OnlineDocumentation
|
||||
final class ImmutableEnumSetRules {
|
||||
private ImmutableEnumSetRules() {}
|
||||
|
||||
/**
|
||||
* Prefer {@link Sets#immutableEnumSet(Iterable)} for enum collections to take advantage of the
|
||||
* internally used {@link EnumSet}.
|
||||
*
|
||||
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the
|
||||
* original code produces a set that iterates over its elements in the same order as the input
|
||||
* {@link Iterable}, the replacement code iterates over the elements in enum definition order.
|
||||
*/
|
||||
static final class SetsImmutableEnumSetIterable<T extends Enum<T>> {
|
||||
@BeforeTemplate
|
||||
ImmutableSet<T> before(Iterable<T> elements) {
|
||||
return ImmutableSet.copyOf(elements);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableSet<T> before(Collection<T> elements) {
|
||||
return ImmutableSet.copyOf(elements);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableSet<T> after(Iterable<T> elements) {
|
||||
return Sets.immutableEnumSet(elements);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Sets#immutableEnumSet(Iterable)} for enum collections to take advantage of the
|
||||
* internally used {@link EnumSet}.
|
||||
*
|
||||
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the
|
||||
* original code produces a set that iterates over its elements in the same order as defined in
|
||||
* the array, the replacement code iterates over the elements in enum definition order.
|
||||
*/
|
||||
static final class SetsImmutableEnumSetArraysAsList<T extends Enum<T>> {
|
||||
@BeforeTemplate
|
||||
ImmutableSet<T> before(T[] elements) {
|
||||
return ImmutableSet.copyOf(elements);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableSet<T> after(T[] elements) {
|
||||
return Sets.immutableEnumSet(Arrays.asList(elements));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
|
||||
* the internally used {@link EnumSet}.
|
||||
*/
|
||||
static final class SetsImmutableEnumSet1<T extends Enum<T>> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("SetsImmutableEnumSetIterable" /* This is a more specific template. */)
|
||||
ImmutableSet<T> before(T e1) {
|
||||
return Refaster.anyOf(ImmutableSet.of(e1), ImmutableSet.copyOf(EnumSet.of(e1)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ImmutableSet<T> after(T e1) {
|
||||
return Sets.immutableEnumSet(e1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
|
||||
* the internally used {@link EnumSet}.
|
||||
*
|
||||
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the {@link
|
||||
* ImmutableSet#of} expression produces a set that iterates over its elements in the listed order,
|
||||
* the replacement code iterates over the elements in enum definition order.
|
||||
*/
|
||||
static final class SetsImmutableEnumSet2<T extends Enum<T>> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("SetsImmutableEnumSetIterable" /* This is a more specific template. */)
|
||||
ImmutableSet<T> before(T e1, T e2) {
|
||||
return Refaster.anyOf(ImmutableSet.of(e1, e2), ImmutableSet.copyOf(EnumSet.of(e1, e2)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ImmutableSet<T> after(T e1, T e2) {
|
||||
return Sets.immutableEnumSet(e1, e2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
|
||||
* the internally used {@link EnumSet}.
|
||||
*
|
||||
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the {@link
|
||||
* ImmutableSet#of} expression produces a set that iterates over its elements in the listed order,
|
||||
* the replacement code iterates over the elements in enum definition order.
|
||||
*/
|
||||
static final class SetsImmutableEnumSet3<T extends Enum<T>> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("SetsImmutableEnumSetIterable" /* This is a more specific template. */)
|
||||
ImmutableSet<T> before(T e1, T e2, T e3) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSet.of(e1, e2, e3), ImmutableSet.copyOf(EnumSet.of(e1, e2, e3)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ImmutableSet<T> after(T e1, T e2, T e3) {
|
||||
return Sets.immutableEnumSet(e1, e2, e3);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
|
||||
* the internally used {@link EnumSet}.
|
||||
*
|
||||
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the {@link
|
||||
* ImmutableSet#of} expression produces a set that iterates over its elements in the listed order,
|
||||
* the replacement code iterates over the elements in enum definition order.
|
||||
*/
|
||||
static final class SetsImmutableEnumSet4<T extends Enum<T>> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("SetsImmutableEnumSetIterable" /* This is a more specific template. */)
|
||||
ImmutableSet<T> before(T e1, T e2, T e3, T e4) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSet.of(e1, e2, e3, e4), ImmutableSet.copyOf(EnumSet.of(e1, e2, e3, e4)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ImmutableSet<T> after(T e1, T e2, T e3, T e4) {
|
||||
return Sets.immutableEnumSet(e1, e2, e3, e4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
|
||||
* the internally used {@link EnumSet}.
|
||||
*
|
||||
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the {@link
|
||||
* ImmutableSet#of} expression produces a set that iterates over its elements in the listed order,
|
||||
* the replacement code iterates over the elements in enum definition order.
|
||||
*/
|
||||
static final class SetsImmutableEnumSet5<T extends Enum<T>> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("SetsImmutableEnumSetIterable" /* This is a more specific template. */)
|
||||
ImmutableSet<T> before(T e1, T e2, T e3, T e4, T e5) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSet.of(e1, e2, e3, e4, e5), ImmutableSet.copyOf(EnumSet.of(e1, e2, e3, e4, e5)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ImmutableSet<T> after(T e1, T e2, T e3, T e4, T e5) {
|
||||
return Sets.immutableEnumSet(e1, e2, e3, e4, e5);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
|
||||
* the internally used {@link EnumSet}.
|
||||
*
|
||||
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the
|
||||
* original code produces a set that iterates over its elements in the listed order, the
|
||||
* replacement code iterates over the elements in enum definition order.
|
||||
*/
|
||||
static final class SetsImmutableEnumSet6<T extends Enum<T>> {
|
||||
@BeforeTemplate
|
||||
ImmutableSet<T> before(T e1, T e2, T e3, T e4, T e5, T e6) {
|
||||
return ImmutableSet.of(e1, e2, e3, e4, e5, e6);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("unchecked")
|
||||
ImmutableSet<T> after(T e1, T e2, T e3, T e4, T e5, T e6) {
|
||||
return Sets.immutableEnumSet(e1, e2, e3, e4, e5, e6);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
|
||||
* the internally used {@link EnumSet}.
|
||||
*/
|
||||
static final class SetsImmutableEnumSetVarArgs<T extends Enum<T>> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("SetsImmutableEnumSetIterable" /* This is a more specific template. */)
|
||||
ImmutableSet<T> before(T e1, @Repeated T elements) {
|
||||
return ImmutableSet.copyOf(EnumSet.of(e1, Refaster.asVarargs(elements)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableSet<T> after(T e1, @Repeated T elements) {
|
||||
return Sets.immutableEnumSet(e1, Refaster.asVarargs(elements));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link Sets#toImmutableEnumSet()} when possible, as it is more efficient than {@link
|
||||
* ImmutableSet#toImmutableSet()} and produces a more compact object.
|
||||
*
|
||||
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the
|
||||
* original code produces a set that iterates over its elements in encounter order, the
|
||||
* replacement code iterates over the elements in enum definition order.
|
||||
*/
|
||||
static final class StreamToImmutableEnumSet<T extends Enum<T>> {
|
||||
@BeforeTemplate
|
||||
ImmutableSet<T> before(Stream<T> stream) {
|
||||
return stream.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
ImmutableSet<T> after(Stream<T> stream) {
|
||||
return stream.collect(toImmutableEnumSet());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,8 @@ final class ImmutableListMultimapRules {
|
||||
* Prefer {@link ImmutableListMultimap#builder()} over the associated constructor on constructions
|
||||
* that produce a less-specific type.
|
||||
*/
|
||||
// XXX: This rule may drop generic type information, leading to non-compilable code.
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableListMultimapBuilder<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableMultimap.Builder<K, V> before() {
|
||||
|
||||
@@ -28,7 +28,8 @@ final class ImmutableListRules {
|
||||
private ImmutableListRules() {}
|
||||
|
||||
/** Prefer {@link ImmutableList#builder()} over the associated constructor. */
|
||||
// XXX: This rule may drop generic type information, leading to non-compilable code.
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableListBuilder<T> {
|
||||
@BeforeTemplate
|
||||
ImmutableList.Builder<T> before() {
|
||||
|
||||
@@ -31,7 +31,8 @@ final class ImmutableMapRules {
|
||||
private ImmutableMapRules() {}
|
||||
|
||||
/** Prefer {@link ImmutableMap#builder()} over the associated constructor. */
|
||||
// XXX: This rule may drop generic type information, leading to non-compilable code.
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableMapBuilder<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableMap.Builder<K, V> before() {
|
||||
|
||||
@@ -21,7 +21,8 @@ final class ImmutableMultisetRules {
|
||||
private ImmutableMultisetRules() {}
|
||||
|
||||
/** Prefer {@link ImmutableMultiset#builder()} over the associated constructor. */
|
||||
// XXX: This rule may drop generic type information, leading to non-compilable code.
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableMultisetBuilder<T> {
|
||||
@BeforeTemplate
|
||||
ImmutableMultiset.Builder<T> before() {
|
||||
|
||||
@@ -29,7 +29,8 @@ final class ImmutableSetMultimapRules {
|
||||
private ImmutableSetMultimapRules() {}
|
||||
|
||||
/** Prefer {@link ImmutableSetMultimap#builder()} over the associated constructor. */
|
||||
// XXX: This rule may drop generic type information, leading to non-compilable code.
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableSetMultimapBuilder<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableSetMultimap.Builder<K, V> before() {
|
||||
|
||||
@@ -29,7 +29,8 @@ final class ImmutableSetRules {
|
||||
private ImmutableSetRules() {}
|
||||
|
||||
/** Prefer {@link ImmutableSet#builder()} over the associated constructor. */
|
||||
// XXX: This rule may drop generic type information, leading to non-compilable code.
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableSetBuilder<T> {
|
||||
@BeforeTemplate
|
||||
ImmutableSet.Builder<T> before() {
|
||||
|
||||
@@ -37,7 +37,8 @@ final class ImmutableSortedMapRules {
|
||||
* Prefer {@link ImmutableSortedMap#naturalOrder()} over the alternative that requires explicitly
|
||||
* providing the {@link Comparator}.
|
||||
*/
|
||||
// XXX: This rule may drop generic type information, leading to non-compilable code.
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableSortedMapNaturalOrderBuilder<K extends Comparable<? super K>, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableSortedMap.Builder<K, V> before() {
|
||||
@@ -54,7 +55,8 @@ final class ImmutableSortedMapRules {
|
||||
* Prefer {@link ImmutableSortedMap#reverseOrder()} over the alternative that requires explicitly
|
||||
* providing the {@link Comparator}.
|
||||
*/
|
||||
// XXX: This rule may drop generic type information, leading to non-compilable code.
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableSortedMapReverseOrderBuilder<K extends Comparable<? super K>, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableSortedMap.Builder<K, V> before() {
|
||||
|
||||
@@ -302,22 +302,16 @@ import tech.picnic.errorprone.refaster.annotation.TypeMigration;
|
||||
final class JUnitToAssertJRules {
|
||||
private JUnitToAssertJRules() {}
|
||||
|
||||
static final class Fail<T> {
|
||||
static final class ThrowNewAssertionError {
|
||||
@BeforeTemplate
|
||||
T before() {
|
||||
return Assertions.fail();
|
||||
void before() {
|
||||
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
|
||||
T after() {
|
||||
return fail();
|
||||
void after() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +321,12 @@ final class JUnitToAssertJRules {
|
||||
return Assertions.fail(message);
|
||||
}
|
||||
|
||||
// XXX: Add `@UseImportPolicy(STATIC_IMPORT_ALWAYS)`. See `Fail` comment.
|
||||
// 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
|
||||
T after(String message) {
|
||||
return fail(message);
|
||||
@@ -340,24 +339,28 @@ final class JUnitToAssertJRules {
|
||||
return Assertions.fail(message, throwable);
|
||||
}
|
||||
|
||||
// XXX: Add `@UseImportPolicy(STATIC_IMPORT_ALWAYS)`. See `Fail` comment.
|
||||
// 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
|
||||
T after(String message, Throwable throwable) {
|
||||
return fail(message, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
static final class FailWithThrowable<T> {
|
||||
static final class FailWithThrowable {
|
||||
@BeforeTemplate
|
||||
T before(Throwable throwable) {
|
||||
return Assertions.fail(throwable);
|
||||
void before(Throwable throwable) {
|
||||
Assertions.fail(throwable);
|
||||
}
|
||||
|
||||
// XXX: Add `@UseImportPolicy(STATIC_IMPORT_ALWAYS)`. See `Fail` comment.
|
||||
@AfterTemplate
|
||||
@DoNotCall
|
||||
T after(Throwable throwable) {
|
||||
return fail(throwable);
|
||||
void after(Throwable throwable) {
|
||||
throw new AssertionError(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
import io.micrometer.core.instrument.Tags;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
|
||||
/** Refaster rules related to expressions dealing with Micrometer. */
|
||||
// XXX: Consider replacing the `TagsOf[N]` rules with a bug checker, so that various other
|
||||
// expressions (e.g. those creating other collection types, those passing in tags some other way, or
|
||||
// those passing in more tags) can be replaced as wel.
|
||||
@OnlineDocumentation
|
||||
final class MicrometerRules {
|
||||
private MicrometerRules() {}
|
||||
|
||||
/** Prefer using {@link Tags} over other immutable collections. */
|
||||
static final class TagsOf1 {
|
||||
@BeforeTemplate
|
||||
ImmutableCollection<Tag> before(Tag tag) {
|
||||
return Refaster.anyOf(ImmutableSet.of(tag), ImmutableList.of(tag));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Iterable<Tag> after(Tag tag) {
|
||||
return Tags.of(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer using {@link Tags} over other immutable collections. */
|
||||
static final class TagsOf2 {
|
||||
@BeforeTemplate
|
||||
ImmutableCollection<Tag> before(Tag tag1, Tag tag2) {
|
||||
return Refaster.anyOf(ImmutableSet.of(tag1, tag2), ImmutableList.of(tag1, tag2));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Iterable<Tag> after(Tag tag1, Tag tag2) {
|
||||
return Tags.of(tag1, tag2);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer using {@link Tags} over other immutable collections. */
|
||||
static final class TagsOf3 {
|
||||
@BeforeTemplate
|
||||
ImmutableCollection<Tag> before(Tag tag1, Tag tag2, Tag tag3) {
|
||||
return Refaster.anyOf(ImmutableSet.of(tag1, tag2, tag3), ImmutableList.of(tag1, tag2, tag3));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Iterable<Tag> after(Tag tag1, Tag tag2, Tag tag3) {
|
||||
return Tags.of(tag1, tag2, tag3);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer using {@link Tags} over other immutable collections. */
|
||||
static final class TagsOf4 {
|
||||
@BeforeTemplate
|
||||
ImmutableCollection<Tag> before(Tag tag1, Tag tag2, Tag tag3, Tag tag4) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSet.of(tag1, tag2, tag3, tag4), ImmutableList.of(tag1, tag2, tag3, tag4));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Iterable<Tag> after(Tag tag1, Tag tag2, Tag tag3, Tag tag4) {
|
||||
return Tags.of(tag1, tag2, tag3, tag4);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer using {@link Tags} over other immutable collections. */
|
||||
static final class TagsOf5 {
|
||||
@BeforeTemplate
|
||||
ImmutableCollection<Tag> before(Tag tag1, Tag tag2, Tag tag3, Tag tag4, Tag tag5) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSet.of(tag1, tag2, tag3, tag4, tag5),
|
||||
ImmutableList.of(tag1, tag2, tag3, tag4, tag5));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Iterable<Tag> after(Tag tag1, Tag tag2, Tag tag3, Tag tag4, Tag tag5) {
|
||||
return Tags.of(tag1, tag2, tag3, tag4, tag5);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static com.mongodb.client.model.Filters.eq;
|
||||
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import com.mongodb.client.model.Filters;
|
||||
import org.bson.conversions.Bson;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
|
||||
/** Refaster rules related to MongoDB client expressions and statements. */
|
||||
@OnlineDocumentation
|
||||
final class MongoDBRules {
|
||||
private MongoDBRules() {}
|
||||
|
||||
/** Avoid {@link Enum#toString()} invocations when invoking {@link Filters#eq(String, Object)}. */
|
||||
static final class Eq {
|
||||
@BeforeTemplate
|
||||
Bson before(String field, Enum<?> value) {
|
||||
return eq(field, value.toString());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
Bson after(String field, Enum<?> value) {
|
||||
return eq(field, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
import tech.picnic.errorprone.refaster.matchers.RequiresComputation;
|
||||
import tech.picnic.errorprone.refaster.matchers.IsLikelyTrivialComputation;
|
||||
|
||||
/** Refaster rules related to expressions dealing with {@link Optional}s. */
|
||||
@OnlineDocumentation
|
||||
@@ -255,21 +255,24 @@ final class OptionalRules {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Optional#orElse(Object)} over {@link Optional#orElseGet(Supplier)} if the
|
||||
* fallback value does not require non-trivial computation.
|
||||
* Prefer {@link Optional#orElseGet(Supplier)} over {@link Optional#orElse(Object)} if the
|
||||
* fallback value is not the result of a trivial computation.
|
||||
*/
|
||||
// XXX: This rule is the counterpart to the `OptionalOrElseGet` bug checker. Once the
|
||||
// `MethodReferenceUsage` bug checker is "production ready", that bug checker may similarly be
|
||||
// replaced with a Refaster rule.
|
||||
static final class OptionalOrElse<T> {
|
||||
// XXX: This rule may introduce a compilation error: the `value` expression may reference a
|
||||
// non-effectively final variable, which is not allowed in the replacement lambda expression.
|
||||
// Review whether a `@Matcher` can be used to avoid this.
|
||||
// XXX: Once `MethodReferenceUsage` is "production ready", replace
|
||||
// `@NotMatches(IsLikelyTrivialComputation.class)` with `@Matches(RequiresComputation.class)` (and
|
||||
// reimplement the matcher accordingly).
|
||||
static final class OptionalOrElseGet<T> {
|
||||
@BeforeTemplate
|
||||
T before(Optional<T> optional, @NotMatches(RequiresComputation.class) T value) {
|
||||
return optional.orElseGet(() -> value);
|
||||
T before(Optional<T> optional, @NotMatches(IsLikelyTrivialComputation.class) T value) {
|
||||
return optional.orElse(value);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(Optional<T> optional, T value) {
|
||||
return optional.orElse(value);
|
||||
return optional.orElseGet(() -> value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,9 +282,6 @@ final class OptionalRules {
|
||||
*/
|
||||
// XXX: Do we need the `.filter(Optional::isPresent)`? If it's absent the caller probably assumed
|
||||
// that the values are present. (If we drop it, we should rewrite vacuous filter steps.)
|
||||
// XXX: The rewritten `filter`/`map` expression may be more performant than its replacement. See
|
||||
// https://github.com/palantir/gradle-baseline/pull/2946. (There are plans to pair Refaster rules
|
||||
// with JMH benchmarks; this would be a great use case.)
|
||||
static final class StreamFlatMapOptional<T> {
|
||||
@BeforeTemplate
|
||||
Stream<T> before(Stream<Optional<T>> stream) {
|
||||
@@ -373,12 +373,7 @@ final class OptionalRules {
|
||||
/** Prefer {@link Optional#or(Supplier)} over more verbose alternatives. */
|
||||
static final class OptionalOrOtherOptional<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings({
|
||||
"LexicographicalAnnotationAttributeListing" /* `key-*` entry must remain last. */,
|
||||
"NestedOptionals" /* This violation will be rewritten. */,
|
||||
"OptionalOrElse" /* Parameters represent expressions that may require computation. */,
|
||||
"key-to-resolve-AnnotationUseStyle-and-TrailingComment-check-conflict"
|
||||
})
|
||||
@SuppressWarnings("NestedOptionals")
|
||||
Optional<T> before(Optional<T> optional1, Optional<T> optional2) {
|
||||
// XXX: Note that rewriting the first and third variant will change the code's behavior if
|
||||
// `optional2` has side-effects.
|
||||
@@ -398,7 +393,10 @@ final class OptionalRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily transform an {@link Optional} to an equivalent instance. */
|
||||
/**
|
||||
* Avoid unnecessary operations on an {@link Optional} that ultimately result in that very same
|
||||
* {@link Optional}.
|
||||
*/
|
||||
static final class OptionalIdentity<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("NestedOptionals")
|
||||
|
||||
@@ -8,8 +8,6 @@ import com.google.common.primitives.Floats;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.google.common.primitives.Shorts;
|
||||
import com.google.common.primitives.UnsignedInts;
|
||||
import com.google.common.primitives.UnsignedLongs;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.AlsoNegation;
|
||||
@@ -78,8 +76,6 @@ final class PrimitiveRules {
|
||||
}
|
||||
|
||||
/** Prefer {@link Math#toIntExact(long)} over the Guava alternative. */
|
||||
// XXX: This rule changes the exception possibly thrown from `IllegalArgumentException` to
|
||||
// `ArithmeticException`.
|
||||
static final class LongToIntExact {
|
||||
@BeforeTemplate
|
||||
int before(long l) {
|
||||
@@ -196,6 +192,97 @@ final class PrimitiveRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Boolean#compare(boolean, boolean)} over the Guava alternative. */
|
||||
static final class BooleanCompare {
|
||||
@BeforeTemplate
|
||||
int before(boolean a, boolean b) {
|
||||
return Booleans.compare(a, b);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(boolean a, boolean b) {
|
||||
return Boolean.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Character#compare(char, char)} over the Guava alternative. */
|
||||
static final class CharacterCompare {
|
||||
@BeforeTemplate
|
||||
int before(char a, char b) {
|
||||
return Chars.compare(a, b);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(char a, char b) {
|
||||
return Character.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Short#compare(short, short)} over the Guava alternative. */
|
||||
static final class ShortCompare {
|
||||
@BeforeTemplate
|
||||
int before(short a, short b) {
|
||||
return Shorts.compare(a, b);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(short a, short b) {
|
||||
return Short.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Integer#compare(int, int)} over the Guava alternative. */
|
||||
static final class IntegerCompare {
|
||||
@BeforeTemplate
|
||||
int before(int a, int b) {
|
||||
return Ints.compare(a, b);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(int a, int b) {
|
||||
return Integer.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Long#compare(long, long)} over the Guava alternative. */
|
||||
static final class LongCompare {
|
||||
@BeforeTemplate
|
||||
int before(long a, long b) {
|
||||
return Longs.compare(a, b);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(long a, long b) {
|
||||
return Long.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Float#compare(float, float)} over the Guava alternative. */
|
||||
static final class FloatCompare {
|
||||
@BeforeTemplate
|
||||
int before(float a, float b) {
|
||||
return Floats.compare(a, b);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(float a, float b) {
|
||||
return Float.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Double#compare(double, double)} over the Guava alternative. */
|
||||
static final class DoubleCompare {
|
||||
@BeforeTemplate
|
||||
int before(double a, double b) {
|
||||
return Doubles.compare(a, b);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(double a, double b) {
|
||||
return Double.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Character#BYTES} over the Guava alternative. */
|
||||
static final class CharacterBytes {
|
||||
@BeforeTemplate
|
||||
@@ -355,205 +442,4 @@ final class PrimitiveRules {
|
||||
return Long.signum(l) == -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Integer#compareUnsigned(int, int)} over third-party alternatives. */
|
||||
static final class IntegerCompareUnsigned {
|
||||
@BeforeTemplate
|
||||
int before(int x, int y) {
|
||||
return UnsignedInts.compare(x, y);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(int x, int y) {
|
||||
return Integer.compareUnsigned(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Long#compareUnsigned(long, long)} over third-party alternatives. */
|
||||
static final class LongCompareUnsigned {
|
||||
@BeforeTemplate
|
||||
long before(long x, long y) {
|
||||
return UnsignedLongs.compare(x, y);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
long after(long x, long y) {
|
||||
return Long.compareUnsigned(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Integer#divideUnsigned(int, int)} over third-party alternatives. */
|
||||
static final class IntegerDivideUnsigned {
|
||||
@BeforeTemplate
|
||||
int before(int x, int y) {
|
||||
return UnsignedInts.divide(x, y);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(int x, int y) {
|
||||
return Integer.divideUnsigned(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Long#divideUnsigned(long, long)} over third-party alternatives. */
|
||||
static final class LongDivideUnsigned {
|
||||
@BeforeTemplate
|
||||
long before(long x, long y) {
|
||||
return UnsignedLongs.divide(x, y);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
long after(long x, long y) {
|
||||
return Long.divideUnsigned(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Integer#remainderUnsigned(int, int)} over third-party alternatives. */
|
||||
static final class IntegerRemainderUnsigned {
|
||||
@BeforeTemplate
|
||||
int before(int x, int y) {
|
||||
return UnsignedInts.remainder(x, y);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(int x, int y) {
|
||||
return Integer.remainderUnsigned(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Long#remainderUnsigned(long, long)} over third-party alternatives. */
|
||||
static final class LongRemainderUnsigned {
|
||||
@BeforeTemplate
|
||||
long before(long x, long y) {
|
||||
return UnsignedLongs.remainder(x, y);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
long after(long x, long y) {
|
||||
return Long.remainderUnsigned(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer JDK's {@link Integer#parseUnsignedInt(String)} over third-party or more verbose
|
||||
* alternatives.
|
||||
*/
|
||||
static final class IntegerParseUnsignedInt {
|
||||
@BeforeTemplate
|
||||
int before(String string) {
|
||||
return Refaster.anyOf(
|
||||
UnsignedInts.parseUnsignedInt(string), Integer.parseUnsignedInt(string, 10));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(String string) {
|
||||
return Integer.parseUnsignedInt(string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer JDK's {@link Long#parseUnsignedLong(String)} over third-party or more verbose
|
||||
* alternatives.
|
||||
*/
|
||||
static final class LongParseUnsignedLong {
|
||||
@BeforeTemplate
|
||||
long before(String string) {
|
||||
return Refaster.anyOf(
|
||||
UnsignedLongs.parseUnsignedLong(string), Long.parseUnsignedLong(string, 10));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
long after(String string) {
|
||||
return Long.parseUnsignedLong(string);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Integer#parseUnsignedInt(String, int)} over third-party alternatives. */
|
||||
static final class IntegerParseUnsignedIntWithRadix {
|
||||
@BeforeTemplate
|
||||
int before(String string, int radix) {
|
||||
return UnsignedInts.parseUnsignedInt(string, radix);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(String string, int radix) {
|
||||
return Integer.parseUnsignedInt(string, radix);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer JDK's {@link Long#parseUnsignedLong(String, int)} over third-party alternatives. */
|
||||
static final class LongParseUnsignedLongWithRadix {
|
||||
@BeforeTemplate
|
||||
long before(String string, int radix) {
|
||||
return UnsignedLongs.parseUnsignedLong(string, radix);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
long after(String string, int radix) {
|
||||
return Long.parseUnsignedLong(string, radix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer JDK's {@link Integer#toUnsignedString(int)} over third-party or more verbose
|
||||
* alternatives.
|
||||
*/
|
||||
static final class IntegerToUnsignedString {
|
||||
@BeforeTemplate
|
||||
String before(int i) {
|
||||
return Refaster.anyOf(UnsignedInts.toString(i), Integer.toUnsignedString(i, 10));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(int i) {
|
||||
return Integer.toUnsignedString(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer JDK's {@link Long#toUnsignedString(long)} over third-party or more verbose alternatives.
|
||||
*/
|
||||
static final class LongToUnsignedString {
|
||||
@BeforeTemplate
|
||||
String before(long i) {
|
||||
return Refaster.anyOf(UnsignedLongs.toString(i), Long.toUnsignedString(i, 10));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(long i) {
|
||||
return Long.toUnsignedString(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer JDK's {@link Integer#toUnsignedString(int,int)} over third-party or more verbose
|
||||
* alternatives.
|
||||
*/
|
||||
static final class IntegerToUnsignedStringWithRadix {
|
||||
@BeforeTemplate
|
||||
String before(int i, int radix) {
|
||||
return UnsignedInts.toString(i, radix);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(int i, int radix) {
|
||||
return Integer.toUnsignedString(i, radix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer JDK's {@link Long#toUnsignedString(long,int)} over third-party or more verbose
|
||||
* alternatives.
|
||||
*/
|
||||
static final class LongToUnsignedStringWithRadix {
|
||||
@BeforeTemplate
|
||||
String before(long i, int radix) {
|
||||
return UnsignedLongs.toString(i, radix);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(long i, int radix) {
|
||||
return Long.toUnsignedString(i, radix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import static java.util.stream.Collectors.toCollection;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static reactor.function.TupleUtils.function;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -34,7 +33,6 @@ 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;
|
||||
@@ -42,7 +40,6 @@ 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;
|
||||
@@ -483,20 +480,15 @@ final class ReactorRules {
|
||||
}
|
||||
|
||||
/** Prefer {@link Flux#just(Object)} over more contrived alternatives. */
|
||||
static final class FluxJust<T> {
|
||||
static final class FluxJust {
|
||||
@BeforeTemplate
|
||||
Flux<Integer> before(int value) {
|
||||
return Flux.range(value, 1);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
Flux<T> before(T value) {
|
||||
return Mono.just(value).repeat().take(1);
|
||||
Flux<Integer> before(int start) {
|
||||
return Flux.range(start, 1);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Flux<T> after(T value) {
|
||||
return Flux.just(value);
|
||||
Flux<Integer> after(int start) {
|
||||
return Flux.just(start);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1206,17 +1198,10 @@ 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<T> {
|
||||
// XXX: Once the `MethodReferenceUsage` check is generally enabled, drop the second
|
||||
// `Refaster.anyOf` variant.
|
||||
@BeforeTemplate
|
||||
Flux<T> before(Collection<T> collection) {
|
||||
return Flux.fromStream(
|
||||
Refaster.<Supplier<Stream<? extends T>>>anyOf(
|
||||
collection::stream, () -> collection.stream()));
|
||||
return Flux.fromStream(collection.stream());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@@ -1742,91 +1727,6 @@ final class ReactorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link PublisherProbe#assertWasSubscribed()} over more verbose alternatives. */
|
||||
static final class PublisherProbeAssertWasSubscribed<T> {
|
||||
@BeforeTemplate
|
||||
void before(PublisherProbe<T> probe) {
|
||||
Refaster.anyOf(
|
||||
assertThat(probe.wasSubscribed()).isTrue(),
|
||||
assertThat(probe.subscribeCount()).isNotNegative(),
|
||||
assertThat(probe.subscribeCount()).isNotEqualTo(0),
|
||||
assertThat(probe.subscribeCount()).isPositive());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
void after(PublisherProbe<T> probe) {
|
||||
probe.assertWasSubscribed();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link PublisherProbe#assertWasNotSubscribed()} over more verbose alternatives. */
|
||||
static final class PublisherProbeAssertWasNotSubscribed<T> {
|
||||
@BeforeTemplate
|
||||
void before(PublisherProbe<T> probe) {
|
||||
Refaster.anyOf(
|
||||
assertThat(probe.wasSubscribed()).isFalse(),
|
||||
assertThat(probe.subscribeCount()).isEqualTo(0),
|
||||
assertThat(probe.subscribeCount()).isNotPositive());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
void after(PublisherProbe<T> probe) {
|
||||
probe.assertWasNotSubscribed();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link PublisherProbe#assertWasCancelled()} over more verbose alternatives. */
|
||||
static final class PublisherProbeAssertWasCancelled<T> {
|
||||
@BeforeTemplate
|
||||
void before(PublisherProbe<T> probe) {
|
||||
assertThat(probe.wasCancelled()).isTrue();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
void after(PublisherProbe<T> probe) {
|
||||
probe.assertWasCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link PublisherProbe#assertWasNotCancelled()} over more verbose alternatives. */
|
||||
static final class PublisherProbeAssertWasNotCancelled<T> {
|
||||
@BeforeTemplate
|
||||
void before(PublisherProbe<T> probe) {
|
||||
assertThat(probe.wasCancelled()).isFalse();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
void after(PublisherProbe<T> probe) {
|
||||
probe.assertWasNotCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link PublisherProbe#assertWasRequested()} over more verbose alternatives. */
|
||||
static final class PublisherProbeAssertWasRequested<T> {
|
||||
@BeforeTemplate
|
||||
void before(PublisherProbe<T> probe) {
|
||||
assertThat(probe.wasRequested()).isTrue();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
void after(PublisherProbe<T> probe) {
|
||||
probe.assertWasRequested();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link PublisherProbe#assertWasNotRequested()} over more verbose alternatives. */
|
||||
static final class PublisherProbeAssertWasNotRequested<T> {
|
||||
@BeforeTemplate
|
||||
void before(PublisherProbe<T> probe) {
|
||||
assertThat(probe.wasRequested()).isFalse();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
void after(PublisherProbe<T> probe) {
|
||||
probe.assertWasNotRequested();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Mono#as(Function)} when creating a {@link StepVerifier}. */
|
||||
static final class StepVerifierFromMono<T> {
|
||||
@BeforeTemplate
|
||||
@@ -1853,60 +1753,6 @@ final class ReactorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link StepVerifier#verify()} over a dangling {@link
|
||||
* StepVerifier#verifyThenAssertThat()}.
|
||||
*/
|
||||
// XXX: Application of this rule (and several others in this class) will cause invalid code if the
|
||||
// result of the rewritten expression is dereferenced. Consider introducing a bug checker that
|
||||
// identifies rules that change the return type of an expression and annotates them accordingly.
|
||||
// The associated annotation can then be used to instruct an annotation processor to generate
|
||||
// corresponding `void` rules that match only statements. This would allow the `Refaster` check to
|
||||
// conditionally skip "not fully safe" rules. This allows conditionally flagging more dubious
|
||||
// code, at the risk of compilation failures. With this rule, for example, we want to explicitly
|
||||
// nudge users towards `StepVerifier.Step#assertNext(Consumer)` or
|
||||
// `StepVerifier.Step#expectNext(Object)`, together with `Step#verifyComplete()`.
|
||||
static final class StepVerifierVerify {
|
||||
@BeforeTemplate
|
||||
StepVerifier.Assertions before(StepVerifier stepVerifier) {
|
||||
return stepVerifier.verifyThenAssertThat();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Duration after(StepVerifier stepVerifier) {
|
||||
return stepVerifier.verify();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link StepVerifier#verify(Duration)} over a dangling {@link
|
||||
* StepVerifier#verifyThenAssertThat(Duration)}.
|
||||
*/
|
||||
static final class StepVerifierVerifyDuration {
|
||||
@BeforeTemplate
|
||||
StepVerifier.Assertions before(StepVerifier stepVerifier, Duration duration) {
|
||||
return stepVerifier.verifyThenAssertThat(duration);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Duration after(StepVerifier stepVerifier, Duration duration) {
|
||||
return stepVerifier.verify(duration);
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily invoke {@link StepVerifier#verifyLater()} multiple times. */
|
||||
static final class StepVerifierVerifyLater {
|
||||
@BeforeTemplate
|
||||
StepVerifier before(StepVerifier stepVerifier) {
|
||||
return stepVerifier.verifyLater().verifyLater();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
StepVerifier after(StepVerifier stepVerifier) {
|
||||
return stepVerifier.verifyLater();
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily have {@link StepVerifier.Step} expect no elements. */
|
||||
static final class StepVerifierStepIdentity<T> {
|
||||
@BeforeTemplate
|
||||
@@ -2007,12 +1853,6 @@ final class ReactorRules {
|
||||
return step.expectErrorMatches(predicate).verify();
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("StepVerifierVerify" /* This is a more specific template. */)
|
||||
StepVerifier.Assertions before2(StepVerifier.LastStep step, Predicate<Throwable> predicate) {
|
||||
return step.expectError().verifyThenAssertThat().hasOperatorErrorMatching(predicate);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Duration after(StepVerifier.LastStep step, Predicate<Throwable> predicate) {
|
||||
return step.verifyErrorMatches(predicate);
|
||||
@@ -2035,30 +1875,6 @@ final class ReactorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link StepVerifier.LastStep#verifyErrorSatisfies(Consumer)} with AssertJ over more
|
||||
* contrived alternatives.
|
||||
*/
|
||||
static final class StepVerifierLastStepVerifyErrorSatisfiesAssertJ<T extends Throwable> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("StepVerifierVerify" /* This is a more specific template. */)
|
||||
StepVerifier.Assertions before(StepVerifier.LastStep step, Class<T> clazz, String message) {
|
||||
return Refaster.anyOf(
|
||||
step.expectError()
|
||||
.verifyThenAssertThat()
|
||||
.hasOperatorErrorOfType(clazz)
|
||||
.hasOperatorErrorWithMessage(message),
|
||||
step.expectError(clazz).verifyThenAssertThat().hasOperatorErrorWithMessage(message),
|
||||
step.expectErrorMessage(message).verifyThenAssertThat().hasOperatorErrorOfType(clazz));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
Duration after(StepVerifier.LastStep step, Class<T> clazz, String message) {
|
||||
return step.verifyErrorSatisfies(t -> assertThat(t).isInstanceOf(clazz).hasMessage(message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link StepVerifier.LastStep#verifyErrorMessage(String)} over more verbose alternatives.
|
||||
*/
|
||||
@@ -2088,76 +1904,4 @@ 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<T> {
|
||||
// XXX: Constrain the `future` parameter using `@NotMatches(IsIdentityOperation.class)` once
|
||||
// `IsIdentityOperation` no longer matches nullary method invocations.
|
||||
@BeforeTemplate
|
||||
Mono<T> before(CompletableFuture<T> future) {
|
||||
return Mono.fromFuture(future);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<T> after(CompletableFuture<T> 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<T> {
|
||||
// XXX: Constrain the `future` parameter using `@NotMatches(IsIdentityOperation.class)` once
|
||||
// `IsIdentityOperation` no longer matches nullary method invocations.
|
||||
@BeforeTemplate
|
||||
Mono<T> before(CompletableFuture<T> future, boolean suppressCancel) {
|
||||
return Mono.fromFuture(future, suppressCancel);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<T> after(CompletableFuture<T> future, boolean suppressCancel) {
|
||||
return Mono.fromFuture(() -> future, suppressCancel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't propagate {@link Mono} cancellations to an upstream cache value computation, as
|
||||
* completion of such computations may benefit concurrent or subsequent cache usages.
|
||||
*/
|
||||
static final class MonoFromFutureAsyncLoadingCacheGet<K, V> {
|
||||
@BeforeTemplate
|
||||
Mono<V> before(AsyncLoadingCache<K, V> cache, K key) {
|
||||
return Mono.fromFuture(() -> cache.get(key));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<V> after(AsyncLoadingCache<K, V> cache, K key) {
|
||||
return Mono.fromFuture(() -> cache.get(key), /* suppressCancel= */ true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Flux#fromStream(Supplier)} over {@link Flux#fromStream(Stream)}, as the former
|
||||
* yields a {@link Flux} that is more likely to behave as expected when subscribed to more than
|
||||
* once.
|
||||
*/
|
||||
static final class FluxFromStreamSupplier<T> {
|
||||
// XXX: Constrain the `stream` parameter using `@NotMatches(IsIdentityOperation.class)` once
|
||||
// `IsIdentityOperation` no longer matches nullary method invocations.
|
||||
@BeforeTemplate
|
||||
Flux<T> before(Stream<T> stream) {
|
||||
return Flux.fromStream(stream);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Flux<T> after(Stream<T> stream) {
|
||||
return Flux.fromStream(() -> stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,22 +297,6 @@ final class StreamRules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer an unconditional {@link Map#get(Object)} call followed by a {@code null} check over a
|
||||
* call to {@link Map#containsKey(Object)}, as the former avoids a second lookup operation.
|
||||
*/
|
||||
static final class StreamMapFilter<T, K, V> {
|
||||
@BeforeTemplate
|
||||
Stream<V> before(Stream<T> stream, Map<K, V> map) {
|
||||
return stream.filter(map::containsKey).map(map::get);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Stream<V> after(Stream<T> stream, Map<K, V> map) {
|
||||
return stream.map(map::get).filter(Objects::nonNull);
|
||||
}
|
||||
}
|
||||
|
||||
static final class StreamMin<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
|
||||
|
||||
@@ -29,14 +29,11 @@ final class StringRules {
|
||||
private StringRules() {}
|
||||
|
||||
/** Prefer {@link String#isEmpty()} over alternatives that consult the string's length. */
|
||||
// XXX: Drop this rule once we (and OpenRewrite) no longer support projects targeting Java 14 or
|
||||
// below. The `CharSequenceIsEmpty` rule then suffices. (This rule exists so that e.g. projects
|
||||
// that target JDK 11 can disable `CharSequenceIsEmpty` without losing a valuable rule.)
|
||||
// XXX: Look into a more general approach to supporting different Java language levels, such as
|
||||
// rule selection based on some annotation, or a separate Maven module.
|
||||
// XXX: Now that we build with JDK 15+, this rule can be generalized to cover all `CharSequence`
|
||||
// subtypes. This does require a mechanism (perhaps an annotation, or a separate Maven module) to
|
||||
// make sure that non-String expressions are rewritten only if client code also targets JDK 15+.
|
||||
static final class StringIsEmpty {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("CharSequenceIsEmpty" /* This is a more specific template. */)
|
||||
boolean before(String str) {
|
||||
return Refaster.anyOf(str.length() == 0, str.length() <= 0, str.length() < 1);
|
||||
}
|
||||
@@ -247,105 +244,4 @@ final class StringRules {
|
||||
return Utf8.encodedLength(str);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link String#indexOf(int, int)} over less efficient alternatives. */
|
||||
static final class StringIndexOfChar {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
|
||||
int before(String string, int ch, int fromIndex) {
|
||||
return string.substring(fromIndex).indexOf(ch);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(String string, int ch, int fromIndex) {
|
||||
return Math.max(-1, string.indexOf(ch, fromIndex) - fromIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link String#indexOf(String, int)} over less efficient alternatives. */
|
||||
static final class StringIndexOfString {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
|
||||
int before(String string, String substring, int fromIndex) {
|
||||
return string.substring(fromIndex).indexOf(substring);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(String string, String substring, int fromIndex) {
|
||||
return Math.max(-1, string.indexOf(substring, fromIndex) - fromIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Once we compile Refaster templates with JDK 21 also suggest `String#indexOf(int, int,
|
||||
// int)` and `String#indexOf(String, int, int)`.
|
||||
|
||||
/** Prefer {@link String#lastIndexOf(int, int)} over less efficient alternatives. */
|
||||
static final class StringLastIndexOfChar {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
|
||||
int before(String string, int ch, int fromIndex) {
|
||||
return string.substring(fromIndex).lastIndexOf(ch);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(String string, int ch, int fromIndex) {
|
||||
return Math.max(-1, string.lastIndexOf(ch) - fromIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link String#lastIndexOf(String, int)} over less efficient alternatives. */
|
||||
static final class StringLastIndexOfString {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
|
||||
int before(String string, String substring, int fromIndex) {
|
||||
return string.substring(fromIndex).lastIndexOf(substring);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(String string, String substring, int fromIndex) {
|
||||
return Math.max(-1, string.lastIndexOf(substring) - fromIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link String#lastIndexOf(int, int)} over less efficient alternatives. */
|
||||
static final class StringLastIndexOfCharWithIndex {
|
||||
@BeforeTemplate
|
||||
int before(String string, int ch, int fromIndex) {
|
||||
return string.substring(0, fromIndex).lastIndexOf(ch);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(String string, int ch, int fromIndex) {
|
||||
return string.lastIndexOf(ch, fromIndex - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link String#lastIndexOf(String, int)} over less efficient alternatives. */
|
||||
// XXX: The replacement expression isn't fully equivalent: in case `substring` is empty, then
|
||||
// the replacement yields `fromIndex - 1` rather than `fromIndex`.
|
||||
static final class StringLastIndexOfStringWithIndex {
|
||||
@BeforeTemplate
|
||||
int before(String string, String substring, int fromIndex) {
|
||||
return string.substring(0, fromIndex).lastIndexOf(substring);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
int after(String string, String substring, int fromIndex) {
|
||||
return string.lastIndexOf(substring, fromIndex - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link String#startsWith(String, int)} over less efficient alternatives. */
|
||||
static final class StringStartsWith {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
|
||||
boolean before(String string, String prefix, int fromIndex) {
|
||||
return string.substring(fromIndex).startsWith(prefix);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
boolean after(String string, String prefix, int fromIndex) {
|
||||
return string.startsWith(prefix, fromIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,9 +161,8 @@ final class TestNGToAssertJRules {
|
||||
|
||||
@AfterTemplate
|
||||
@DoNotCall
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after() {
|
||||
fail();
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,63 +142,6 @@ final class TimeRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily transform an {@link Instant} to an equivalent instance. */
|
||||
static final class InstantIdentity {
|
||||
@BeforeTemplate
|
||||
Instant before(Instant instant, TemporalUnit temporalUnit) {
|
||||
return Refaster.anyOf(
|
||||
instant.plus(Duration.ZERO),
|
||||
instant.plus(0, temporalUnit),
|
||||
instant.plusNanos(0),
|
||||
instant.plusMillis(0),
|
||||
instant.plusSeconds(0),
|
||||
instant.minus(Duration.ZERO),
|
||||
instant.minus(0, temporalUnit),
|
||||
instant.minusNanos(0),
|
||||
instant.minusMillis(0),
|
||||
instant.minusSeconds(0),
|
||||
Instant.parse(instant.toString()),
|
||||
instant.truncatedTo(ChronoUnit.NANOS),
|
||||
Instant.ofEpochSecond(instant.getEpochSecond(), instant.getNano()));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Instant after(Instant instant) {
|
||||
return instant;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Instant#truncatedTo(TemporalUnit)} over less obvious alternatives.
|
||||
*
|
||||
* <p>Note that {@link Instant#toEpochMilli()} throws an {@link ArithmeticException} for dates
|
||||
* very far in the past or future, while the suggested alternative doesn't.
|
||||
*/
|
||||
static final class InstantTruncatedToMilliseconds {
|
||||
@BeforeTemplate
|
||||
Instant before(Instant instant) {
|
||||
return Instant.ofEpochMilli(instant.toEpochMilli());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Instant after(Instant instant) {
|
||||
return instant.truncatedTo(ChronoUnit.MILLIS);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Instant#truncatedTo(TemporalUnit)} over less obvious alternatives. */
|
||||
static final class InstantTruncatedToSeconds {
|
||||
@BeforeTemplate
|
||||
Instant before(Instant instant) {
|
||||
return Instant.ofEpochSecond(instant.getEpochSecond());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Instant after(Instant instant) {
|
||||
return instant.truncatedTo(ChronoUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Instant#atOffset(ZoneOffset)} over more verbose alternatives. */
|
||||
static final class InstantAtOffset {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
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 ClassCastLambdaUsageTest {
|
||||
@Test
|
||||
void identification() {
|
||||
CompilationTestHelper.newInstance(ClassCastLambdaUsage.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import java.util.stream.IntStream;",
|
||||
"import java.util.stream.Stream;",
|
||||
"",
|
||||
"class A {",
|
||||
" <T> void m() {",
|
||||
" Number localVariable = 0;",
|
||||
"",
|
||||
" Stream.of(0).map(i -> i);",
|
||||
" Stream.of(1).map(i -> i + 1);",
|
||||
" Stream.of(2).map(Integer.class::cast);",
|
||||
" Stream.of(3).map(i -> (Integer) 2);",
|
||||
" Stream.of(4).map(i -> (Integer) localVariable);",
|
||||
" // XXX: Ideally this case is also flagged. Pick this up in the context of merging the",
|
||||
" // `ClassCastLambdaUsage` and `MethodReferenceUsage` checks, or introduce a separate check that",
|
||||
" // simplifies unnecessary block lambda expressions.",
|
||||
" Stream.of(5)",
|
||||
" .map(",
|
||||
" i -> {",
|
||||
" return (Integer) i;",
|
||||
" });",
|
||||
" Stream.<ImmutableSet>of(ImmutableSet.of(6)).map(s -> (ImmutableSet<Number>) s);",
|
||||
" Stream.of(ImmutableSet.of(7)).map(s -> (ImmutableSet<?>) s);",
|
||||
" Stream.of(8).reduce((a, b) -> (Integer) a);",
|
||||
" IntStream.of(9).mapToObj(i -> (char) i);",
|
||||
" Stream.of(10).map(i -> (T) i);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Stream.of(11).map(i -> (Integer) i);",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
BugCheckerRefactoringTestHelper.newInstance(ClassCastLambdaUsage.class, getClass())
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import java.util.stream.Stream;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() {",
|
||||
" Stream.of(1).map(i -> (Integer) i);",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import java.util.stream.Stream;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() {",
|
||||
" Stream.of(1).map(Integer.class::cast);",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
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 ConstantNamingTest {
|
||||
@Test
|
||||
void identification() {
|
||||
CompilationTestHelper.newInstance(ConstantNaming.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"class A {",
|
||||
" private static final long serialVersionUID = 1L;",
|
||||
" private static final int FOO = 1;",
|
||||
" // BUG: Diagnostic contains: consider renaming to 'BAR', though note that this is not a private",
|
||||
" // constant",
|
||||
" static final int bar = 2;",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private static final int baz = 3;",
|
||||
" // BUG: Diagnostic contains: consider renaming to 'QUX_QUUX', though note that a variable with",
|
||||
" // this name is already declared",
|
||||
" private static final int qux_QUUX = 4;",
|
||||
" // BUG: Diagnostic contains: consider renaming to 'QUUZ', though note that a variable with",
|
||||
" // this name is already declared",
|
||||
" private static final int quuz = 3;",
|
||||
"",
|
||||
" private final int foo = 4;",
|
||||
" private final Runnable QUX_QUUX =",
|
||||
" new Runnable() {",
|
||||
" private static final int QUUZ = 1;",
|
||||
"",
|
||||
" @Override",
|
||||
" public void run() {}",
|
||||
" };",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void identificationWithCustomExemption() {
|
||||
CompilationTestHelper.newInstance(ConstantNaming.class, getClass())
|
||||
.setArgs("-XepOpt:CanonicalConstantNaming:ExemptedNames=foo,baz")
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"class A {",
|
||||
" private static final long serialVersionUID = 1L;",
|
||||
" private static final int foo = 1;",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private static final int bar = 2;",
|
||||
" private static final int baz = 3;",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
BugCheckerRefactoringTestHelper.newInstance(ConstantNaming.class, getClass())
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"class A {",
|
||||
" static final int foo = 1;",
|
||||
" private static final int bar = 2;",
|
||||
" private static final int baz = 3;",
|
||||
" private static final int BAZ = 4;",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"class A {",
|
||||
" static final int foo = 1;",
|
||||
" private static final int BAR = 2;",
|
||||
" private static final int baz = 3;",
|
||||
" private static final int BAZ = 4;",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
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 ExplicitArgumentEnumerationTest {
|
||||
@Test
|
||||
void identification() {
|
||||
CompilationTestHelper.newInstance(ExplicitArgumentEnumeration.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static org.assertj.core.api.Assertions.assertThat;",
|
||||
"",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"import org.jooq.impl.DSL;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.test.StepVerifier;",
|
||||
"",
|
||||
"class A {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private final int value = unaryMethod(ImmutableList.of(1, 2));",
|
||||
"",
|
||||
" void m() {",
|
||||
" ImmutableList<String> list = ImmutableList.of();",
|
||||
" assertThat(ImmutableList.of()).containsAnyElementsOf(list);",
|
||||
"",
|
||||
" ImmutableList.<ImmutableList<String>>builder().add(ImmutableList.of());",
|
||||
"",
|
||||
" DSL.row(ImmutableList.of(1, 2));",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" unaryMethod(ImmutableList.of(1, 2));",
|
||||
" unaryMethodWithLessVisibleOverload(ImmutableList.of(1, 2));",
|
||||
" binaryMethod(ImmutableList.of(1, 2), 3);",
|
||||
"",
|
||||
" ImmutableList.builder()",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" .addAll(ImmutableList.of())",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" .addAll(ImmutableList.copyOf(new String[0]))",
|
||||
" .addAll(ImmutableList.copyOf(ImmutableList.of()))",
|
||||
" .build();",
|
||||
"",
|
||||
" assertThat(ImmutableList.of(1))",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" .containsAnyElementsOf(ImmutableList.of(1))",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" .isSubsetOf(ImmutableList.of(1));",
|
||||
"",
|
||||
" Flux.just(1, 2)",
|
||||
" .as(StepVerifier::create)",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" .expectNextSequence(ImmutableList.of(1, 2))",
|
||||
" .verifyComplete();",
|
||||
"",
|
||||
" CompilationTestHelper.newInstance(BugChecker.class, getClass())",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" .setArgs(ImmutableList.of(\"foo\"))",
|
||||
" .withClasspath();",
|
||||
" }",
|
||||
"",
|
||||
" private int unaryMethod(ImmutableList<Integer> args) {",
|
||||
" return 0;",
|
||||
" }",
|
||||
"",
|
||||
" private int unaryMethod(Integer... args) {",
|
||||
" return unaryMethod(ImmutableList.copyOf(args));",
|
||||
" }",
|
||||
"",
|
||||
" void unaryMethodWithLessVisibleOverload(ImmutableList<Integer> args) {}",
|
||||
"",
|
||||
" private void unaryMethodWithLessVisibleOverload(Integer... args) {",
|
||||
" unaryMethodWithLessVisibleOverload(ImmutableList.copyOf(args));",
|
||||
" }",
|
||||
"",
|
||||
" private void binaryMethod(ImmutableList<Integer> args, int extraArg) {}",
|
||||
"",
|
||||
" private void binaryMethod(Integer... args) {",
|
||||
" binaryMethod(ImmutableList.copyOf(args), 0);",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
BugCheckerRefactoringTestHelper.newInstance(ExplicitArgumentEnumeration.class, getClass())
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import static org.assertj.core.api.Assertions.assertThat;",
|
||||
"",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableMultiset;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"import java.util.Arrays;",
|
||||
"import java.util.List;",
|
||||
"import java.util.Set;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.test.StepVerifier;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() {",
|
||||
" ImmutableList.builder().addAll(ImmutableList.of()).build();",
|
||||
"",
|
||||
" assertThat(ImmutableList.of()).containsAnyElementsOf(ImmutableMultiset.of());",
|
||||
" assertThat(ImmutableList.of()).containsAll(ImmutableSet.of());",
|
||||
" assertThat(ImmutableList.of()).containsExactlyElementsOf(List.of());",
|
||||
" assertThat(ImmutableList.of()).containsExactlyInAnyOrderElementsOf(Set.of());",
|
||||
" assertThat(ImmutableList.of()).containsSequence(Arrays.asList());",
|
||||
" assertThat(ImmutableList.of()).containsSubsequence(ImmutableList.of(1));",
|
||||
" assertThat(ImmutableList.of()).doesNotContainAnyElementsOf(ImmutableMultiset.of(2));",
|
||||
" assertThat(ImmutableList.of()).doesNotContainSequence(ImmutableSet.of(3));",
|
||||
" assertThat(ImmutableList.of()).doesNotContainSubsequence(List.of(4));",
|
||||
" assertThat(ImmutableList.of()).hasSameElementsAs(Set.of(5));",
|
||||
" assertThat(ImmutableList.of()).isSubsetOf(Arrays.asList(6));",
|
||||
"",
|
||||
" Flux.empty()",
|
||||
" .as(StepVerifier::create)",
|
||||
" .expectNextSequence(ImmutableList.of(1, 2))",
|
||||
" .verifyComplete();",
|
||||
"",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(BugChecker.class, getClass())",
|
||||
" .setArgs(ImmutableList.of(\"foo\", \"bar\"));",
|
||||
" CompilationTestHelper.newInstance(BugChecker.class, getClass())",
|
||||
" .setArgs(ImmutableList.of(\"foo\", \"bar\"));",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import static org.assertj.core.api.Assertions.assertThat;",
|
||||
"",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableMultiset;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"import java.util.Arrays;",
|
||||
"import java.util.List;",
|
||||
"import java.util.Set;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.test.StepVerifier;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() {",
|
||||
" ImmutableList.builder().add().build();",
|
||||
"",
|
||||
" assertThat(ImmutableList.of()).containsAnyOf();",
|
||||
" assertThat(ImmutableList.of()).contains();",
|
||||
" assertThat(ImmutableList.of()).containsExactly();",
|
||||
" assertThat(ImmutableList.of()).containsExactlyInAnyOrder();",
|
||||
" assertThat(ImmutableList.of()).containsSequence();",
|
||||
" assertThat(ImmutableList.of()).containsSubsequence(1);",
|
||||
" assertThat(ImmutableList.of()).doesNotContain(2);",
|
||||
" assertThat(ImmutableList.of()).doesNotContainSequence(3);",
|
||||
" assertThat(ImmutableList.of()).doesNotContainSubsequence(4);",
|
||||
" assertThat(ImmutableList.of()).containsOnly(5);",
|
||||
" assertThat(ImmutableList.of()).isSubsetOf(6);",
|
||||
"",
|
||||
" Flux.empty().as(StepVerifier::create).expectNext(1, 2).verifyComplete();",
|
||||
"",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(BugChecker.class, getClass()).setArgs(\"foo\", \"bar\");",
|
||||
" CompilationTestHelper.newInstance(BugChecker.class, getClass()).setArgs(\"foo\", \"bar\");",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,6 @@ final class IdentityConversionTest {
|
||||
"import com.google.common.collect.ImmutableTable;",
|
||||
"import com.google.errorprone.matchers.Matcher;",
|
||||
"import com.google.errorprone.matchers.Matchers;",
|
||||
"import java.time.Instant;",
|
||||
"import java.time.ZonedDateTime;",
|
||||
"import reactor.adapter.rxjava.RxJava2Adapter;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
@@ -151,10 +149,6 @@ final class IdentityConversionTest {
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableTable<Object, Object, Object> o11 = ImmutableTable.copyOf(ImmutableTable.of());",
|
||||
"",
|
||||
" Instant instant1 = Instant.from(ZonedDateTime.now());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Instant instant2 = Instant.from(Instant.now());",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Matcher allOf1 = Matchers.allOf(instanceMethod());",
|
||||
" Matcher allOf2 = Matchers.allOf(instanceMethod(), staticMethod());",
|
||||
|
||||
@@ -18,23 +18,22 @@ final class IsInstanceLambdaUsageTest {
|
||||
" void m() {",
|
||||
" Integer localVariable = 0;",
|
||||
"",
|
||||
" Stream.of(0).map(i -> i);",
|
||||
" Stream.of(1).map(i -> i + 1);",
|
||||
" Stream.of(2).filter(Integer.class::isInstance);",
|
||||
" Stream.of(3).filter(i -> i.getClass() instanceof Class);",
|
||||
" Stream.of(4).filter(i -> localVariable instanceof Integer);",
|
||||
" Stream.of(0).map(i -> i + 1);",
|
||||
" Stream.of(1).filter(Integer.class::isInstance);",
|
||||
" Stream.of(2).filter(i -> i.getClass() instanceof Class);",
|
||||
" Stream.of(3).filter(i -> localVariable instanceof Integer);",
|
||||
" // XXX: Ideally this case is also flagged. Pick this up in the context of merging the",
|
||||
" // `IsInstanceLambdaUsage` and `MethodReferenceUsage` checks, or introduce a separate check that",
|
||||
" // simplifies unnecessary block lambda expressions.",
|
||||
" Stream.of(5)",
|
||||
" Stream.of(4)",
|
||||
" .filter(",
|
||||
" i -> {",
|
||||
" return i instanceof Integer;",
|
||||
" return localVariable instanceof Integer;",
|
||||
" });",
|
||||
" Flux.just(6, \"foo\").distinctUntilChanged(v -> v, (a, b) -> a instanceof Integer);",
|
||||
" Flux.just(5, \"foo\").distinctUntilChanged(v -> v, (a, b) -> a instanceof Integer);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Stream.of(7).filter(i -> i instanceof Integer);",
|
||||
" Stream.of(6).filter(i -> i instanceof Integer);",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
|
||||
@@ -11,7 +11,7 @@ final class JUnitMethodDeclarationTest {
|
||||
CompilationTestHelper.newInstance(JUnitMethodDeclaration.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.*;",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import org.junit.jupiter.api.AfterAll;",
|
||||
"import org.junit.jupiter.api.AfterEach;",
|
||||
@@ -154,10 +154,8 @@ final class JUnitMethodDeclarationTest {
|
||||
" void overload() {}",
|
||||
"",
|
||||
" @Test",
|
||||
" // BUG: Diagnostic contains: (but note that another method named `arguments` is in scope)",
|
||||
" void testArguments() {",
|
||||
" arguments();",
|
||||
" }",
|
||||
" // BUG: Diagnostic contains: (but note that `arguments` is already statically imported)",
|
||||
" void testArguments() {}",
|
||||
"",
|
||||
" @Test",
|
||||
" // BUG: Diagnostic contains: (but note that `public` is not a valid identifier)",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
@@ -28,10 +29,6 @@ final class LexicographicalAnnotationAttributeListingTest {
|
||||
" @interface Foo {",
|
||||
" String[] value() default {};",
|
||||
"",
|
||||
" boolean[] bools() default {};",
|
||||
"",
|
||||
" char[] chars() default {};",
|
||||
"",
|
||||
" int[] ints() default {};",
|
||||
"",
|
||||
" Class<?>[] cls() default {};",
|
||||
@@ -72,32 +69,6 @@ final class LexicographicalAnnotationAttributeListingTest {
|
||||
" @Foo({\"a\", \"A\"})",
|
||||
" A unsortedStringCaseInsensitiveWithTotalOrderFallback();",
|
||||
"",
|
||||
" @Foo(bools = {})",
|
||||
" A noBools();",
|
||||
"",
|
||||
" @Foo(bools = {false})",
|
||||
" A oneBool();",
|
||||
"",
|
||||
" @Foo(bools = {false, true})",
|
||||
" A sortedBools();",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @Foo(bools = {true, false})",
|
||||
" A unsortedBools();",
|
||||
"",
|
||||
" @Foo(chars = {})",
|
||||
" A noChars();",
|
||||
"",
|
||||
" @Foo(chars = {'a'})",
|
||||
" A oneChar();",
|
||||
"",
|
||||
" @Foo(chars = {'a', 'b'})",
|
||||
" A sortedChars();",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @Foo(chars = {'b', 'a'})",
|
||||
" A unsortedChars();",
|
||||
"",
|
||||
" @Foo(ints = {})",
|
||||
" A noInts();",
|
||||
"",
|
||||
@@ -202,10 +173,6 @@ final class LexicographicalAnnotationAttributeListingTest {
|
||||
" @interface Foo {",
|
||||
" String[] value() default {};",
|
||||
"",
|
||||
" boolean[] bools() default {};",
|
||||
"",
|
||||
" char[] chars() default {};",
|
||||
"",
|
||||
" Class<?>[] cls() default {};",
|
||||
"",
|
||||
" RoundingMode[] enums() default {};",
|
||||
@@ -218,13 +185,7 @@ final class LexicographicalAnnotationAttributeListingTest {
|
||||
" }",
|
||||
"",
|
||||
" @Foo({\" \", \"\", \"b\", \"a\"})",
|
||||
" A unsortedStrings();",
|
||||
"",
|
||||
" @Foo(bools = {true, false})",
|
||||
" A unsortedBooleans();",
|
||||
"",
|
||||
" @Foo(chars = {'b', 'a'})",
|
||||
" A unsortedChars();",
|
||||
" A unsortedString();",
|
||||
"",
|
||||
" @Foo(cls = {long.class, int.class})",
|
||||
" A unsortedClasses();",
|
||||
@@ -249,10 +210,6 @@ final class LexicographicalAnnotationAttributeListingTest {
|
||||
" @interface Foo {",
|
||||
" String[] value() default {};",
|
||||
"",
|
||||
" boolean[] bools() default {};",
|
||||
"",
|
||||
" char[] chars() default {};",
|
||||
"",
|
||||
" Class<?>[] cls() default {};",
|
||||
"",
|
||||
" RoundingMode[] enums() default {};",
|
||||
@@ -265,13 +222,7 @@ final class LexicographicalAnnotationAttributeListingTest {
|
||||
" }",
|
||||
"",
|
||||
" @Foo({\"\", \" \", \"a\", \"b\"})",
|
||||
" A unsortedStrings();",
|
||||
"",
|
||||
" @Foo(bools = {false, true})",
|
||||
" A unsortedBooleans();",
|
||||
"",
|
||||
" @Foo(chars = {'a', 'b'})",
|
||||
" A unsortedChars();",
|
||||
" A unsortedString();",
|
||||
"",
|
||||
" @Foo(cls = {int.class, long.class})",
|
||||
" A unsortedClasses();",
|
||||
@@ -293,8 +244,9 @@ final class LexicographicalAnnotationAttributeListingTest {
|
||||
/* Some violations are not flagged because they are not in- or excluded. */
|
||||
CompilationTestHelper.newInstance(LexicographicalAnnotationAttributeListing.class, getClass())
|
||||
.setArgs(
|
||||
"-XepOpt:LexicographicalAnnotationAttributeListing:Includes=pkg.A.Foo,pkg.A.Bar",
|
||||
"-XepOpt:LexicographicalAnnotationAttributeListing:Excludes=pkg.A.Bar#value")
|
||||
ImmutableList.of(
|
||||
"-XepOpt:LexicographicalAnnotationAttributeListing:Includes=pkg.A.Foo,pkg.A.Bar",
|
||||
"-XepOpt:LexicographicalAnnotationAttributeListing:Excludes=pkg.A.Bar#value"))
|
||||
.addSourceLines(
|
||||
"pkg/A.java",
|
||||
"package pkg;",
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class ModernizerTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(Modernizer.class, getClass());
|
||||
|
||||
// XXX: Also add calls that should not be flagged.
|
||||
// XXX: Test extension, field references, instance methods, static methods.
|
||||
// methods with primitives, primitive arrays, references, reference arrays
|
||||
// zero, one two args.
|
||||
// XXX: Also test constructors!
|
||||
// XXX: Test that the appropriate "prefer" message is emitted.
|
||||
// XXX: List the test cases in `ModernizerTest`?
|
||||
|
||||
@Test
|
||||
void fieldIdentification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static com.google.common.base.Charsets.ISO_8859_1;",
|
||||
"",
|
||||
"import com.google.common.base.Charsets;",
|
||||
"import java.nio.charset.StandardCharsets;",
|
||||
"",
|
||||
"class A {",
|
||||
" {",
|
||||
" // BUG: Diagnostic contains: Prefer java.nio.charset.StandardCharsets",
|
||||
" Object o1 = ISO_8859_1;",
|
||||
" // BUG: Diagnostic contains: Prefer java.nio.charset.StandardCharsets",
|
||||
" Object o2 = Charsets.ISO_8859_1;",
|
||||
" // BUG: Diagnostic contains: Prefer java.nio.charset.StandardCharsets",
|
||||
" Object o3 = com.google.common.base.Charsets.ISO_8859_1;",
|
||||
"",
|
||||
" Object o4 = StandardCharsets.ISO_8859_1;",
|
||||
" Object o5 = java.nio.charset.StandardCharsets.ISO_8859_1;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullaryMethodIdentification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static com.google.common.base.Optional.absent;",
|
||||
"",
|
||||
"import com.google.common.base.Optional;",
|
||||
"import java.util.function.Supplier;",
|
||||
"",
|
||||
"class A {",
|
||||
" {",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Optional",
|
||||
" absent();",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Optional",
|
||||
" Optional.absent();",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Optional",
|
||||
" com.google.common.base.Optional.absent();",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Optional",
|
||||
" Supplier<?> s1 = Optional::absent;",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Optional",
|
||||
" Supplier<?> s2 = com.google.common.base.Optional::absent;",
|
||||
"",
|
||||
" java.util.Optional.empty();",
|
||||
" Supplier<?> s3 = java.util.Optional::empty;",
|
||||
"",
|
||||
" Dummy.absent();",
|
||||
" }",
|
||||
"",
|
||||
" static final class Dummy {",
|
||||
" static Optional<?> absent() {",
|
||||
" return null;",
|
||||
" }",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void unaryMethodWithIntegerArgumentIdentification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static com.google.common.collect.Lists.newArrayListWithCapacity;",
|
||||
"",
|
||||
"import com.google.common.collect.Lists;",
|
||||
"import java.util.ArrayList;",
|
||||
"import java.util.function.IntFunction;",
|
||||
"",
|
||||
"class A {",
|
||||
" {",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.ArrayList<>(int)",
|
||||
" newArrayListWithCapacity(0);",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.ArrayList<>(int)",
|
||||
" Lists.newArrayListWithCapacity(1);",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.ArrayList<>(int)",
|
||||
" com.google.common.collect.Lists.newArrayListWithCapacity(2);",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.ArrayList<>(int)",
|
||||
" IntFunction<?> f1 = Lists::newArrayListWithCapacity;",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.ArrayList<>(int)",
|
||||
" IntFunction<?> f2 = com.google.common.collect.Lists::newArrayListWithCapacity;",
|
||||
"",
|
||||
" new ArrayList<>(3);",
|
||||
" IntFunction<?> f3 = ArrayList::new;",
|
||||
" IntFunction<?> f4 = java.util.ArrayList::new;",
|
||||
"",
|
||||
" Dummy.newArrayListWithCapacity(4);",
|
||||
" }",
|
||||
"",
|
||||
" static final class Dummy {",
|
||||
" static ArrayList<?> newArrayListWithCapacity(int initialArraySize) {",
|
||||
" return null;",
|
||||
" }",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void binaryMethodWithObjectArgumentsIdentification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static com.google.common.base.Objects.equal;",
|
||||
"",
|
||||
"import com.google.common.base.Objects;",
|
||||
"import java.util.function.BiPredicate;",
|
||||
"",
|
||||
"class A {",
|
||||
" {",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Objects.equals(Object, Object)",
|
||||
" equal(null, null);",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Objects.equals(Object, Object)",
|
||||
" Objects.equal(null, null);",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Objects.equals(Object, Object)",
|
||||
" com.google.common.base.Objects.equal(null, null);",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Objects.equals(Object, Object)",
|
||||
" BiPredicate<?, ?> p1 = Objects::equal;",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Objects.equals(Object, Object)",
|
||||
" BiPredicate<?, ?> p2 = com.google.common.base.Objects::equal;",
|
||||
"",
|
||||
" java.util.Objects.equals(null, null);",
|
||||
" BiPredicate<?, ?> p3 = java.util.Objects::equals;",
|
||||
"",
|
||||
" Dummy.equal(null, null);",
|
||||
" }",
|
||||
"",
|
||||
" static final class Dummy {",
|
||||
" static boolean equal(Object a, Object b) {",
|
||||
" return false;",
|
||||
" }",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void varargsMethodWithObjectArgumentsIdentification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import com.google.common.base.Objects;",
|
||||
"import java.util.function.ToIntFunction;",
|
||||
"",
|
||||
"class A {",
|
||||
" {",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Objects.hash(Object...)",
|
||||
" Objects.hashCode((Object) null);",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Objects.hash(Object...)",
|
||||
" com.google.common.base.Objects.hashCode(null, null);",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Objects.hash(Object...)",
|
||||
" ToIntFunction<?> f1 = Objects::hashCode;",
|
||||
" // BUG: Diagnostic contains: Prefer java.util.Objects.hash(Object...)",
|
||||
" ToIntFunction<?> f2 = com.google.common.base.Objects::hashCode;",
|
||||
"",
|
||||
" java.util.Objects.hash(null, null, null);",
|
||||
" ToIntFunction<?> f3 = java.util.Objects::hash;",
|
||||
"",
|
||||
" Dummy.hashCode(null, null, null, null);",
|
||||
" }",
|
||||
"",
|
||||
" static final class Dummy {",
|
||||
" static int hashCode(Object... objects) {",
|
||||
" return 0;",
|
||||
" }",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void binaryConstructorWithByteArrayAndObjectArgumentsIdentification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import java.io.UnsupportedEncodingException;",
|
||||
"import java.nio.charset.StandardCharsets;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() throws UnsupportedEncodingException {",
|
||||
" // BUG: Diagnostic contains: Prefer java.lang.String.<init>(byte[], java.nio.charset.Charset)",
|
||||
" new String(new byte[0], \"\");",
|
||||
" // BUG: Diagnostic contains: Prefer java.lang.String.<init>(byte[], java.nio.charset.Charset)",
|
||||
" new java.lang.String(new byte[] {}, toString());",
|
||||
"",
|
||||
" new String(new byte[0], StandardCharsets.UTF_8);",
|
||||
" new java.lang.String(new byte[0], StandardCharsets.UTF_8);",
|
||||
"",
|
||||
" new Dummy(new byte[0], \"\");",
|
||||
" }",
|
||||
"",
|
||||
" static final class Dummy {",
|
||||
" Dummy(byte bytes[], String charsetName) {}",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,14 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class OptionalOrElseGetTest {
|
||||
final class OptionalOrElseTest {
|
||||
@Test
|
||||
void identification() {
|
||||
CompilationTestHelper.newInstance(OptionalOrElseGet.class, getClass())
|
||||
CompilationTestHelper.newInstance(OptionalOrElse.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import com.google.errorprone.refaster.Refaster;",
|
||||
"import java.util.Optional;",
|
||||
"import java.util.function.Supplier;",
|
||||
"",
|
||||
"class A {",
|
||||
" private final Optional<Object> optional = Optional.empty();",
|
||||
@@ -28,7 +27,6 @@ final class OptionalOrElseGetTest {
|
||||
" optional.orElse(string);",
|
||||
" optional.orElse(this.string);",
|
||||
" optional.orElse(Refaster.anyOf(\"constant\", \"another\"));",
|
||||
" Optional.<Supplier<String>>empty().orElse(() -> \"constant\");",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Optional.empty().orElse(string + \"constant\");",
|
||||
@@ -69,7 +67,7 @@ final class OptionalOrElseGetTest {
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
BugCheckerRefactoringTestHelper.newInstance(OptionalOrElseGet.class, getClass())
|
||||
BugCheckerRefactoringTestHelper.newInstance(OptionalOrElse.class, getClass())
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import java.util.Optional;",
|
||||
@@ -1,5 +1,6 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
@@ -407,7 +408,8 @@ final class RedundantStringConversionTest {
|
||||
void identificationOfCustomConversionMethod() {
|
||||
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
|
||||
.setArgs(
|
||||
"-XepOpt:RedundantStringConversion:ExtraConversionMethods=java.lang.Enum#name(),A#name(),A.B#toString(int)")
|
||||
ImmutableList.of(
|
||||
"-XepOpt:RedundantStringConversion:ExtraConversionMethods=java.lang.Enum#name(),A#name(),A.B#toString(int)"))
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import java.math.RoundingMode;",
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
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 RedundantStringEscapeTest {
|
||||
@Test
|
||||
void identification() {
|
||||
CompilationTestHelper.newInstance(RedundantStringEscape.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import java.util.Arrays;",
|
||||
"import java.util.List;",
|
||||
"",
|
||||
"class A {",
|
||||
" List<String> m() {",
|
||||
" return Arrays.asList(",
|
||||
" \"foo\",",
|
||||
" \"ß\",",
|
||||
" \"'\",",
|
||||
" \"\\\"\",",
|
||||
" \"\\\\\",",
|
||||
" \"\\\\'\",",
|
||||
" \"'\\\\\",",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" \"\\\\\\'\",",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" \"\\'\\\\\",",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" \"\\'\",",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" \"'\\'\",",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" \"\\''\",",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" \"\\'\\'\",",
|
||||
" (",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" /* Leading comment. */ \"\\'\" /* Trailing comment. */),",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" \"\\'foo\\\"bar\\'baz\\\"qux\\'\");",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
BugCheckerRefactoringTestHelper.newInstance(RedundantStringEscape.class, getClass())
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import java.util.Arrays;",
|
||||
"import java.util.List;",
|
||||
"",
|
||||
"class A {",
|
||||
" List<String> m() {",
|
||||
" return Arrays.asList(",
|
||||
" \"\\'\",",
|
||||
" \"'\\'\",",
|
||||
" \"\\''\",",
|
||||
" \"\\'\\'\",",
|
||||
" \"\\'ß\\'\",",
|
||||
" (",
|
||||
" /* Leading comment. */ \"\\'\" /* Trailing comment. */),",
|
||||
" \"\\'foo\\\"bar\\'baz\\\"qux\\'\");",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import java.util.Arrays;",
|
||||
"import java.util.List;",
|
||||
"",
|
||||
"class A {",
|
||||
" List<String> m() {",
|
||||
" return Arrays.asList(",
|
||||
" \"'\",",
|
||||
" \"''\",",
|
||||
" \"''\",",
|
||||
" \"''\",",
|
||||
" \"'ß'\",",
|
||||
" (",
|
||||
" /* Leading comment. */ \"'\" /* Trailing comment. */),",
|
||||
" \"'foo\\\"bar'baz\\\"qux'\");",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
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 Slf4jLoggerDeclarationTest {
|
||||
@Test
|
||||
void identification() {
|
||||
CompilationTestHelper.newInstance(Slf4jLoggerDeclaration.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static java.lang.Class.forName;",
|
||||
"",
|
||||
"import org.slf4j.Logger;",
|
||||
"import org.slf4j.LoggerFactory;",
|
||||
"",
|
||||
"class A {",
|
||||
" private static final long serialVersionUID = 1L;",
|
||||
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
|
||||
"",
|
||||
" abstract static class DynamicLogger {",
|
||||
" private final Logger log = LoggerFactory.getLogger(getClass());",
|
||||
" }",
|
||||
"",
|
||||
" abstract static class DynamicLoggerWithExplicitThis {",
|
||||
" private final Logger log = LoggerFactory.getLogger(this.getClass());",
|
||||
" }",
|
||||
"",
|
||||
" static final class StaticLogger {",
|
||||
" private static final Logger LOG = LoggerFactory.getLogger(StaticLogger.class);",
|
||||
" }",
|
||||
"",
|
||||
" static final class StaticLoggerWithCustomIdentifier {",
|
||||
" private static final Logger LOG = LoggerFactory.getLogger(\"custom-identifier\");",
|
||||
" }",
|
||||
"",
|
||||
" interface StaticLoggerForInterface {",
|
||||
" Logger LOG = LoggerFactory.getLogger(StaticLoggerForInterface.class);",
|
||||
" }",
|
||||
"",
|
||||
" abstract static class DynamicLoggerForWrongTypeWithoutReceiver {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private final Logger log = LoggerFactory.getLogger(forName(\"A.class\"));",
|
||||
"",
|
||||
" DynamicLoggerForWrongTypeWithoutReceiver() throws ClassNotFoundException {}",
|
||||
" }",
|
||||
"",
|
||||
" abstract static class DynamicLoggerForWrongTypeWithoutSymbol {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private final Logger log = LoggerFactory.getLogger(\"foo\".getClass());",
|
||||
" }",
|
||||
"",
|
||||
" abstract static class DynamicLoggerForWrongTypeWithSymbol {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private final Logger log = LoggerFactory.getLogger(new A().getClass());",
|
||||
" }",
|
||||
"",
|
||||
" static final class NonAbstractDynamicLogger {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private final Logger log = LoggerFactory.getLogger(getClass());",
|
||||
" }",
|
||||
"",
|
||||
" abstract static class DynamicLoggerWithMissingModifier {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" final Logger log = LoggerFactory.getLogger(getClass());",
|
||||
" }",
|
||||
"",
|
||||
" abstract static class DynamicLoggerWithExcessModifier {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private final transient Logger log = LoggerFactory.getLogger(getClass());",
|
||||
" }",
|
||||
"",
|
||||
" abstract static class MisnamedDynamicLogger {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private final Logger LOG = LoggerFactory.getLogger(getClass());",
|
||||
" }",
|
||||
"",
|
||||
" static final class StaticLoggerWithMissingModifier {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" static final Logger LOG = LoggerFactory.getLogger(StaticLoggerWithMissingModifier.class);",
|
||||
" }",
|
||||
"",
|
||||
" static final class StaticLoggerWithExcessModifier {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private static final transient Logger LOG =",
|
||||
" LoggerFactory.getLogger(StaticLoggerWithExcessModifier.class);",
|
||||
" }",
|
||||
"",
|
||||
" static final class MisnamedStaticLogger {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private static final Logger log = LoggerFactory.getLogger(MisnamedStaticLogger.class);",
|
||||
" }",
|
||||
"",
|
||||
" static final class StaticLoggerWithIncorrectIdentifier {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
|
||||
" }",
|
||||
"",
|
||||
" static final class StaticLoggerWithCustomIdentifierAndMissingModifier {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" static final Logger LOG = LoggerFactory.getLogger(\"custom-identifier\");",
|
||||
" }",
|
||||
"",
|
||||
" static final class StaticLoggerWithCustomIdentifierAndExcessModifier {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private static final transient Logger LOG = LoggerFactory.getLogger(\"custom-identifier\");",
|
||||
" }",
|
||||
"",
|
||||
" static final class MisnamedStaticLoggerWithCustomIdentifier {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private static final Logger log = LoggerFactory.getLogger(\"custom-identifier\");",
|
||||
" }",
|
||||
"",
|
||||
" interface StaticLoggerForInterfaceWithExcessModifier {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" static Logger LOG = LoggerFactory.getLogger(StaticLoggerForInterfaceWithExcessModifier.class);",
|
||||
" }",
|
||||
"",
|
||||
" interface MisnamedStaticLoggerForInterface {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Logger log = LoggerFactory.getLogger(MisnamedStaticLoggerForInterface.class);",
|
||||
" }",
|
||||
"",
|
||||
" interface StaticLoggerForInterfaceWithIncorrectIdentifier {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Logger LOG = LoggerFactory.getLogger(A.class);",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
BugCheckerRefactoringTestHelper.newInstance(Slf4jLoggerDeclaration.class, getClass())
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.slf4j.Logger;",
|
||||
"import org.slf4j.LoggerFactory;",
|
||||
"",
|
||||
"class A {",
|
||||
" static Logger foo = LoggerFactory.getLogger(Logger.class);",
|
||||
"",
|
||||
" abstract static class DynamicLogger {",
|
||||
" transient Logger BAR = LoggerFactory.getLogger(getClass());",
|
||||
" }",
|
||||
"",
|
||||
" static final class StaticLogger {",
|
||||
" transient Logger baz = LoggerFactory.getLogger(LoggerFactory.class);",
|
||||
" }",
|
||||
"",
|
||||
" static final class StaticLoggerWithCustomIdentifier {",
|
||||
" transient Logger qux = LoggerFactory.getLogger(\"custom-identifier\");",
|
||||
" }",
|
||||
"",
|
||||
" interface StaticLoggerForInterface {",
|
||||
" public static final Logger quux = LoggerFactory.getLogger(A.class);",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import org.slf4j.Logger;",
|
||||
"import org.slf4j.LoggerFactory;",
|
||||
"",
|
||||
"class A {",
|
||||
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
|
||||
"",
|
||||
" abstract static class DynamicLogger {",
|
||||
" private final Logger log = LoggerFactory.getLogger(getClass());",
|
||||
" }",
|
||||
"",
|
||||
" static final class StaticLogger {",
|
||||
" private static final Logger LOG = LoggerFactory.getLogger(StaticLogger.class);",
|
||||
" }",
|
||||
"",
|
||||
" static final class StaticLoggerWithCustomIdentifier {",
|
||||
" private static final Logger LOG = LoggerFactory.getLogger(\"custom-identifier\");",
|
||||
" }",
|
||||
"",
|
||||
" interface StaticLoggerForInterface {",
|
||||
" Logger LOG = LoggerFactory.getLogger(StaticLoggerForInterface.class);",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacementWithCustomLoggerName() {
|
||||
BugCheckerRefactoringTestHelper.newInstance(Slf4jLoggerDeclaration.class, getClass())
|
||||
.setArgs("-XepOpt:Slf4jLoggerDeclaration:CanonicalStaticLoggerName=FOO_BAR")
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.slf4j.Logger;",
|
||||
"import org.slf4j.LoggerFactory;",
|
||||
"",
|
||||
"class A {",
|
||||
" transient Logger LOG = LoggerFactory.getLogger(Logger.class);",
|
||||
"",
|
||||
" abstract static class DynamicLogger {",
|
||||
" transient Logger log = LoggerFactory.getLogger(getClass());",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import org.slf4j.Logger;",
|
||||
"import org.slf4j.LoggerFactory;",
|
||||
"",
|
||||
"class A {",
|
||||
" private static final Logger FOO_BAR = LoggerFactory.getLogger(A.class);",
|
||||
"",
|
||||
" abstract static class DynamicLogger {",
|
||||
" private final Logger fooBar = LoggerFactory.getLogger(getClass());",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,6 @@ final class RefasterRulesTest {
|
||||
AssortedRules.class,
|
||||
BigDecimalRules.class,
|
||||
BugCheckerRules.class,
|
||||
CharSequenceRules.class,
|
||||
ClassRules.class,
|
||||
CollectionRules.class,
|
||||
ComparatorRules.class,
|
||||
@@ -44,7 +43,6 @@ final class RefasterRulesTest {
|
||||
EqualityRules.class,
|
||||
FileRules.class,
|
||||
InputStreamRules.class,
|
||||
ImmutableEnumSetRules.class,
|
||||
ImmutableListRules.class,
|
||||
ImmutableListMultimapRules.class,
|
||||
ImmutableMapRules.class,
|
||||
@@ -60,8 +58,8 @@ final class RefasterRulesTest {
|
||||
LongStreamRules.class,
|
||||
MapEntryRules.class,
|
||||
MapRules.class,
|
||||
MicrometerRules.class,
|
||||
MockitoRules.class,
|
||||
MongoDBRules.class,
|
||||
MultimapRules.class,
|
||||
NullRules.class,
|
||||
OptionalRules.class,
|
||||
|
||||
@@ -33,14 +33,6 @@ final class AssertJStringRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return assertThat("foo".isEmpty()).isFalse();
|
||||
}
|
||||
|
||||
AbstractAssert<?, ?> testAssertThatStringContains() {
|
||||
return assertThat("foo".contains("bar")).isTrue();
|
||||
}
|
||||
|
||||
AbstractAssert<?, ?> testAssertThatStringDoesNotContain() {
|
||||
return assertThat("foo".contains("bar")).isFalse();
|
||||
}
|
||||
|
||||
AbstractAssert<?, ?> testAssertThatMatches() {
|
||||
return assertThat("foo".matches(".*")).isTrue();
|
||||
}
|
||||
|
||||
@@ -34,14 +34,6 @@ final class AssertJStringRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return assertThat("foo").isNotEmpty();
|
||||
}
|
||||
|
||||
AbstractAssert<?, ?> testAssertThatStringContains() {
|
||||
return assertThat("foo").contains("bar");
|
||||
}
|
||||
|
||||
AbstractAssert<?, ?> testAssertThatStringDoesNotContain() {
|
||||
return assertThat("foo").doesNotContain("bar");
|
||||
}
|
||||
|
||||
AbstractAssert<?, ?> testAssertThatMatches() {
|
||||
return assertThat("foo").matches(".*");
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.InstanceOfAssertFactories.throwable;
|
||||
import static org.assertj.core.api.InstanceOfAssertFactories.type;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.assertj.core.api.AbstractObjectAssert;
|
||||
@@ -22,13 +20,7 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
assertThatIOException(),
|
||||
assertThatIllegalArgumentException(),
|
||||
assertThatIllegalStateException(),
|
||||
assertThatNullPointerException(),
|
||||
type(Throwable.class));
|
||||
}
|
||||
|
||||
void testAssertThatThrownByIsInstanceOf() {
|
||||
assertThatThrownBy(() -> {}).asInstanceOf(throwable(IllegalArgumentException.class));
|
||||
assertThatThrownBy(() -> {}).asInstanceOf(type(IllegalArgumentException.class));
|
||||
assertThatNullPointerException());
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalArgumentException() {
|
||||
@@ -39,13 +31,6 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
return assertThatIllegalArgumentException().isThrownBy(() -> {}).withMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalArgumentExceptionRootCauseHasMessage() {
|
||||
return assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> {})
|
||||
.havingRootCause()
|
||||
.withMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalArgumentExceptionHasMessageParameters() {
|
||||
return assertThatIllegalArgumentException().isThrownBy(() -> {}).withMessage("foo %s", "bar");
|
||||
}
|
||||
@@ -74,13 +59,6 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
return assertThatIllegalStateException().isThrownBy(() -> {}).withMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalStateExceptionRootCauseHasMessage() {
|
||||
return assertThatIllegalStateException()
|
||||
.isThrownBy(() -> {})
|
||||
.havingRootCause()
|
||||
.withMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalStateExceptionHasMessageParameters() {
|
||||
return assertThatIllegalStateException().isThrownBy(() -> {}).withMessage("foo %s", "bar");
|
||||
}
|
||||
@@ -105,13 +83,6 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
return assertThatNullPointerException().isThrownBy(() -> {}).withMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByNullPointerExceptionRootCauseHasMessage() {
|
||||
return assertThatNullPointerException()
|
||||
.isThrownBy(() -> {})
|
||||
.havingRootCause()
|
||||
.withMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByNullPointerExceptionHasMessageParameters() {
|
||||
return assertThatNullPointerException().isThrownBy(() -> {}).withMessage("foo %s", "bar");
|
||||
}
|
||||
@@ -136,10 +107,6 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
return assertThatIOException().isThrownBy(() -> {}).withMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIOExceptionRootCauseHasMessage() {
|
||||
return assertThatIOException().isThrownBy(() -> {}).havingRootCause().withMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIOExceptionHasMessageParameters() {
|
||||
return assertThatIOException().isThrownBy(() -> {}).withMessage("foo %s", "bar");
|
||||
}
|
||||
@@ -156,7 +123,7 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
return assertThatIOException().isThrownBy(() -> {}).withMessageNotContaining("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByAsInstanceOfThrowable() {
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownBy() {
|
||||
return assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> {});
|
||||
}
|
||||
|
||||
@@ -166,13 +133,6 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
.withMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByRootCauseHasMessage() {
|
||||
return assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> {})
|
||||
.havingRootCause()
|
||||
.withMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByHasMessageParameters() {
|
||||
return assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> {})
|
||||
|
||||
@@ -6,8 +6,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.InstanceOfAssertFactories.throwable;
|
||||
import static org.assertj.core.api.InstanceOfAssertFactories.type;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.io.IOException;
|
||||
@@ -23,13 +21,7 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
assertThatIOException(),
|
||||
assertThatIllegalArgumentException(),
|
||||
assertThatIllegalStateException(),
|
||||
assertThatNullPointerException(),
|
||||
type(Throwable.class));
|
||||
}
|
||||
|
||||
void testAssertThatThrownByIsInstanceOf() {
|
||||
assertThatThrownBy(() -> {}).isInstanceOf(IllegalArgumentException.class);
|
||||
assertThatThrownBy(() -> {}).isInstanceOf(IllegalArgumentException.class);
|
||||
assertThatNullPointerException());
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalArgumentException() {
|
||||
@@ -42,13 +34,6 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
.hasMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalArgumentExceptionRootCauseHasMessage() {
|
||||
return assertThatThrownBy(() -> {})
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.rootCause()
|
||||
.hasMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalArgumentExceptionHasMessageParameters() {
|
||||
return assertThatThrownBy(() -> {})
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
@@ -83,13 +68,6 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
return assertThatThrownBy(() -> {}).isInstanceOf(IllegalStateException.class).hasMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalStateExceptionRootCauseHasMessage() {
|
||||
return assertThatThrownBy(() -> {})
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.rootCause()
|
||||
.hasMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalStateExceptionHasMessageParameters() {
|
||||
return assertThatThrownBy(() -> {})
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
@@ -122,13 +100,6 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
return assertThatThrownBy(() -> {}).isInstanceOf(NullPointerException.class).hasMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByNullPointerExceptionRootCauseHasMessage() {
|
||||
return assertThatThrownBy(() -> {})
|
||||
.isInstanceOf(NullPointerException.class)
|
||||
.rootCause()
|
||||
.hasMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByNullPointerExceptionHasMessageParameters() {
|
||||
return assertThatThrownBy(() -> {})
|
||||
.isInstanceOf(NullPointerException.class)
|
||||
@@ -161,13 +132,6 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
return assertThatThrownBy(() -> {}).isInstanceOf(IOException.class).hasMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIOExceptionRootCauseHasMessage() {
|
||||
return assertThatThrownBy(() -> {})
|
||||
.isInstanceOf(IOException.class)
|
||||
.rootCause()
|
||||
.hasMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByIOExceptionHasMessageParameters() {
|
||||
return assertThatThrownBy(() -> {}).isInstanceOf(IOException.class).hasMessage("foo %s", "bar");
|
||||
}
|
||||
@@ -188,8 +152,8 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
.hasMessageNotContaining("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByAsInstanceOfThrowable() {
|
||||
return assertThatThrownBy(() -> {}).asInstanceOf(throwable(IllegalArgumentException.class));
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownBy() {
|
||||
return assertThatThrownBy(() -> {}).isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByHasMessage() {
|
||||
@@ -198,13 +162,6 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
|
||||
.hasMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByRootCauseHasMessage() {
|
||||
return assertThatThrownBy(() -> {})
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.rootCause()
|
||||
.hasMessage("foo");
|
||||
}
|
||||
|
||||
AbstractObjectAssert<?, ?> testAssertThatThrownByHasMessageParameters() {
|
||||
return assertThatThrownBy(() -> {})
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.BoundType;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -21,7 +24,8 @@ final class AssortedRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Preconditions.class,
|
||||
Sets.class,
|
||||
Splitter.class,
|
||||
Streams.class);
|
||||
Streams.class,
|
||||
toImmutableSet());
|
||||
}
|
||||
|
||||
int testCheckIndex() {
|
||||
@@ -34,6 +38,10 @@ final class AssortedRulesTest implements RefasterRuleCollectionTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
ImmutableSet<BoundType> testStreamToImmutableEnumSet() {
|
||||
return Stream.of(BoundType.OPEN).collect(toImmutableSet());
|
||||
}
|
||||
|
||||
ImmutableSet<String> testIteratorGetNextOrDefault() {
|
||||
return ImmutableSet.of(
|
||||
ImmutableList.of("a").iterator().hasNext()
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Sets.toImmutableEnumSet;
|
||||
import static java.util.Objects.checkIndex;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.BoundType;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -24,7 +27,8 @@ final class AssortedRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Preconditions.class,
|
||||
Sets.class,
|
||||
Splitter.class,
|
||||
Streams.class);
|
||||
Streams.class,
|
||||
toImmutableSet());
|
||||
}
|
||||
|
||||
int testCheckIndex() {
|
||||
@@ -35,6 +39,10 @@ final class AssortedRulesTest implements RefasterRuleCollectionTestCase {
|
||||
checkIndex(1, 2);
|
||||
}
|
||||
|
||||
ImmutableSet<BoundType> testStreamToImmutableEnumSet() {
|
||||
return Stream.of(BoundType.OPEN).collect(toImmutableEnumSet());
|
||||
}
|
||||
|
||||
ImmutableSet<String> testIteratorGetNextOrDefault() {
|
||||
return ImmutableSet.of(
|
||||
Iterators.getNext(ImmutableList.of("a").iterator(), "foo"),
|
||||
|
||||
@@ -4,15 +4,13 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.sun.tools.javac.util.Constants;
|
||||
import com.sun.tools.javac.util.Convert;
|
||||
import javax.lang.model.element.Name;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
final class BugCheckerRulesTest implements RefasterRuleCollectionTestCase {
|
||||
@Override
|
||||
public ImmutableSet<Object> elidedTypesAndStaticImports() {
|
||||
return ImmutableSet.of(Constants.class, Convert.class, FixChoosers.class);
|
||||
return ImmutableSet.of(Convert.class, FixChoosers.class);
|
||||
}
|
||||
|
||||
ImmutableSet<BugCheckerRefactoringTestHelper> testBugCheckerRefactoringTestHelperIdentity() {
|
||||
@@ -30,13 +28,7 @@ final class BugCheckerRulesTest implements RefasterRuleCollectionTestCase {
|
||||
.addOutputLines("A.java", "class A {}");
|
||||
}
|
||||
|
||||
ImmutableSet<String> testConstantsFormat() {
|
||||
return ImmutableSet.of(Constants.format("foo"), String.format("\"%s\"", Convert.quote("bar")));
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testNameContentEquals() {
|
||||
return ImmutableSet.of(
|
||||
((Name) null).toString().equals("foo".subSequence(0, 1).toString()),
|
||||
((com.sun.tools.javac.util.Name) null).toString().equals("bar"));
|
||||
String testConstantsFormat() {
|
||||
return String.format("\"%s\"", Convert.quote("foo"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,12 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.sun.tools.javac.util.Constants;
|
||||
import com.sun.tools.javac.util.Convert;
|
||||
import javax.lang.model.element.Name;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
final class BugCheckerRulesTest implements RefasterRuleCollectionTestCase {
|
||||
@Override
|
||||
public ImmutableSet<Object> elidedTypesAndStaticImports() {
|
||||
return ImmutableSet.of(Constants.class, Convert.class, FixChoosers.class);
|
||||
return ImmutableSet.of(Convert.class, FixChoosers.class);
|
||||
}
|
||||
|
||||
ImmutableSet<BugCheckerRefactoringTestHelper> testBugCheckerRefactoringTestHelperIdentity() {
|
||||
@@ -29,15 +27,7 @@ final class BugCheckerRulesTest implements RefasterRuleCollectionTestCase {
|
||||
.expectUnchanged();
|
||||
}
|
||||
|
||||
ImmutableSet<String> testConstantsFormat() {
|
||||
return ImmutableSet.of(
|
||||
SourceCode.toStringConstantExpression("foo", /* REPLACEME */ null),
|
||||
SourceCode.toStringConstantExpression("bar", /* REPLACEME */ null));
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testNameContentEquals() {
|
||||
return ImmutableSet.of(
|
||||
((Name) null).contentEquals("foo".subSequence(0, 1)),
|
||||
((com.sun.tools.javac.util.Name) null).contentEquals("bar"));
|
||||
String testConstantsFormat() {
|
||||
return Constants.format("foo");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
final class CharSequenceRulesTest implements RefasterRuleCollectionTestCase {
|
||||
ImmutableSet<Boolean> testCharSequenceIsEmpty() {
|
||||
return ImmutableSet.of(
|
||||
new StringBuilder("foo").length() == 0,
|
||||
new StringBuilder("bar").length() <= 0,
|
||||
new StringBuilder("baz").length() < 1,
|
||||
new StringBuilder("qux").length() != 0,
|
||||
new StringBuilder("quux").length() > 0,
|
||||
new StringBuilder("corge").length() >= 1);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
final class CharSequenceRulesTest implements RefasterRuleCollectionTestCase {
|
||||
ImmutableSet<Boolean> testCharSequenceIsEmpty() {
|
||||
return ImmutableSet.of(
|
||||
new StringBuilder("foo").isEmpty(),
|
||||
new StringBuilder("bar").isEmpty(),
|
||||
new StringBuilder("baz").isEmpty(),
|
||||
!new StringBuilder("qux").isEmpty(),
|
||||
!new StringBuilder("quux").isEmpty(),
|
||||
!new StringBuilder("corge").isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,26 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.util.function.Function;
|
||||
import java.io.IOException;
|
||||
import java.util.function.Predicate;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
final class ClassRulesTest implements RefasterRuleCollectionTestCase {
|
||||
boolean testClassIsInstance() {
|
||||
boolean testClassIsInstance() throws IOException {
|
||||
return CharSequence.class.isAssignableFrom("foo".getClass());
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testInstanceof() {
|
||||
ImmutableSet<Boolean> testInstanceof() throws IOException {
|
||||
Class<?> clazz = CharSequence.class;
|
||||
return ImmutableSet.of(CharSequence.class.isInstance("foo"), clazz.isInstance("bar"));
|
||||
}
|
||||
|
||||
Predicate<String> testClassLiteralIsInstancePredicate() {
|
||||
Predicate<String> testClassLiteralIsInstancePredicate() throws IOException {
|
||||
return s -> s instanceof CharSequence;
|
||||
}
|
||||
|
||||
Predicate<String> testClassReferenceIsInstancePredicate() {
|
||||
Predicate<String> testClassReferenceIsInstancePredicate() throws IOException {
|
||||
Class<?> clazz = CharSequence.class;
|
||||
return s -> clazz.isInstance(s);
|
||||
}
|
||||
|
||||
Function<Number, Integer> testClassReferenceCast() {
|
||||
return i -> Integer.class.cast(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.util.function.Function;
|
||||
import java.io.IOException;
|
||||
import java.util.function.Predicate;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
final class ClassRulesTest implements RefasterRuleCollectionTestCase {
|
||||
boolean testClassIsInstance() {
|
||||
boolean testClassIsInstance() throws IOException {
|
||||
return CharSequence.class.isInstance("foo");
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testInstanceof() {
|
||||
ImmutableSet<Boolean> testInstanceof() throws IOException {
|
||||
Class<?> clazz = CharSequence.class;
|
||||
return ImmutableSet.of("foo" instanceof CharSequence, clazz.isInstance("bar"));
|
||||
}
|
||||
|
||||
Predicate<String> testClassLiteralIsInstancePredicate() {
|
||||
Predicate<String> testClassLiteralIsInstancePredicate() throws IOException {
|
||||
return CharSequence.class::isInstance;
|
||||
}
|
||||
|
||||
Predicate<String> testClassReferenceIsInstancePredicate() {
|
||||
Predicate<String> testClassReferenceIsInstancePredicate() throws IOException {
|
||||
Class<?> clazz = CharSequence.class;
|
||||
return clazz::isInstance;
|
||||
}
|
||||
|
||||
Function<Number, Integer> testClassReferenceCast() {
|
||||
return Integer.class::cast;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,9 @@ 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;
|
||||
@@ -72,10 +70,6 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
Stream<Integer> testSetStream() {
|
||||
return ImmutableSet.of(1).stream().distinct();
|
||||
}
|
||||
|
||||
ArrayList<String> testNewArrayListFromCollection() {
|
||||
return Lists.newArrayList(ImmutableList.of("foo"));
|
||||
}
|
||||
@@ -100,10 +94,6 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(1).asList().toString();
|
||||
}
|
||||
|
||||
List<String> testArraysAsList() {
|
||||
return Arrays.stream(new String[0]).toList();
|
||||
}
|
||||
|
||||
ImmutableSet<Object[]> testCollectionToArray() {
|
||||
return ImmutableSet.of(
|
||||
ImmutableSet.of(1).toArray(new Object[1]),
|
||||
@@ -119,9 +109,8 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(1).asList().toArray(Integer[]::new);
|
||||
}
|
||||
|
||||
ImmutableSet<Iterator<Integer>> testCollectionIterator() {
|
||||
return ImmutableSet.of(
|
||||
ImmutableSet.of(1).stream().iterator(), ImmutableSet.of(2).asList().iterator());
|
||||
Iterator<Integer> testImmutableCollectionIterator() {
|
||||
return ImmutableSet.of(1).asList().iterator();
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<Integer>> testOptionalFirstCollectionElement() {
|
||||
|
||||
@@ -6,11 +6,9 @@ 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;
|
||||
@@ -64,10 +62,6 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
|
||||
new HashSet<Number>().removeAll(ImmutableSet.of(2));
|
||||
}
|
||||
|
||||
Stream<Integer> testSetStream() {
|
||||
return ImmutableSet.of(1).stream();
|
||||
}
|
||||
|
||||
ArrayList<String> testNewArrayListFromCollection() {
|
||||
return new ArrayList<>(ImmutableList.of("foo"));
|
||||
}
|
||||
@@ -92,10 +86,6 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(1).toString();
|
||||
}
|
||||
|
||||
List<String> testArraysAsList() {
|
||||
return Arrays.asList(new String[0]);
|
||||
}
|
||||
|
||||
ImmutableSet<Object[]> testCollectionToArray() {
|
||||
return ImmutableSet.of(
|
||||
ImmutableSet.of(1).toArray(), ImmutableSet.of(2).toArray(), ImmutableSet.of(3).toArray());
|
||||
@@ -109,8 +99,8 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(1).toArray(Integer[]::new);
|
||||
}
|
||||
|
||||
ImmutableSet<Iterator<Integer>> testCollectionIterator() {
|
||||
return ImmutableSet.of(ImmutableSet.of(1).iterator(), ImmutableSet.of(2).iterator());
|
||||
Iterator<Integer> testImmutableCollectionIterator() {
|
||||
return ImmutableSet.of(1).iterator();
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<Integer>> testOptionalFirstCollectionElement() {
|
||||
|
||||
@@ -107,24 +107,6 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Comparator.<String>reverseOrder().compare("baz", "qux"));
|
||||
}
|
||||
|
||||
void testCollectionsSort() {
|
||||
Collections.sort(ImmutableList.of("foo", "bar"), naturalOrder());
|
||||
}
|
||||
|
||||
ImmutableSet<String> testCollectionsMin() {
|
||||
return ImmutableSet.of(
|
||||
Collections.min(ImmutableList.of("foo"), naturalOrder()),
|
||||
Collections.max(ImmutableList.of("bar"), reverseOrder()));
|
||||
}
|
||||
|
||||
String testMinOfArray() {
|
||||
return Arrays.stream(new String[0]).min(naturalOrder()).orElseThrow();
|
||||
}
|
||||
|
||||
String testCollectionsMinWithComparator() {
|
||||
return ImmutableSet.of("foo", "bar").stream().min(naturalOrder()).orElseThrow();
|
||||
}
|
||||
|
||||
int testMinOfVarargs() {
|
||||
return Stream.of(1, 2).min(naturalOrder()).orElseThrow();
|
||||
}
|
||||
@@ -153,20 +135,6 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Collections.min(ImmutableSet.of("a", "b"), (a, b) -> 1));
|
||||
}
|
||||
|
||||
ImmutableSet<String> testCollectionsMax() {
|
||||
return ImmutableSet.of(
|
||||
Collections.max(ImmutableList.of("foo"), naturalOrder()),
|
||||
Collections.min(ImmutableList.of("bar"), reverseOrder()));
|
||||
}
|
||||
|
||||
String testMaxOfArray() {
|
||||
return Arrays.stream(new String[0]).max(naturalOrder()).orElseThrow();
|
||||
}
|
||||
|
||||
String testCollectionsMaxWithComparator() {
|
||||
return ImmutableSet.of("foo", "bar").stream().max(naturalOrder()).orElseThrow();
|
||||
}
|
||||
|
||||
int testMaxOfVarargs() {
|
||||
return Stream.of(1, 2).max(naturalOrder()).orElseThrow();
|
||||
}
|
||||
|
||||
@@ -98,23 +98,6 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of("foo".compareTo("bar"), "qux".compareTo("baz"));
|
||||
}
|
||||
|
||||
void testCollectionsSort() {
|
||||
Collections.sort(ImmutableList.of("foo", "bar"));
|
||||
}
|
||||
|
||||
ImmutableSet<String> testCollectionsMin() {
|
||||
return ImmutableSet.of(
|
||||
Collections.min(ImmutableList.of("foo")), Collections.min(ImmutableList.of("bar")));
|
||||
}
|
||||
|
||||
String testMinOfArray() {
|
||||
return Collections.min(Arrays.asList(new String[0]), naturalOrder());
|
||||
}
|
||||
|
||||
String testCollectionsMinWithComparator() {
|
||||
return Collections.min(ImmutableSet.of("foo", "bar"), naturalOrder());
|
||||
}
|
||||
|
||||
int testMinOfVarargs() {
|
||||
return Collections.min(Arrays.asList(1, 2), naturalOrder());
|
||||
}
|
||||
@@ -143,19 +126,6 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Comparators.min("a", "b", (a, b) -> 1));
|
||||
}
|
||||
|
||||
ImmutableSet<String> testCollectionsMax() {
|
||||
return ImmutableSet.of(
|
||||
Collections.max(ImmutableList.of("foo")), Collections.max(ImmutableList.of("bar")));
|
||||
}
|
||||
|
||||
String testMaxOfArray() {
|
||||
return Collections.max(Arrays.asList(new String[0]), naturalOrder());
|
||||
}
|
||||
|
||||
String testCollectionsMaxWithComparator() {
|
||||
return Collections.max(ImmutableSet.of("foo", "bar"), naturalOrder());
|
||||
}
|
||||
|
||||
int testMaxOfVarargs() {
|
||||
return Collections.max(Arrays.asList(1, 2), naturalOrder());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static java.util.function.Predicate.isEqual;
|
||||
import static java.util.function.Predicate.not;
|
||||
|
||||
import com.google.common.collect.BoundType;
|
||||
@@ -15,10 +14,10 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
final class EqualityRulesTest implements RefasterRuleCollectionTestCase {
|
||||
@Override
|
||||
public ImmutableSet<Object> elidedTypesAndStaticImports() {
|
||||
return ImmutableSet.of(Objects.class, Optional.class, isEqual(null), not(null));
|
||||
return ImmutableSet.of(Objects.class, Optional.class, not(null));
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testEnumReferenceEquality() {
|
||||
ImmutableSet<Boolean> testPrimitiveOrReferenceEquality() {
|
||||
return ImmutableSet.of(
|
||||
RoundingMode.UP.equals(RoundingMode.DOWN),
|
||||
Objects.equals(RoundingMode.UP, RoundingMode.DOWN),
|
||||
@@ -28,10 +27,6 @@ final class EqualityRulesTest implements RefasterRuleCollectionTestCase {
|
||||
RoundingMode.UP.ordinal() != RoundingMode.DOWN.ordinal());
|
||||
}
|
||||
|
||||
ImmutableSet<Predicate<RoundingMode>> testEnumReferenceEqualityLambda() {
|
||||
return ImmutableSet.of(isEqual(RoundingMode.DOWN), RoundingMode.UP::equals);
|
||||
}
|
||||
|
||||
boolean testEqualsPredicate() {
|
||||
// XXX: When boxing is involved this rule seems to break. Example:
|
||||
// Stream.of(1).anyMatch(e -> Integer.MIN_VALUE.equals(e));
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static java.util.function.Predicate.isEqual;
|
||||
import static java.util.function.Predicate.not;
|
||||
|
||||
import com.google.common.collect.BoundType;
|
||||
@@ -15,10 +14,10 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
final class EqualityRulesTest implements RefasterRuleCollectionTestCase {
|
||||
@Override
|
||||
public ImmutableSet<Object> elidedTypesAndStaticImports() {
|
||||
return ImmutableSet.of(Objects.class, Optional.class, isEqual(null), not(null));
|
||||
return ImmutableSet.of(Objects.class, Optional.class, not(null));
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testEnumReferenceEquality() {
|
||||
ImmutableSet<Boolean> testPrimitiveOrReferenceEquality() {
|
||||
return ImmutableSet.of(
|
||||
RoundingMode.UP == RoundingMode.DOWN,
|
||||
RoundingMode.UP == RoundingMode.DOWN,
|
||||
@@ -28,10 +27,6 @@ final class EqualityRulesTest implements RefasterRuleCollectionTestCase {
|
||||
RoundingMode.UP != RoundingMode.DOWN);
|
||||
}
|
||||
|
||||
ImmutableSet<Predicate<RoundingMode>> 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));
|
||||
|
||||
@@ -1,28 +1,12 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
final class FileRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Path testPathOfUri() {
|
||||
return Paths.get(URI.create("foo"));
|
||||
}
|
||||
|
||||
ImmutableSet<Path> testPathOfString() {
|
||||
return ImmutableSet.of(Paths.get("foo"), Paths.get("bar", "baz", "qux"));
|
||||
}
|
||||
|
||||
Path testPathInstance() {
|
||||
return Path.of("foo").toFile().toPath();
|
||||
}
|
||||
|
||||
String testFilesReadStringWithCharset() throws IOException {
|
||||
return new String(Files.readAllBytes(Paths.get("foo")), StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
@@ -30,13 +14,4 @@ final class FileRulesTest implements RefasterRuleCollectionTestCase {
|
||||
String testFilesReadString() throws IOException {
|
||||
return Files.readString(Paths.get("foo"), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
ImmutableSet<File> testFilesCreateTempFileToFile() throws IOException {
|
||||
return ImmutableSet.of(
|
||||
File.createTempFile("foo", "bar"), File.createTempFile("baz", "qux", null));
|
||||
}
|
||||
|
||||
File testFilesCreateTempFileInCustomDirectoryToFile() throws IOException {
|
||||
return File.createTempFile("foo", "bar", new File("baz"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,12 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
final class FileRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Path testPathOfUri() {
|
||||
return Path.of(URI.create("foo"));
|
||||
}
|
||||
|
||||
ImmutableSet<Path> testPathOfString() {
|
||||
return ImmutableSet.of(Path.of("foo"), Path.of("bar", "baz", "qux"));
|
||||
}
|
||||
|
||||
Path testPathInstance() {
|
||||
return Path.of("foo");
|
||||
}
|
||||
|
||||
String testFilesReadStringWithCharset() throws IOException {
|
||||
return Files.readString(Paths.get("foo"), StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
@@ -30,13 +14,4 @@ final class FileRulesTest implements RefasterRuleCollectionTestCase {
|
||||
String testFilesReadString() throws IOException {
|
||||
return Files.readString(Paths.get("foo"));
|
||||
}
|
||||
|
||||
ImmutableSet<File> testFilesCreateTempFileToFile() throws IOException {
|
||||
return ImmutableSet.of(
|
||||
Files.createTempFile("foo", "bar").toFile(), Files.createTempFile("baz", "qux").toFile());
|
||||
}
|
||||
|
||||
File testFilesCreateTempFileInCustomDirectoryToFile() throws IOException {
|
||||
return Files.createTempFile(new File("baz").toPath(), "foo", "bar").toFile();
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user