mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 08:11:25 +00:00
Compare commits
166 Commits
sschroever
...
v0.16.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8aec87b40e | ||
|
|
03bd3215c7 | ||
|
|
c806f4044d | ||
|
|
23ceb4aa6b | ||
|
|
5cca9d23da | ||
|
|
df701d3d3c | ||
|
|
3b005b0edc | ||
|
|
4e0eb1e9bf | ||
|
|
d6cc4c92c8 | ||
|
|
d9dd1c5882 | ||
|
|
3950ff5066 | ||
|
|
8847a15414 | ||
|
|
03b8925fe5 | ||
|
|
105cccc245 | ||
|
|
e9263d9d07 | ||
|
|
c214733517 | ||
|
|
3d49c80999 | ||
|
|
398d162b8d | ||
|
|
110ac01d10 | ||
|
|
479ded388a | ||
|
|
4ceeb2bcd5 | ||
|
|
fa1adbdb02 | ||
|
|
82c23bb332 | ||
|
|
8f64489fa0 | ||
|
|
424f96878f | ||
|
|
3c211bdf60 | ||
|
|
219254813e | ||
|
|
39c40d2f14 | ||
|
|
01dfa2960d | ||
|
|
ded0a48258 | ||
|
|
41e42114c6 | ||
|
|
d8f0a613b9 | ||
|
|
efca24141c | ||
|
|
f8fc14e73a | ||
|
|
574753022a | ||
|
|
194a828c67 | ||
|
|
1f83eada44 | ||
|
|
34b57b76bc | ||
|
|
cd3c2aab5d | ||
|
|
b39e322a67 | ||
|
|
ad9d2dd534 | ||
|
|
d3307645cb | ||
|
|
2185a0397a | ||
|
|
c4a9f6fab7 | ||
|
|
98b6b7ec0c | ||
|
|
cc6211d560 | ||
|
|
8855ba33a0 | ||
|
|
e87b02cfe3 | ||
|
|
21a16e8803 | ||
|
|
d3cc77ed93 | ||
|
|
c2365c01c3 | ||
|
|
1d0d1d6cae | ||
|
|
cce897ed4a | ||
|
|
28bb4f6895 | ||
|
|
1fe67677b4 | ||
|
|
433b8b90c0 | ||
|
|
1f50772433 | ||
|
|
382b79989c | ||
|
|
1cc792c615 | ||
|
|
b5ace6e044 | ||
|
|
1f71ccccf7 | ||
|
|
57fa6ae2b8 | ||
|
|
da3ec2ce90 | ||
|
|
b8abceab73 | ||
|
|
c594a16c7c | ||
|
|
2c95de3879 | ||
|
|
048167b021 | ||
|
|
bf06625211 | ||
|
|
9a2a1915eb | ||
|
|
a01e5e4cf1 | ||
|
|
6195ede1f5 | ||
|
|
03ba14b229 | ||
|
|
c771d5f6b9 | ||
|
|
0ba806432d | ||
|
|
f66e513042 | ||
|
|
0a3537669a | ||
|
|
c96fdf5ed2 | ||
|
|
a4ab37d2a9 | ||
|
|
07fe6df3af | ||
|
|
79ac13809f | ||
|
|
f7f561bc9e | ||
|
|
90066f87d1 | ||
|
|
32d5c114c1 | ||
|
|
e3d94a9ac4 | ||
|
|
e086be4fea | ||
|
|
5f80fb5370 | ||
|
|
ba27fc588d | ||
|
|
000c33c85f | ||
|
|
a926e5534c | ||
|
|
e65e2ce730 | ||
|
|
d4be298022 | ||
|
|
4f6d32191e | ||
|
|
aa592e5e16 | ||
|
|
a183f921c9 | ||
|
|
7d4dc16d6f | ||
|
|
33ebcac257 | ||
|
|
4849bb5ae2 | ||
|
|
a73624da1d | ||
|
|
7ef4537ff4 | ||
|
|
7e0e1216ec | ||
|
|
aa1cfd9071 | ||
|
|
0aa612073f | ||
|
|
9e6d35569f | ||
|
|
dc65917ef1 | ||
|
|
f12474e8e6 | ||
|
|
8f152b1135 | ||
|
|
b88fc8e542 | ||
|
|
8f2ac01ac8 | ||
|
|
51317fbace | ||
|
|
e48492628e | ||
|
|
b2552e6feb | ||
|
|
0c2180b151 | ||
|
|
b404737433 | ||
|
|
54bba95ffa | ||
|
|
6222bcb0d4 | ||
|
|
641bb5c566 | ||
|
|
09317abb18 | ||
|
|
dff67fecbc | ||
|
|
d126336742 | ||
|
|
b8eabff9bc | ||
|
|
0b04e0fb3f | ||
|
|
14506ed392 | ||
|
|
664adb4aa4 | ||
|
|
f35d62061d | ||
|
|
a89f986763 | ||
|
|
6ad94b5c29 | ||
|
|
63fe19b487 | ||
|
|
fbd9b0689c | ||
|
|
c7a288cf29 | ||
|
|
931632d90b | ||
|
|
71de432645 | ||
|
|
97a4cb0227 | ||
|
|
6b1cb4c2e9 | ||
|
|
e0cb7eb1f9 | ||
|
|
a2f44f82f2 | ||
|
|
0ec8ebe8ab | ||
|
|
e1be5d23e9 | ||
|
|
12f2feaacf | ||
|
|
423a306719 | ||
|
|
b5327f2e97 | ||
|
|
059e1dbbe7 | ||
|
|
cef7f3409f | ||
|
|
82bcb405c3 | ||
|
|
0440df8911 | ||
|
|
25eaf11171 | ||
|
|
3578a8cbec | ||
|
|
4e1158d4df | ||
|
|
0109632b70 | ||
|
|
1650b6b00d | ||
|
|
967a446909 | ||
|
|
4d30329448 | ||
|
|
66967fb903 | ||
|
|
e6854a9147 | ||
|
|
1ca0f536c4 | ||
|
|
d569156a6b | ||
|
|
e7c3d39059 | ||
|
|
9d66379486 | ||
|
|
d01002de06 | ||
|
|
0c857b3d90 | ||
|
|
cc36aa993c | ||
|
|
379bbf3f83 | ||
|
|
a0b1f7091e | ||
|
|
626246bcc0 | ||
|
|
22272d6059 | ||
|
|
ff3be8ae3f | ||
|
|
7c2078b771 |
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.8`).
|
||||
- Error Prone version (e.g. `2.18.0`).
|
||||
- Error Prone Support version (e.g. `0.9.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
|
||||
|
||||
|
||||
29
.github/workflows/build.yml
vendored
29
.github/workflows/build.yml
vendored
@@ -10,36 +10,41 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-22.04 ]
|
||||
jdk: [ 11.0.20, 17.0.8, 21.0.0 ]
|
||||
jdk: [ 17.0.10, 21.0.2 ]
|
||||
distribution: [ temurin ]
|
||||
experimental: [ false ]
|
||||
include:
|
||||
- os: macos-12
|
||||
jdk: 17.0.8
|
||||
- os: macos-14
|
||||
jdk: 17.0.10
|
||||
distribution: temurin
|
||||
experimental: false
|
||||
- os: windows-2022
|
||||
jdk: 17.0.8
|
||||
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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
github.com:443
|
||||
jitpack.io:443
|
||||
repo.maven.apache.org:443
|
||||
# We run the build twice for each supported JDK: once against the
|
||||
# original Error Prone release, using only Error Prone checks available
|
||||
# on Maven Central, and once against the Picnic Error Prone fork,
|
||||
# additionally enabling all checks defined in this project and any Error
|
||||
# Prone checks available only from other artifact repositories.
|
||||
- name: Check out code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
with:
|
||||
java-version: ${{ matrix.jdk }}
|
||||
distribution: ${{ matrix.distribution }}
|
||||
cache: maven
|
||||
java-distribution: ${{ matrix.distribution }}
|
||||
maven-version: 3.9.6
|
||||
- name: Display build environment details
|
||||
run: mvn --version
|
||||
- name: Build project against vanilla Error Prone, compile Javadoc
|
||||
|
||||
27
.github/workflows/codeql.yml
vendored
27
.github/workflows/codeql.yml
vendored
@@ -21,24 +21,31 @@ jobs:
|
||||
security-events: write
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.github.com:443
|
||||
github.com:443
|
||||
objects.githubusercontent.com:443
|
||||
repo.maven.apache.org:443
|
||||
uploads.github.com:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
with:
|
||||
java-version: 17.0.8
|
||||
distribution: temurin
|
||||
cache: maven
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.6
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
|
||||
uses: github/codeql-action/init@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5
|
||||
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@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
|
||||
uses: github/codeql-action/analyze@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5
|
||||
with:
|
||||
category: /language:${{ matrix.language }}
|
||||
|
||||
40
.github/workflows/deploy-website.yml
vendored
40
.github/workflows/deploy-website.yml
vendored
@@ -11,11 +11,38 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.adoptium.net:443
|
||||
api.github.com:443
|
||||
bestpractices.coreinfrastructure.org:443
|
||||
blog.picnic.nl:443
|
||||
errorprone.info:443
|
||||
github.com:443
|
||||
img.shields.io:443
|
||||
index.rubygems.org:443
|
||||
jitpack.io:443
|
||||
maven.apache.org:443
|
||||
objects.githubusercontent.com:443
|
||||
pitest.org:443
|
||||
repo.maven.apache.org:443
|
||||
rubygems.org:443
|
||||
search.maven.org:443
|
||||
securityscorecards.dev:443
|
||||
sonarcloud.io:443
|
||||
www.baeldung.com:443
|
||||
www.bestpractices.dev:443
|
||||
www.youtube.com:443
|
||||
youtrack.jetbrains.com:443
|
||||
- name: Check out code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: ruby/setup-ruby@8575951200e472d5f2d95c625da0c7bec8217c42 # v1.161.0
|
||||
- uses: ruby/setup-ruby@d4526a55538b775af234ba4af27118ed6f8f6677 # v1.172.0
|
||||
with:
|
||||
working-directory: ./website
|
||||
bundler-cache: true
|
||||
@@ -32,7 +59,7 @@ jobs:
|
||||
# "Refaster rules" terminology on our website and in the code.
|
||||
run: bundle exec htmlproofer --disable_external true --check-external-hash false ./_site
|
||||
- name: Upload website as artifact
|
||||
uses: actions/upload-pages-artifact@a753861a5debcf57bf8b404356158c8e1e33150c # v2.0.0
|
||||
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1
|
||||
with:
|
||||
path: ./website/_site
|
||||
deploy:
|
||||
@@ -46,6 +73,13 @@ jobs:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.github.com:443
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@13b55b33dd8996121833dbc1db458c793a334630 # v3.0.1
|
||||
uses: actions/deploy-pages@decdde0ac072f6dcbe43649d82d9c635fff5b4e4 # v4.0.4
|
||||
|
||||
17
.github/workflows/openssf-scorecard.yml
vendored
17
.github/workflows/openssf-scorecard.yml
vendored
@@ -20,6 +20,21 @@ jobs:
|
||||
id-token: write
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.github.com:443
|
||||
api.osv.dev:443
|
||||
api.securityscorecards.dev:443
|
||||
fulcio.sigstore.dev:443
|
||||
github.com:443
|
||||
oss-fuzz-build-logs.storage.googleapis.com:443
|
||||
rekor.sigstore.dev:443
|
||||
tuf-repo-cdn.sigstore.dev:443
|
||||
www.bestpractices.dev:443
|
||||
- name: Check out code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
@@ -31,6 +46,6 @@ jobs:
|
||||
results_format: sarif
|
||||
publish_results: ${{ github.ref == 'refs/heads/master' }}
|
||||
- name: Update GitHub's code scanning dashboard
|
||||
uses: github/codeql-action/upload-sarif@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
|
||||
uses: github/codeql-action/upload-sarif@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
24
.github/workflows/pitest-analyze-pr.yml
vendored
24
.github/workflows/pitest-analyze-pr.yml
vendored
@@ -11,17 +11,21 @@ jobs:
|
||||
analyze-pr:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
fetch-depth: 2
|
||||
persist-credentials: false
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
github.com:443
|
||||
repo.maven.apache.org:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
with:
|
||||
java-version: 17.0.8
|
||||
distribution: temurin
|
||||
cache: maven
|
||||
checkout-fetch-depth: 2
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
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
|
||||
@@ -32,7 +36,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@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: pitest-reports
|
||||
path: ./target/pit-reports-ci
|
||||
|
||||
23
.github/workflows/pitest-update-pr.yml
vendored
23
.github/workflows/pitest-update-pr.yml
vendored
@@ -19,18 +19,23 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.github.com:443
|
||||
github.com:443
|
||||
repo.maven.apache.org:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
with:
|
||||
java-version: 17.0.8
|
||||
distribution: temurin
|
||||
cache: maven
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.6
|
||||
- name: Download Pitest analysis artifact
|
||||
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0
|
||||
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
name: pitest-reports
|
||||
|
||||
32
.github/workflows/run-integration-tests.yml
vendored
32
.github/workflows/run-integration-tests.yml
vendored
@@ -18,26 +18,34 @@ jobs:
|
||||
github.event.issue.pull_request && contains(github.event.comment.body, '/integration-test')
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: refs/pull/${{ github.event.issue.number }}/head
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
checkstyle.org:443
|
||||
github.com:443
|
||||
oss.sonatype.org:443
|
||||
raw.githubusercontent.com:443
|
||||
repo.maven.apache.org:443
|
||||
repository.sonatype.org:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
with:
|
||||
java-version: 17.0.8
|
||||
distribution: temurin
|
||||
cache: maven
|
||||
checkout-ref: "refs/pull/${{ github.event.issue.number }}/head"
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
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/checkstyle-10.12.4.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@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: integration-test-checkstyle-10.12.4
|
||||
name: integration-test-checkstyle
|
||||
path: "${{ runner.temp }}/artifacts"
|
||||
- name: Remove installed project artifacts
|
||||
run: mvn build-helper:remove-project-artifact
|
||||
|
||||
29
.github/workflows/sonarcloud.yml
vendored
29
.github/workflows/sonarcloud.yml
vendored
@@ -11,21 +11,32 @@ permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
analyze:
|
||||
# Analysis of code in forked repositories is skipped, as such workflow runs
|
||||
# do not have access to the requisite secrets.
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
ea6ne4j2sb.execute-api.eu-central-1.amazonaws.com:443
|
||||
github.com:443
|
||||
repo.maven.apache.org:443
|
||||
sc-cleancode-sensorcache-eu-central-1-prod.s3.amazonaws.com:443
|
||||
scanner.sonarcloud.io:443
|
||||
sonarcloud.io:443
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
with:
|
||||
java-version: 17.0.8
|
||||
distribution: temurin
|
||||
cache: maven
|
||||
checkout-fetch-depth: 0
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
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
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
},
|
||||
{
|
||||
"matchDepNames": [
|
||||
"dawidd6/action-download-artifact",
|
||||
"github/codeql-action",
|
||||
"ruby/setup-ruby"
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2023 Picnic Technologies BV
|
||||
Copyright (c) 2017-2024 Picnic Technologies BV
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -49,7 +49,9 @@ high-quality and consistent Java code_][picnic-blog-ep-post].
|
||||
### Installation
|
||||
|
||||
This library is built on top of [Error Prone][error-prone-orig-repo]. To use
|
||||
it, read the installation guide for Maven or Gradle below.
|
||||
it, read the installation guide for Maven or Gradle below. The library requires
|
||||
that your build is executed using JDK 17 or above, but supports builds that
|
||||
[target][baeldung-java-source-target-options] older versions of Java.
|
||||
|
||||
#### Maven
|
||||
|
||||
@@ -65,6 +67,8 @@ it, read the installation guide for Maven or Gradle below.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<!-- Prefer using the latest release. -->
|
||||
<version>3.12.0</version>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<!-- Error Prone itself. -->
|
||||
@@ -94,8 +98,6 @@ it, read the installation guide for Maven or Gradle below.
|
||||
</arg>
|
||||
<arg>-XDcompilePolicy=simple</arg>
|
||||
</compilerArgs>
|
||||
<!-- Some checks raise warnings rather than errors. -->
|
||||
<showWarnings>true</showWarnings>
|
||||
<!-- Enable this if you'd like to fail your build upon warnings. -->
|
||||
<!-- <failOnWarning>true</failOnWarning> -->
|
||||
</configuration>
|
||||
@@ -263,6 +265,7 @@ guidelines][contributing].
|
||||
If you want to report a security vulnerability, please do so through a private
|
||||
channel; please see our [security policy][security] for details.
|
||||
|
||||
[baeldung-java-source-target-options]: https://www.baeldung.com/java-source-target-options
|
||||
[bug-checks]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/
|
||||
[bug-checks-identity-conversion]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IdentityConversion.java
|
||||
[codeql-badge]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/codeql.yml/badge.svg?branch=master&event=push
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
<parent>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.14.1-SNAPSHOT</version>
|
||||
<version>0.16.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>documentation-support</artifactId>
|
||||
|
||||
<name>Picnic :: Error Prone Support :: Documentation Support</name>
|
||||
<description>Data extraction support for the purpose of documentation generation.</description>
|
||||
<url>https://error-prone.picnic.tech</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -40,6 +41,14 @@
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-parameter-names</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto</groupId>
|
||||
<artifactId>auto-common</artifactId>
|
||||
@@ -73,6 +82,8 @@
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- XXX: Explicitly declared as a workaround for
|
||||
https://github.com/pitest/pitest-junit5-plugin/issues/105. -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
|
||||
@@ -4,6 +4,7 @@ import static com.google.common.base.Verify.verify;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.google.auto.common.AnnotationMirrors;
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.auto.value.AutoValue;
|
||||
@@ -17,6 +18,7 @@ import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.tools.javac.code.Attribute;
|
||||
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
||||
import java.net.URI;
|
||||
import java.util.Optional;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import tech.picnic.errorprone.documentation.BugPatternExtractor.BugPatternDocumentation;
|
||||
@@ -45,7 +47,8 @@ public final class BugPatternExtractor implements Extractor<BugPatternDocumentat
|
||||
}
|
||||
|
||||
return Optional.of(
|
||||
new AutoValue_BugPatternExtractor_BugPatternDocumentation(
|
||||
BugPatternDocumentation.create(
|
||||
state.getPath().getCompilationUnit().getSourceFile().toUri(),
|
||||
symbol.getQualifiedName().toString(),
|
||||
annotation.name().isEmpty() ? tree.getSimpleName().toString() : annotation.name(),
|
||||
ImmutableList.copyOf(annotation.altNames()),
|
||||
@@ -91,7 +94,36 @@ public final class BugPatternExtractor implements Extractor<BugPatternDocumentat
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
@JsonDeserialize(as = AutoValue_BugPatternExtractor_BugPatternDocumentation.class)
|
||||
abstract static class BugPatternDocumentation {
|
||||
static BugPatternDocumentation create(
|
||||
URI source,
|
||||
String fullyQualifiedName,
|
||||
String name,
|
||||
ImmutableList<String> altNames,
|
||||
String link,
|
||||
ImmutableList<String> tags,
|
||||
String summary,
|
||||
String explanation,
|
||||
SeverityLevel severityLevel,
|
||||
boolean canDisable,
|
||||
ImmutableList<String> suppressionAnnotations) {
|
||||
return new AutoValue_BugPatternExtractor_BugPatternDocumentation(
|
||||
source,
|
||||
fullyQualifiedName,
|
||||
name,
|
||||
altNames,
|
||||
link,
|
||||
tags,
|
||||
summary,
|
||||
explanation,
|
||||
severityLevel,
|
||||
canDisable,
|
||||
suppressionAnnotations);
|
||||
}
|
||||
|
||||
abstract URI source();
|
||||
|
||||
abstract String fullyQualifiedName();
|
||||
|
||||
abstract String name();
|
||||
|
||||
@@ -4,6 +4,13 @@ import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
|
||||
import static java.util.function.Predicate.not;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -15,6 +22,7 @@ import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -52,7 +60,9 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
|
||||
.map(
|
||||
tests ->
|
||||
new AutoValue_BugPatternTestExtractor_TestCases(
|
||||
ASTHelpers.getSymbol(tree).className(), tests));
|
||||
state.getPath().getCompilationUnit().getSourceFile().toUri(),
|
||||
ASTHelpers.getSymbol(tree).className(),
|
||||
tests));
|
||||
}
|
||||
|
||||
private static final class BugPatternTestCollector
|
||||
@@ -67,7 +77,7 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
|
||||
"com.google.errorprone.CompilationTestHelper",
|
||||
"com.google.errorprone.BugCheckerRefactoringTestHelper")
|
||||
.named("newInstance")
|
||||
.withParameters("java.lang.Class", "java.lang.Class");
|
||||
.withParameters(Class.class.getCanonicalName(), Class.class.getCanonicalName());
|
||||
private static final Matcher<ExpressionTree> IDENTIFICATION_SOURCE_LINES =
|
||||
instanceMethod()
|
||||
.onDescendantOf("com.google.errorprone.CompilationTestHelper")
|
||||
@@ -125,8 +135,8 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
|
||||
}
|
||||
|
||||
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
|
||||
return receiver instanceof MethodInvocationTree
|
||||
? getClassUnderTest((MethodInvocationTree) receiver, state)
|
||||
return receiver instanceof MethodInvocationTree methodInvocation
|
||||
? getClassUnderTest(methodInvocation, state)
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
@@ -144,8 +154,8 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
|
||||
}
|
||||
|
||||
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
|
||||
if (receiver instanceof MethodInvocationTree) {
|
||||
extractIdentificationTestCases((MethodInvocationTree) receiver, sink, state);
|
||||
if (receiver instanceof MethodInvocationTree methodInvocation) {
|
||||
extractIdentificationTestCases(methodInvocation, sink, state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,8 +184,8 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
|
||||
}
|
||||
|
||||
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
|
||||
if (receiver instanceof MethodInvocationTree) {
|
||||
extractReplacementTestCases((MethodInvocationTree) receiver, sink, state);
|
||||
if (receiver instanceof MethodInvocationTree methodInvocation) {
|
||||
extractReplacementTestCases(methodInvocation, sink, state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,32 +208,78 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
@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<TestCase> testCases();
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
@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();
|
||||
|
||||
abstract ImmutableList<TestEntry> entries();
|
||||
}
|
||||
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(AutoValue_BugPatternTestExtractor_IdentificationTestEntry.class),
|
||||
@JsonSubTypes.Type(AutoValue_BugPatternTestExtractor_ReplacementTestEntry.class)
|
||||
})
|
||||
@JsonTypeInfo(include = As.EXISTING_PROPERTY, property = "type", use = JsonTypeInfo.Id.DEDUCTION)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonPropertyOrder("type")
|
||||
interface TestEntry {
|
||||
TestType type();
|
||||
|
||||
String path();
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class ReplacementTestEntry implements TestEntry {
|
||||
abstract String input();
|
||||
|
||||
abstract String output();
|
||||
enum TestType {
|
||||
IDENTIFICATION,
|
||||
REPLACEMENT
|
||||
}
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class IdentificationTestEntry implements TestEntry {
|
||||
static IdentificationTestEntry create(String path, String code) {
|
||||
return new AutoValue_BugPatternTestExtractor_IdentificationTestEntry(path, code);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Override
|
||||
public final TestType type() {
|
||||
return TestType.IDENTIFICATION;
|
||||
}
|
||||
|
||||
abstract String code();
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class ReplacementTestEntry implements TestEntry {
|
||||
static ReplacementTestEntry create(String path, String input, String output) {
|
||||
return new AutoValue_BugPatternTestExtractor_ReplacementTestEntry(path, input, output);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@Override
|
||||
public final TestType type() {
|
||||
return TestType.REPLACEMENT;
|
||||
}
|
||||
|
||||
abstract String input();
|
||||
|
||||
abstract String output();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package tech.picnic.errorprone.documentation;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
@@ -15,10 +10,7 @@ import com.sun.source.util.TaskListener;
|
||||
import com.sun.source.util.TreePath;
|
||||
import com.sun.tools.javac.api.JavacTrees;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -39,9 +31,6 @@ final class DocumentationGeneratorTaskListener implements TaskListener {
|
||||
ServiceLoader.load(
|
||||
Extractor.class, DocumentationGeneratorTaskListener.class.getClassLoader()));
|
||||
|
||||
private static final ObjectMapper OBJECT_MAPPER =
|
||||
new ObjectMapper().setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
|
||||
|
||||
private final Context context;
|
||||
private final Path docsPath;
|
||||
|
||||
@@ -94,13 +83,7 @@ final class DocumentationGeneratorTaskListener implements TaskListener {
|
||||
}
|
||||
|
||||
private <T> void writeToFile(String identifier, String className, T data) {
|
||||
File file = docsPath.resolve(String.format("%s-%s.json", identifier, className)).toFile();
|
||||
|
||||
try (FileWriter fileWriter = new FileWriter(file, UTF_8)) {
|
||||
OBJECT_MAPPER.writeValue(fileWriter, data);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(String.format("Cannot write to file '%s'", file.getPath()), e);
|
||||
}
|
||||
Json.write(docsPath.resolve(String.format("%s-%s.json", identifier, className)), data);
|
||||
}
|
||||
|
||||
private static String getSimpleClassName(URI path) {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package tech.picnic.errorprone.documentation;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.guava.GuavaModule;
|
||||
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
|
||||
import com.google.errorprone.annotations.FormatMethod;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Utility class that offers mutually consistent JSON serialization and deserialization operations,
|
||||
* without further specifying the exact schema used.
|
||||
*/
|
||||
final class Json {
|
||||
private static final ObjectMapper OBJECT_MAPPER =
|
||||
new ObjectMapper()
|
||||
.setVisibility(PropertyAccessor.FIELD, Visibility.ANY)
|
||||
.registerModules(new GuavaModule(), new ParameterNamesModule());
|
||||
|
||||
private Json() {}
|
||||
|
||||
static <T> T read(Path path, Class<T> clazz) {
|
||||
try {
|
||||
return OBJECT_MAPPER.readValue(path.toFile(), clazz);
|
||||
} catch (IOException e) {
|
||||
throw failure(e, "Failure reading from '%s'", path);
|
||||
}
|
||||
}
|
||||
|
||||
static <T> void write(Path path, T object) {
|
||||
try {
|
||||
OBJECT_MAPPER.writeValue(path.toFile(), object);
|
||||
} catch (IOException e) {
|
||||
throw failure(e, "Failure writing to '%s'", path);
|
||||
}
|
||||
}
|
||||
|
||||
@FormatMethod
|
||||
private static UncheckedIOException failure(IOException cause, String format, Object... args) {
|
||||
return new UncheckedIOException(String.format(format, args), cause);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
package tech.picnic.errorprone.documentation;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import java.io.IOException;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.BugPattern;
|
||||
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.BugPatternExtractor.BugPatternDocumentation;
|
||||
|
||||
final class BugPatternExtractorTest {
|
||||
@Test
|
||||
@@ -23,7 +27,7 @@ final class BugPatternExtractorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void minimalBugPattern(@TempDir Path outputDirectory) throws IOException {
|
||||
void minimalBugPattern(@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"MinimalBugChecker.java",
|
||||
@@ -36,11 +40,25 @@ final class BugPatternExtractorTest {
|
||||
"@BugPattern(summary = \"MinimalBugChecker summary\", severity = SeverityLevel.ERROR)",
|
||||
"public final class MinimalBugChecker extends BugChecker {}");
|
||||
|
||||
verifyGeneratedFileContent(outputDirectory, "MinimalBugChecker");
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"MinimalBugChecker",
|
||||
BugPatternDocumentation.create(
|
||||
URI.create("file:///MinimalBugChecker.java"),
|
||||
"pkg.MinimalBugChecker",
|
||||
"MinimalBugChecker",
|
||||
ImmutableList.of(),
|
||||
"",
|
||||
ImmutableList.of(),
|
||||
"MinimalBugChecker summary",
|
||||
"",
|
||||
ERROR,
|
||||
/* canDisable= */ true,
|
||||
ImmutableList.of(SuppressWarnings.class.getCanonicalName())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void completeBugPattern(@TempDir Path outputDirectory) throws IOException {
|
||||
void completeBugPattern(@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"CompleteBugChecker.java",
|
||||
@@ -64,11 +82,25 @@ final class BugPatternExtractorTest {
|
||||
" suppressionAnnotations = {BugPattern.class, Test.class})",
|
||||
"public final class CompleteBugChecker extends BugChecker {}");
|
||||
|
||||
verifyGeneratedFileContent(outputDirectory, "CompleteBugChecker");
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"CompleteBugChecker",
|
||||
BugPatternDocumentation.create(
|
||||
URI.create("file:///CompleteBugChecker.java"),
|
||||
"pkg.CompleteBugChecker",
|
||||
"OtherName",
|
||||
ImmutableList.of("Check"),
|
||||
"https://error-prone.picnic.tech",
|
||||
ImmutableList.of("Simplification"),
|
||||
"CompleteBugChecker summary",
|
||||
"Example explanation",
|
||||
SUGGESTION,
|
||||
/* canDisable= */ false,
|
||||
ImmutableList.of(BugPattern.class.getCanonicalName(), "org.junit.jupiter.api.Test")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void undocumentedSuppressionBugPattern(@TempDir Path outputDirectory) throws IOException {
|
||||
void undocumentedSuppressionBugPattern(@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"UndocumentedSuppressionBugPattern.java",
|
||||
@@ -84,23 +116,27 @@ final class BugPatternExtractorTest {
|
||||
" documentSuppression = false)",
|
||||
"public final class UndocumentedSuppressionBugPattern extends BugChecker {}");
|
||||
|
||||
verifyGeneratedFileContent(outputDirectory, "UndocumentedSuppressionBugPattern");
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"UndocumentedSuppressionBugPattern",
|
||||
BugPatternDocumentation.create(
|
||||
URI.create("file:///UndocumentedSuppressionBugPattern.java"),
|
||||
"pkg.UndocumentedSuppressionBugPattern",
|
||||
"UndocumentedSuppressionBugPattern",
|
||||
ImmutableList.of(),
|
||||
"",
|
||||
ImmutableList.of(),
|
||||
"UndocumentedSuppressionBugPattern summary",
|
||||
"",
|
||||
WARNING,
|
||||
/* canDisable= */ true,
|
||||
ImmutableList.of()));
|
||||
}
|
||||
|
||||
private static void verifyGeneratedFileContent(Path outputDirectory, String testClass)
|
||||
throws IOException {
|
||||
String resourceName = String.format("bugpattern-%s.json", testClass);
|
||||
assertThat(outputDirectory.resolve(resourceName))
|
||||
.content(UTF_8)
|
||||
.isEqualToIgnoringWhitespace(
|
||||
getResource(
|
||||
String.join("-", BugPatternExtractorTest.class.getSimpleName(), resourceName)));
|
||||
}
|
||||
|
||||
// XXX: Once we support only JDK 15+, drop this method in favour of including the resources as
|
||||
// text blocks in this class.
|
||||
private static String getResource(String resourceName) throws IOException {
|
||||
return Resources.toString(
|
||||
Resources.getResource(BugPatternExtractorTest.class, resourceName), UTF_8);
|
||||
private static void verifyGeneratedFileContent(
|
||||
Path outputDirectory, String testClass, BugPatternDocumentation expected) {
|
||||
assertThat(outputDirectory.resolve(String.format("bugpattern-%s.json", testClass)))
|
||||
.exists()
|
||||
.returns(expected, path -> Json.read(path, BugPatternDocumentation.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package tech.picnic.errorprone.documentation;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import java.io.IOException;
|
||||
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.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
|
||||
@@ -246,7 +249,7 @@ final class BugPatternTestExtractorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleFileCompilationTestHelper(@TempDir Path outputDirectory) throws IOException {
|
||||
void singleFileCompilationTestHelper(@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"SingleFileCompilationTestHelperTest.java",
|
||||
@@ -263,12 +266,22 @@ final class BugPatternTestExtractorTest {
|
||||
" }",
|
||||
"}");
|
||||
|
||||
verifyGeneratedFileContent(outputDirectory, "SingleFileCompilationTestHelperTest");
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"SingleFileCompilationTestHelperTest",
|
||||
TestCases.create(
|
||||
URI.create("file:///SingleFileCompilationTestHelperTest.java"),
|
||||
"SingleFileCompilationTestHelperTest",
|
||||
ImmutableList.of(
|
||||
TestCase.create(
|
||||
"SingleFileCompilationTestHelperTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
IdentificationTestEntry.create(
|
||||
"A.java", "// BUG: Diagnostic contains:\nclass A {}\n"))))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleFileCompilationTestHelperWithSetArgs(@TempDir Path outputDirectory)
|
||||
throws IOException {
|
||||
void singleFileCompilationTestHelperWithSetArgs(@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"SingleFileCompilationTestHelperWithSetArgsTest.java",
|
||||
@@ -286,11 +299,22 @@ final class BugPatternTestExtractorTest {
|
||||
" }",
|
||||
"}");
|
||||
|
||||
verifyGeneratedFileContent(outputDirectory, "SingleFileCompilationTestHelperWithSetArgsTest");
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"SingleFileCompilationTestHelperWithSetArgsTest",
|
||||
TestCases.create(
|
||||
URI.create("file:///SingleFileCompilationTestHelperWithSetArgsTest.java"),
|
||||
"SingleFileCompilationTestHelperWithSetArgsTest",
|
||||
ImmutableList.of(
|
||||
TestCase.create(
|
||||
"SingleFileCompilationTestHelperWithSetArgsTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
IdentificationTestEntry.create(
|
||||
"A.java", "// BUG: Diagnostic contains:\nclass A {}\n"))))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void multiFileCompilationTestHelper(@TempDir Path outputDirectory) throws IOException {
|
||||
void multiFileCompilationTestHelper(@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"MultiFileCompilationTestHelperTest.java",
|
||||
@@ -308,11 +332,24 @@ final class BugPatternTestExtractorTest {
|
||||
" }",
|
||||
"}");
|
||||
|
||||
verifyGeneratedFileContent(outputDirectory, "MultiFileCompilationTestHelperTest");
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"MultiFileCompilationTestHelperTest",
|
||||
TestCases.create(
|
||||
URI.create("file:///MultiFileCompilationTestHelperTest.java"),
|
||||
"MultiFileCompilationTestHelperTest",
|
||||
ImmutableList.of(
|
||||
TestCase.create(
|
||||
"MultiFileCompilationTestHelperTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
IdentificationTestEntry.create(
|
||||
"A.java", "// BUG: Diagnostic contains:\nclass A {}\n"),
|
||||
IdentificationTestEntry.create(
|
||||
"B.java", "// BUG: Diagnostic contains:\nclass B {}\n"))))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleFileBugCheckerRefactoringTestHelper(@TempDir Path outputDirectory) throws IOException {
|
||||
void singleFileBugCheckerRefactoringTestHelper(@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"SingleFileBugCheckerRefactoringTestHelperTest.java",
|
||||
@@ -330,12 +367,23 @@ final class BugPatternTestExtractorTest {
|
||||
" }",
|
||||
"}");
|
||||
|
||||
verifyGeneratedFileContent(outputDirectory, "SingleFileBugCheckerRefactoringTestHelperTest");
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"SingleFileBugCheckerRefactoringTestHelperTest",
|
||||
TestCases.create(
|
||||
URI.create("file:///SingleFileBugCheckerRefactoringTestHelperTest.java"),
|
||||
"SingleFileBugCheckerRefactoringTestHelperTest",
|
||||
ImmutableList.of(
|
||||
TestCase.create(
|
||||
"SingleFileBugCheckerRefactoringTestHelperTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
ReplacementTestEntry.create(
|
||||
"A.java", "class A {}\n", "class A { /* This is a change. */ }\n"))))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestMode(
|
||||
@TempDir Path outputDirectory) throws IOException {
|
||||
@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest.java",
|
||||
@@ -359,11 +407,21 @@ final class BugPatternTestExtractorTest {
|
||||
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest");
|
||||
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest",
|
||||
TestCases.create(
|
||||
URI.create(
|
||||
"file:///SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest.java"),
|
||||
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest",
|
||||
ImmutableList.of(
|
||||
TestCase.create(
|
||||
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
ReplacementTestEntry.create(
|
||||
"A.java", "class A {}\n", "class A { /* This is a change. */ }\n"))))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void multiFileBugCheckerRefactoringTestHelper(@TempDir Path outputDirectory) throws IOException {
|
||||
void multiFileBugCheckerRefactoringTestHelper(@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"MultiFileBugCheckerRefactoringTestHelperTest.java",
|
||||
@@ -383,12 +441,24 @@ final class BugPatternTestExtractorTest {
|
||||
" }",
|
||||
"}");
|
||||
|
||||
verifyGeneratedFileContent(outputDirectory, "MultiFileBugCheckerRefactoringTestHelperTest");
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"MultiFileBugCheckerRefactoringTestHelperTest",
|
||||
TestCases.create(
|
||||
URI.create("file:///MultiFileBugCheckerRefactoringTestHelperTest.java"),
|
||||
"MultiFileBugCheckerRefactoringTestHelperTest",
|
||||
ImmutableList.of(
|
||||
TestCase.create(
|
||||
"MultiFileBugCheckerRefactoringTestHelperTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
ReplacementTestEntry.create(
|
||||
"A.java", "class A {}\n", "class A { /* This is a change. */ }\n"),
|
||||
ReplacementTestEntry.create(
|
||||
"B.java", "class B {}\n", "class B { /* This is a change. */ }\n"))))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void compilationAndBugCheckerRefactoringTestHelpers(@TempDir Path outputDirectory)
|
||||
throws IOException {
|
||||
void compilationAndBugCheckerRefactoringTestHelpers(@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"CompilationAndBugCheckerRefactoringTestHelpersTest.java",
|
||||
@@ -412,12 +482,27 @@ final class BugPatternTestExtractorTest {
|
||||
"}");
|
||||
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory, "CompilationAndBugCheckerRefactoringTestHelpersTest");
|
||||
outputDirectory,
|
||||
"CompilationAndBugCheckerRefactoringTestHelpersTest",
|
||||
TestCases.create(
|
||||
URI.create("file:///CompilationAndBugCheckerRefactoringTestHelpersTest.java"),
|
||||
"CompilationAndBugCheckerRefactoringTestHelpersTest",
|
||||
ImmutableList.of(
|
||||
TestCase.create(
|
||||
"CompilationAndBugCheckerRefactoringTestHelpersTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
IdentificationTestEntry.create(
|
||||
"A.java", "// BUG: Diagnostic contains:\nclass A {}\n"))),
|
||||
TestCase.create(
|
||||
"CompilationAndBugCheckerRefactoringTestHelpersTest.TestChecker",
|
||||
ImmutableList.of(
|
||||
ReplacementTestEntry.create(
|
||||
"A.java", "class A {}\n", "class A { /* This is a change. */ }\n"))))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void compilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNames(
|
||||
@TempDir Path outputDirectory) throws IOException {
|
||||
@TempDir Path outputDirectory) {
|
||||
Compilation.compileWithDocumentationGenerator(
|
||||
outputDirectory,
|
||||
"CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.java",
|
||||
@@ -446,23 +531,28 @@ final class BugPatternTestExtractorTest {
|
||||
|
||||
verifyGeneratedFileContent(
|
||||
outputDirectory,
|
||||
"CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest");
|
||||
"CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest",
|
||||
TestCases.create(
|
||||
URI.create(
|
||||
"file:///CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.java"),
|
||||
"pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest",
|
||||
ImmutableList.of(
|
||||
TestCase.create(
|
||||
"pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.CustomTestChecker",
|
||||
ImmutableList.of(
|
||||
IdentificationTestEntry.create(
|
||||
"A.java", "// BUG: Diagnostic contains:\nclass A {}\n"))),
|
||||
TestCase.create(
|
||||
"pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.CustomTestChecker2",
|
||||
ImmutableList.of(
|
||||
ReplacementTestEntry.create(
|
||||
"A.java", "class A {}\n", "class A { /* This is a change. */ }\n"))))));
|
||||
}
|
||||
|
||||
private static void verifyGeneratedFileContent(Path outputDirectory, String testClass)
|
||||
throws IOException {
|
||||
String resourceName = String.format("bugpattern-test-%s.json", testClass);
|
||||
assertThat(outputDirectory.resolve(resourceName))
|
||||
.content(UTF_8)
|
||||
.isEqualToIgnoringWhitespace(
|
||||
getResource(
|
||||
String.join("-", BugPatternTestExtractorTest.class.getSimpleName(), resourceName)));
|
||||
}
|
||||
|
||||
// XXX: Once we support only JDK 15+, drop this method in favour of including the resources as
|
||||
// text blocks in this class.
|
||||
private static String getResource(String resourceName) throws IOException {
|
||||
return Resources.toString(
|
||||
Resources.getResource(BugPatternTestExtractorTest.class, resourceName), UTF_8);
|
||||
private static void verifyGeneratedFileContent(
|
||||
Path outputDirectory, String testClass, TestCases expected) {
|
||||
assertThat(outputDirectory.resolve(String.format("bugpattern-test-%s.json", testClass)))
|
||||
.exists()
|
||||
.returns(expected, path -> Json.read(path, TestCases.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,13 +93,20 @@ final class DocumentationGeneratorTaskListenerTest {
|
||||
"DocumentationGeneratorTaskListenerTestClass.java",
|
||||
"class DocumentationGeneratorTaskListenerTestClass {}");
|
||||
|
||||
// XXX: Once we support only JDK 15+, use a text block for the `expected` string.
|
||||
assertThat(
|
||||
outputDirectory.resolve(
|
||||
"documentation-generator-task-listener-test-DocumentationGeneratorTaskListenerTestClass.json"))
|
||||
.content(UTF_8)
|
||||
.isEqualToIgnoringWhitespace(
|
||||
"{\"className\":\"DocumentationGeneratorTaskListenerTestClass\",\"path\":[\"CLASS: DocumentationGeneratorTaskListenerTestClass\",\"COMPILATION_UNIT\"]}");
|
||||
"""
|
||||
{
|
||||
"className": "DocumentationGeneratorTaskListenerTestClass",
|
||||
"path": [
|
||||
"CLASS: DocumentationGeneratorTaskListenerTestClass",
|
||||
"COMPILATION_UNIT"
|
||||
]
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@@ -125,8 +132,8 @@ final class DocumentationGeneratorTaskListenerTest {
|
||||
}
|
||||
|
||||
private static String describeTree(Tree tree) {
|
||||
return (tree instanceof ClassTree)
|
||||
? String.join(": ", String.valueOf(tree.getKind()), ((ClassTree) tree).getSimpleName())
|
||||
return (tree instanceof ClassTree clazz)
|
||||
? String.join(": ", String.valueOf(tree.getKind()), clazz.getSimpleName())
|
||||
: tree.getKind().toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package tech.picnic.errorprone.documentation;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
final class JsonTest {
|
||||
private static final TestObject TEST_OBJECT = new AutoValue_JsonTest_TestObject("foo", 42);
|
||||
private static final String TEST_JSON = "{\"string\":\"foo\",\"number\":42}";
|
||||
|
||||
@Test
|
||||
void write(@TempDir Path directory) {
|
||||
Path file = directory.resolve("test.json");
|
||||
|
||||
Json.write(file, TEST_OBJECT);
|
||||
|
||||
assertThat(file).content(UTF_8).isEqualTo(TEST_JSON);
|
||||
}
|
||||
|
||||
@Test
|
||||
void writeFailure(@TempDir Path directory) {
|
||||
assertThatThrownBy(() -> Json.write(directory, TEST_OBJECT))
|
||||
.isInstanceOf(UncheckedIOException.class)
|
||||
.hasMessageContaining("Failure writing to '%s'", directory)
|
||||
.hasCauseInstanceOf(FileNotFoundException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void read(@TempDir Path directory) throws IOException {
|
||||
Path file = directory.resolve("test.json");
|
||||
|
||||
Files.writeString(file, TEST_JSON, UTF_8);
|
||||
|
||||
assertThat(Json.read(file, TestObject.class)).isEqualTo(TEST_OBJECT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readFailure(@TempDir Path directory) {
|
||||
assertThatThrownBy(() -> Json.read(directory, TestObject.class))
|
||||
.isInstanceOf(UncheckedIOException.class)
|
||||
.hasMessageContaining("Failure reading from '%s'", directory)
|
||||
.hasCauseInstanceOf(FileNotFoundException.class);
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
@JsonDeserialize(as = AutoValue_JsonTest_TestObject.class)
|
||||
abstract static class TestObject {
|
||||
abstract String string();
|
||||
|
||||
abstract int number();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"fullyQualifiedName": "pkg.CompleteBugChecker",
|
||||
"name": "OtherName",
|
||||
"altNames": [
|
||||
"Check"
|
||||
],
|
||||
"link": "https://error-prone.picnic.tech",
|
||||
"tags": [
|
||||
"Simplification"
|
||||
],
|
||||
"summary": "CompleteBugChecker summary",
|
||||
"explanation": "Example explanation",
|
||||
"severityLevel": "SUGGESTION",
|
||||
"canDisable": false,
|
||||
"suppressionAnnotations": [
|
||||
"com.google.errorprone.BugPattern",
|
||||
"org.junit.jupiter.api.Test"
|
||||
]
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"fullyQualifiedName": "pkg.MinimalBugChecker",
|
||||
"name": "MinimalBugChecker",
|
||||
"altNames": [],
|
||||
"link": "",
|
||||
"tags": [],
|
||||
"summary": "MinimalBugChecker summary",
|
||||
"explanation": "",
|
||||
"severityLevel": "ERROR",
|
||||
"canDisable": true,
|
||||
"suppressionAnnotations": [
|
||||
"java.lang.SuppressWarnings"
|
||||
]
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"fullyQualifiedName": "pkg.UndocumentedSuppressionBugPattern",
|
||||
"name": "UndocumentedSuppressionBugPattern",
|
||||
"altNames": [],
|
||||
"link": "",
|
||||
"tags": [],
|
||||
"summary": "UndocumentedSuppressionBugPattern summary",
|
||||
"explanation": "",
|
||||
"severityLevel": "WARNING",
|
||||
"canDisable": true,
|
||||
"suppressionAnnotations": []
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"testClass": "CompilationAndBugCheckerRefactoringTestHelpersTest",
|
||||
"testCases": [
|
||||
{
|
||||
"classUnderTest": "CompilationAndBugCheckerRefactoringTestHelpersTest.TestChecker",
|
||||
"entries": [
|
||||
{
|
||||
"path": "A.java",
|
||||
"code": "// BUG: Diagnostic contains:\nclass A {}\n"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"classUnderTest": "CompilationAndBugCheckerRefactoringTestHelpersTest.TestChecker",
|
||||
"entries": [
|
||||
{
|
||||
"path": "A.java",
|
||||
"input": "class A {}\n",
|
||||
"output": "class A { /* This is a change. */ }\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"testClass": "pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest",
|
||||
"testCases": [
|
||||
{
|
||||
"classUnderTest": "pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.CustomTestChecker",
|
||||
"entries": [
|
||||
{
|
||||
"path": "A.java",
|
||||
"code": "// BUG: Diagnostic contains:\nclass A {}\n"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"classUnderTest": "pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.CustomTestChecker2",
|
||||
"entries": [
|
||||
{
|
||||
"path": "A.java",
|
||||
"input": "class A {}\n",
|
||||
"output": "class A { /* This is a change. */ }\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"testClass": "MultiFileBugCheckerRefactoringTestHelperTest",
|
||||
"testCases": [
|
||||
{
|
||||
"classUnderTest": "MultiFileBugCheckerRefactoringTestHelperTest.TestChecker",
|
||||
"entries": [
|
||||
{
|
||||
"path": "A.java",
|
||||
"input": "class A {}\n",
|
||||
"output": "class A { /* This is a change. */ }\n"
|
||||
},
|
||||
{
|
||||
"path": "B.java",
|
||||
"input": "class B {}\n",
|
||||
"output": "class B { /* This is a change. */ }\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"testClass": "MultiFileCompilationTestHelperTest",
|
||||
"testCases": [
|
||||
{
|
||||
"classUnderTest": "MultiFileCompilationTestHelperTest.TestChecker",
|
||||
"entries": [
|
||||
{
|
||||
"path": "A.java",
|
||||
"code": "// BUG: Diagnostic contains:\nclass A {}\n"
|
||||
},
|
||||
{
|
||||
"path": "B.java",
|
||||
"code": "// BUG: Diagnostic contains:\nclass B {}\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"testClass": "SingleFileBugCheckerRefactoringTestHelperTest",
|
||||
"testCases": [
|
||||
{
|
||||
"classUnderTest": "SingleFileBugCheckerRefactoringTestHelperTest.TestChecker",
|
||||
"entries": [
|
||||
{
|
||||
"path": "A.java",
|
||||
"input": "class A {}\n",
|
||||
"output": "class A { /* This is a change. */ }\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"testClass": "SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest",
|
||||
"testCases": [
|
||||
{
|
||||
"classUnderTest": "SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest.TestChecker",
|
||||
"entries": [
|
||||
{
|
||||
"path": "A.java",
|
||||
"input": "class A {}\n",
|
||||
"output": "class A { /* This is a change. */ }\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"testClass": "SingleFileCompilationTestHelperTest",
|
||||
"testCases": [
|
||||
{
|
||||
"classUnderTest": "SingleFileCompilationTestHelperTest.TestChecker",
|
||||
"entries": [
|
||||
{
|
||||
"path": "A.java",
|
||||
"code": "// BUG: Diagnostic contains:\nclass A {}\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"testClass": "SingleFileCompilationTestHelperWithSetArgsTest",
|
||||
"testCases": [
|
||||
{
|
||||
"classUnderTest": "SingleFileCompilationTestHelperWithSetArgsTest.TestChecker",
|
||||
"entries": [
|
||||
{
|
||||
"path": "A.java",
|
||||
"code": "// BUG: Diagnostic contains:\nclass A {}\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -5,13 +5,14 @@
|
||||
<parent>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.14.1-SNAPSHOT</version>
|
||||
<version>0.16.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>error-prone-contrib</artifactId>
|
||||
|
||||
<name>Picnic :: Error Prone Support :: Contrib</name>
|
||||
<description>Extra Error Prone plugins by Picnic.</description>
|
||||
<url>https://error-prone.picnic.tech</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -47,6 +48,10 @@
|
||||
`annotationProcessorPaths` configuration below. -->
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>error-prone-utils</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>refaster-support</artifactId>
|
||||
@@ -60,11 +65,6 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto</groupId>
|
||||
<artifactId>auto-common</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -77,10 +77,6 @@
|
||||
<artifactId>auto-value-annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.googlejavaformat</groupId>
|
||||
<artifactId>google-java-format</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
@@ -161,6 +157,8 @@
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- XXX: Explicitly declared as a workaround for
|
||||
https://github.com/pitest/pitest-junit5-plugin/issues/105. -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
@@ -181,6 +179,31 @@
|
||||
<artifactId>mongodb-driver-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
<artifactId>rewrite-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
<artifactId>rewrite-java</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
<artifactId>rewrite-java-11</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
<artifactId>rewrite-templating</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
<artifactId>rewrite-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.reactivestreams</groupId>
|
||||
<artifactId>reactive-streams</artifactId>
|
||||
@@ -216,6 +239,11 @@
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
@@ -231,6 +259,9 @@
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths combine.children="append">
|
||||
<!-- XXX: Drop the version declarations once
|
||||
properly supported. See
|
||||
https://youtrack.jetbrains.com/issue/IDEA-342187. -->
|
||||
<path>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>documentation-support</artifactId>
|
||||
@@ -248,7 +279,6 @@
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs combine.children="append">
|
||||
<arg>-Xplugin:RefasterRuleCompiler</arg>
|
||||
<arg>-Xplugin:DocumentationGenerator -XoutputDirectory=${project.build.directory}/docs</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
|
||||
@@ -4,7 +4,7 @@ import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.LIKELY_ERROR;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -18,7 +18,7 @@ import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import java.util.Map;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
@@ -46,7 +46,7 @@ public final class AmbiguousJsonCreator extends BugChecker implements Annotation
|
||||
}
|
||||
|
||||
ClassTree clazz = state.findEnclosing(ClassTree.class);
|
||||
if (clazz == null || clazz.getKind() != Tree.Kind.ENUM) {
|
||||
if (clazz == null || clazz.getKind() != Kind.ENUM) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import static com.google.errorprone.matchers.Matchers.argument;
|
||||
import static com.google.errorprone.matchers.Matchers.argumentCount;
|
||||
import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.nullLiteral;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
|
||||
@@ -6,7 +6,7 @@ import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
|
||||
import static com.google.errorprone.matchers.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
|
||||
import static com.google.errorprone.matchers.Matchers.annotations;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -23,7 +23,7 @@ import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import java.util.List;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} that flags redundant {@code @Autowired} constructor annotations. */
|
||||
@AutoService(BugChecker.class)
|
||||
|
||||
@@ -3,7 +3,7 @@ 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.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -19,14 +19,13 @@ import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.AssignmentTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.NewArrayTree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} that flags annotations that could be written more concisely. */
|
||||
@AutoService(BugChecker.class)
|
||||
@@ -119,7 +118,7 @@ public final class CanonicalAnnotationSyntax extends BugChecker implements Annot
|
||||
* the expression as a whole.
|
||||
*/
|
||||
ExpressionTree value =
|
||||
(arg.getKind() == Kind.ASSIGNMENT) ? ((AssignmentTree) arg).getExpression() : arg;
|
||||
(arg instanceof AssignmentTree assignment) ? assignment.getExpression() : arg;
|
||||
|
||||
/* Store a fix for each expression that was successfully simplified. */
|
||||
simplifyAttributeValue(value, state)
|
||||
@@ -130,13 +129,10 @@ public final class CanonicalAnnotationSyntax extends BugChecker implements Annot
|
||||
}
|
||||
|
||||
private static Optional<String> simplifyAttributeValue(ExpressionTree expr, VisitorState state) {
|
||||
if (expr.getKind() != Kind.NEW_ARRAY) {
|
||||
/* There are no curly braces or commas to be dropped here. */
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
NewArrayTree array = (NewArrayTree) expr;
|
||||
return simplifySingletonArray(array, state).or(() -> dropTrailingComma(array, state));
|
||||
/* Drop curly braces or commas if possible. */
|
||||
return expr instanceof NewArrayTree newArray
|
||||
? simplifySingletonArray(newArray, state).or(() -> dropTrailingComma(newArray, state))
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
/** Returns the expression describing the array's sole element, if any. */
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
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.FRAGILE_CODE;
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
import static com.google.errorprone.matchers.Matchers.anything;
|
||||
import static com.google.errorprone.matchers.Matchers.classLiteral;
|
||||
import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.receiverOfInvocation;
|
||||
import static com.google.errorprone.matchers.Matchers.toType;
|
||||
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.annotations.Var;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
|
||||
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.BinaryTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.util.TreePath;
|
||||
import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags invocations of {@link Class#getName()} where {@link
|
||||
* Class#getCanonicalName()} was likely meant.
|
||||
*
|
||||
* <p>For top-level types these two methods generally return the same result, but for nested types
|
||||
* the former separates identifiers using a dollar sign ({@code $}) rather than a dot ({@code .}).
|
||||
*
|
||||
* @implNote This check currently only flags {@link Class#getName()} invocations on class literals,
|
||||
* and doesn't flag method references. This avoids false positives, such as suggesting use of
|
||||
* {@link Class#getCanonicalName()} in contexts where the canonical name is {@code null}.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "This code should likely use the type's canonical name",
|
||||
link = BUG_PATTERNS_BASE_URL + "CanonicalClassNameUsage",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = FRAGILE_CODE)
|
||||
public final class CanonicalClassNameUsage extends BugChecker
|
||||
implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> GET_NAME_INVOCATION =
|
||||
toType(
|
||||
MethodInvocationTree.class,
|
||||
allOf(
|
||||
receiverOfInvocation(classLiteral(anything())),
|
||||
instanceMethod().onExactClass(Class.class.getCanonicalName()).named("getName")));
|
||||
private static final Pattern CANONICAL_NAME_USING_TYPES =
|
||||
Pattern.compile("(com\\.google\\.errorprone|tech\\.picnic\\.errorprone)\\..*");
|
||||
|
||||
/** Instantiates a new {@link CanonicalClassNameUsage} instance. */
|
||||
public CanonicalClassNameUsage() {}
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
if (!GET_NAME_INVOCATION.matches(tree, state) || !isPassedToCanonicalNameUsingType(state)) {
|
||||
/*
|
||||
* This is not a `class.getName()` invocation of which the result is passed to another method
|
||||
* known to accept canonical type names.
|
||||
*/
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
return describeMatch(
|
||||
tree, SuggestedFixes.renameMethodInvocation(tree, "getCanonicalName", state));
|
||||
}
|
||||
|
||||
private static boolean isPassedToCanonicalNameUsingType(VisitorState state) {
|
||||
@Var TreePath path = state.getPath().getParentPath();
|
||||
while (path.getLeaf() instanceof BinaryTree) {
|
||||
path = path.getParentPath();
|
||||
}
|
||||
|
||||
return path.getLeaf() instanceof MethodInvocationTree methodInvocation
|
||||
&& isOwnedByCanonicalNameUsingType(ASTHelpers.getSymbol(methodInvocation));
|
||||
}
|
||||
|
||||
private static boolean isOwnedByCanonicalNameUsingType(MethodSymbol symbol) {
|
||||
return CANONICAL_NAME_USING_TYPES.matcher(symbol.owner.getQualifiedName()).matches();
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,12 @@ import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.FRAGILE_CODE;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
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.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
@@ -17,8 +20,12 @@ import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.Collector;
|
||||
import tech.picnic.errorprone.bugpatterns.util.ThirdPartyLibrary;
|
||||
import java.util.stream.Collectors;
|
||||
import tech.picnic.errorprone.utils.ThirdPartyLibrary;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags {@link Collector Collectors} that don't clearly express
|
||||
@@ -38,7 +45,7 @@ import tech.picnic.errorprone.bugpatterns.util.ThirdPartyLibrary;
|
||||
public final class CollectorMutability extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> COLLECTOR_METHOD =
|
||||
staticMethod().onClass("java.util.stream.Collectors");
|
||||
staticMethod().onClass(Collectors.class.getCanonicalName());
|
||||
private static final Matcher<ExpressionTree> LIST_COLLECTOR =
|
||||
staticMethod().anyClass().named("toList");
|
||||
private static final Matcher<ExpressionTree> MAP_COLLECTOR =
|
||||
@@ -58,7 +65,10 @@ public final class CollectorMutability extends BugChecker implements MethodInvoc
|
||||
|
||||
if (LIST_COLLECTOR.matches(tree, state)) {
|
||||
return suggestToCollectionAlternatives(
|
||||
tree, "com.google.common.collect.ImmutableList.toImmutableList", "ArrayList", state);
|
||||
tree,
|
||||
ImmutableList.class.getCanonicalName() + ".toImmutableList",
|
||||
ArrayList.class.getCanonicalName(),
|
||||
state);
|
||||
}
|
||||
|
||||
if (MAP_COLLECTOR.matches(tree, state)) {
|
||||
@@ -67,7 +77,10 @@ public final class CollectorMutability extends BugChecker implements MethodInvoc
|
||||
|
||||
if (SET_COLLECTOR.matches(tree, state)) {
|
||||
return suggestToCollectionAlternatives(
|
||||
tree, "com.google.common.collect.ImmutableSet.toImmutableSet", "HashSet", state);
|
||||
tree,
|
||||
ImmutableSet.class.getCanonicalName() + ".toImmutableSet",
|
||||
HashSet.class.getCanonicalName(),
|
||||
state);
|
||||
}
|
||||
|
||||
return Description.NO_MATCH;
|
||||
@@ -75,20 +88,20 @@ public final class CollectorMutability extends BugChecker implements MethodInvoc
|
||||
|
||||
private Description suggestToCollectionAlternatives(
|
||||
MethodInvocationTree tree,
|
||||
String fullyQualifiedImmutableReplacement,
|
||||
String immutableReplacement,
|
||||
String mutableReplacement,
|
||||
VisitorState state) {
|
||||
SuggestedFix.Builder mutableFix = SuggestedFix.builder();
|
||||
String toCollectionSelect =
|
||||
SuggestedFixes.qualifyStaticImport(
|
||||
"java.util.stream.Collectors.toCollection", mutableFix, state);
|
||||
Collectors.class.getCanonicalName() + ".toCollection", mutableFix, state);
|
||||
String mutableCollection = SuggestedFixes.qualifyType(state, mutableFix, mutableReplacement);
|
||||
|
||||
return buildDescription(tree)
|
||||
.addFix(replaceMethodInvocation(tree, fullyQualifiedImmutableReplacement, state))
|
||||
.addFix(replaceMethodInvocation(tree, immutableReplacement, state))
|
||||
.addFix(
|
||||
mutableFix
|
||||
.addImport(String.format("java.util.%s", mutableReplacement))
|
||||
.replace(tree, String.format("%s(%s::new)", toCollectionSelect, mutableReplacement))
|
||||
.replace(tree, String.format("%s(%s::new)", toCollectionSelect, mutableCollection))
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
@@ -99,17 +112,20 @@ public final class CollectorMutability extends BugChecker implements MethodInvoc
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
SuggestedFix.Builder mutableFix = SuggestedFix.builder();
|
||||
String hashMap =
|
||||
SuggestedFixes.qualifyType(state, mutableFix, HashMap.class.getCanonicalName());
|
||||
|
||||
return buildDescription(tree)
|
||||
.addFix(
|
||||
replaceMethodInvocation(
|
||||
tree, "com.google.common.collect.ImmutableMap.toImmutableMap", state))
|
||||
tree, ImmutableMap.class.getCanonicalName() + ".toImmutableMap", state))
|
||||
.addFix(
|
||||
SuggestedFix.builder()
|
||||
.addImport("java.util.HashMap")
|
||||
mutableFix
|
||||
.postfixWith(
|
||||
tree.getArguments().get(argCount - 1),
|
||||
(argCount == 2 ? ", (a, b) -> { throw new IllegalStateException(); }" : "")
|
||||
+ ", HashMap::new")
|
||||
+ String.format(", %s::new", hashMap))
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import static com.google.errorprone.matchers.Matchers.not;
|
||||
import static com.google.errorprone.matchers.Matchers.returnStatement;
|
||||
import static com.google.errorprone.matchers.Matchers.staticMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.toType;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.Streams;
|
||||
@@ -39,8 +39,8 @@ import com.sun.tools.javac.code.Symbol;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.bugpatterns.util.MoreASTHelpers;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.MoreASTHelpers;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags unnecessary local variable assignments preceding a return
|
||||
@@ -58,7 +58,10 @@ public final class DirectReturn extends BugChecker implements BlockTreeMatcher {
|
||||
private static final Matcher<StatementTree> VARIABLE_RETURN = returnStatement(isVariable());
|
||||
private static final Matcher<ExpressionTree> MOCKITO_MOCK_OR_SPY_WITH_IMPLICIT_TYPE =
|
||||
allOf(
|
||||
not(toType(MethodInvocationTree.class, argument(0, isSameType(Class.class.getName())))),
|
||||
not(
|
||||
toType(
|
||||
MethodInvocationTree.class,
|
||||
argument(0, isSameType(Class.class.getCanonicalName())))),
|
||||
staticMethod().onClass("org.mockito.Mockito").namedAnyOf("mock", "spy"));
|
||||
|
||||
/** Instantiates a new {@link DirectReturn} instance. */
|
||||
@@ -98,19 +101,17 @@ public final class DirectReturn extends BugChecker implements BlockTreeMatcher {
|
||||
}
|
||||
|
||||
private static Optional<ExpressionTree> tryMatchAssignment(Symbol targetSymbol, Tree tree) {
|
||||
if (tree instanceof ExpressionStatementTree) {
|
||||
return tryMatchAssignment(targetSymbol, ((ExpressionStatementTree) tree).getExpression());
|
||||
if (tree instanceof ExpressionStatementTree expressionStatement) {
|
||||
return tryMatchAssignment(targetSymbol, expressionStatement.getExpression());
|
||||
}
|
||||
|
||||
if (tree instanceof AssignmentTree) {
|
||||
AssignmentTree assignment = (AssignmentTree) tree;
|
||||
if (tree instanceof AssignmentTree assignment) {
|
||||
return targetSymbol.equals(ASTHelpers.getSymbol(assignment.getVariable()))
|
||||
? Optional.of(assignment.getExpression())
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
if (tree instanceof VariableTree) {
|
||||
VariableTree declaration = (VariableTree) tree;
|
||||
if (tree instanceof VariableTree declaration) {
|
||||
return declaration.getModifiers().getAnnotations().isEmpty()
|
||||
&& targetSymbol.equals(ASTHelpers.getSymbol(declaration))
|
||||
? Optional.ofNullable(declaration.getInitializer())
|
||||
@@ -148,11 +149,11 @@ public final class DirectReturn extends BugChecker implements BlockTreeMatcher {
|
||||
Streams.stream(state.getPath()).skip(1),
|
||||
Streams.stream(state.getPath()),
|
||||
(tree, child) -> {
|
||||
if (!(tree instanceof TryTree)) {
|
||||
if (!(tree instanceof TryTree tryTree)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BlockTree finallyBlock = ((TryTree) tree).getFinallyBlock();
|
||||
BlockTree finallyBlock = tryTree.getFinallyBlock();
|
||||
return !child.equals(finallyBlock) ? finallyBlock : null;
|
||||
})
|
||||
.anyMatch(finallyBlock -> referencesIdentifierSymbol(symbol, finallyBlock));
|
||||
|
||||
@@ -7,7 +7,7 @@ import static com.google.errorprone.matchers.ChildMultiMatcher.MatchType.AT_LEAS
|
||||
import static com.google.errorprone.matchers.Matchers.annotations;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -21,7 +21,7 @@ import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} that flags empty methods that seemingly can simply be deleted. */
|
||||
@AutoService(BugChecker.class)
|
||||
@@ -36,7 +36,9 @@ public final class EmptyMethod extends BugChecker implements MethodTreeMatcher {
|
||||
private static final Matcher<Tree> PERMITTED_ANNOTATION =
|
||||
annotations(
|
||||
AT_LEAST_ONE,
|
||||
anyOf(isType("java.lang.Override"), isType("org.aspectj.lang.annotation.Pointcut")));
|
||||
anyOf(
|
||||
isType(Override.class.getCanonicalName()),
|
||||
isType("org.aspectj.lang.annotation.Pointcut")));
|
||||
|
||||
/** Instantiates a new {@link EmptyMethod} instance. */
|
||||
public EmptyMethod() {}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.LIKELY_ERROR;
|
||||
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.typePredicateMatcher;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.MoreTypePredicates.isSubTypeOf;
|
||||
import static tech.picnic.errorprone.utils.MoreTypes.generic;
|
||||
import static tech.picnic.errorprone.utils.MoreTypes.type;
|
||||
|
||||
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.MethodInvocationTreeMatcher;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.suppliers.Supplier;
|
||||
import com.google.errorprone.suppliers.Suppliers;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags {@link Mono#zip} and {@link Mono#zipWith} invocations with a
|
||||
* {@code Mono<Void>} or {@link Mono#empty()} argument or receiver.
|
||||
*
|
||||
* <p>When a zipped reactive stream completes empty, then the other zipped streams will be cancelled
|
||||
* (or not subscribed to), and the operation as a whole will complete empty as well. This is
|
||||
* generally not what was intended.
|
||||
*/
|
||||
// XXX: Generalize this check to also cover `Flux` zip operations.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Don't pass a `Mono<Void>` or `Mono.empty()` argument to `Mono#{zip,With}`",
|
||||
link = BUG_PATTERNS_BASE_URL + "EmptyMonoZip",
|
||||
linkType = CUSTOM,
|
||||
severity = ERROR,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class EmptyMonoZip extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Supplier<Type> MONO =
|
||||
Suppliers.typeFromString("reactor.core.publisher.Mono");
|
||||
private static final Matcher<ExpressionTree> MONO_ZIP_OR_ZIP_WITH =
|
||||
anyOf(
|
||||
instanceMethod().onDescendantOf(MONO).named("zipWith"),
|
||||
staticMethod().onClass(MONO).named("zip"));
|
||||
private static final Matcher<ExpressionTree> EMPTY_MONO =
|
||||
anyOf(
|
||||
staticMethod().onDescendantOf(MONO).named("empty"),
|
||||
typePredicateMatcher(isSubTypeOf(generic(MONO, type(Void.class.getCanonicalName())))));
|
||||
|
||||
/** Instantiates a new {@link EmptyMonoZip} instance. */
|
||||
public EmptyMonoZip() {}
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
if (!MONO_ZIP_OR_ZIP_WITH.matches(tree, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
if (hasEmptyReceiver(tree, state)) {
|
||||
return buildDescription(tree)
|
||||
.setMessage("Invoking `Mono#zipWith` on `Mono#empty()` or a `Mono<Void>` is a no-op")
|
||||
.build();
|
||||
}
|
||||
|
||||
if (hasEmptyArguments(tree, state)) {
|
||||
return describeMatch(tree);
|
||||
}
|
||||
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
private static boolean hasEmptyReceiver(MethodInvocationTree tree, VisitorState state) {
|
||||
return tree.getMethodSelect() instanceof MemberSelectTree memberSelect
|
||||
&& EMPTY_MONO.matches(memberSelect.getExpression(), state);
|
||||
}
|
||||
|
||||
private static boolean hasEmptyArguments(MethodInvocationTree tree, VisitorState state) {
|
||||
return tree.getArguments().stream().anyMatch(arg -> EMPTY_MONO.matches(arg, state));
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.FRAGILE_CODE;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
|
||||
import static java.util.stream.Collectors.collectingAndThen;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -44,7 +44,7 @@ import java.util.stream.Stream;
|
||||
public final class ExplicitEnumOrdering extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> EXPLICIT_ORDERING =
|
||||
staticMethod().onClass(Ordering.class.getName()).named("explicit");
|
||||
staticMethod().onClass(Ordering.class.getCanonicalName()).named("explicit");
|
||||
|
||||
/** Instantiates a new {@link ExplicitEnumOrdering} instance. */
|
||||
public ExplicitEnumOrdering() {}
|
||||
@@ -72,7 +72,7 @@ public final class ExplicitEnumOrdering extends BugChecker implements MethodInvo
|
||||
List<? extends ExpressionTree> expressions) {
|
||||
return expressions.stream()
|
||||
.map(ASTHelpers::getSymbol)
|
||||
.filter(Symbol::isEnum)
|
||||
.filter(s -> s != null && s.isEnum())
|
||||
.collect(
|
||||
collectingAndThen(
|
||||
toImmutableSetMultimap(Symbol::asType, Symbol::toString),
|
||||
|
||||
@@ -4,11 +4,11 @@ import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.LIKELY_ERROR;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.generic;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.subOf;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.type;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.unbound;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.MoreTypes.generic;
|
||||
import static tech.picnic.errorprone.utils.MoreTypes.subOf;
|
||||
import static tech.picnic.errorprone.utils.MoreTypes.type;
|
||||
import static tech.picnic.errorprone.utils.MoreTypes.unbound;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -50,8 +50,9 @@ import reactor.core.publisher.Flux;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"`Flux#flatMap` and `Flux#flatMapSequential` have subtle semantics; "
|
||||
+ "please use `Flux#concatMap` or explicitly specify the desired amount of concurrency",
|
||||
"""
|
||||
`Flux#flatMap` and `Flux#flatMapSequential` have subtle semantics; please use \
|
||||
`Flux#concatMap` or explicitly specify the desired amount of concurrency""",
|
||||
link = BUG_PATTERNS_BASE_URL + "FluxFlatMapUsage",
|
||||
linkType = CUSTOM,
|
||||
severity = ERROR,
|
||||
@@ -66,7 +67,7 @@ public final class FluxFlatMapUsage extends BugChecker
|
||||
instanceMethod()
|
||||
.onDescendantOf(FLUX)
|
||||
.namedAnyOf("flatMap", "flatMapSequential")
|
||||
.withParameters(Function.class.getName());
|
||||
.withParameters(Function.class.getCanonicalName());
|
||||
private static final Supplier<Type> FLUX_OF_PUBLISHERS =
|
||||
VisitorState.memoize(
|
||||
generic(FLUX, subOf(generic(type("org.reactivestreams.Publisher"), unbound()))));
|
||||
|
||||
@@ -6,9 +6,10 @@ import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.CONCURRENCY;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.PERFORMANCE;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
@@ -24,8 +25,9 @@ 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.Position;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.bugpatterns.util.ThirdPartyLibrary;
|
||||
import tech.picnic.errorprone.utils.ThirdPartyLibrary;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags {@link reactor.core.publisher.Flux} operator usages that may
|
||||
@@ -48,7 +50,8 @@ public final class FluxImplicitBlock extends BugChecker implements MethodInvocat
|
||||
.onDescendantOf("reactor.core.publisher.Flux")
|
||||
.namedAnyOf("toIterable", "toStream")
|
||||
.withNoParameters();
|
||||
private static final Supplier<Type> STREAM = Suppliers.typeFromString(Stream.class.getName());
|
||||
private static final Supplier<Type> STREAM =
|
||||
Suppliers.typeFromString(Stream.class.getCanonicalName());
|
||||
|
||||
/** Instantiates a new {@link FluxImplicitBlock} instance. */
|
||||
public FluxImplicitBlock() {}
|
||||
@@ -64,10 +67,11 @@ public final class FluxImplicitBlock extends BugChecker implements MethodInvocat
|
||||
if (ThirdPartyLibrary.GUAVA.isIntroductionAllowed(state)) {
|
||||
description.addFix(
|
||||
suggestBlockingElementCollection(
|
||||
tree, "com.google.common.collect.ImmutableList.toImmutableList", state));
|
||||
tree, ImmutableList.class.getCanonicalName() + ".toImmutableList", state));
|
||||
}
|
||||
description.addFix(
|
||||
suggestBlockingElementCollection(tree, "java.util.stream.Collectors.toList", state));
|
||||
suggestBlockingElementCollection(
|
||||
tree, Collectors.class.getCanonicalName() + ".toList", state));
|
||||
|
||||
return description.build();
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
import static com.google.errorprone.matchers.Matchers.staticMethod;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Verify;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
@@ -30,10 +32,11 @@ import com.sun.source.tree.Tree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import com.sun.source.util.SimpleTreeVisitor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Formatter;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags string concatenations that produce a format string; in such cases
|
||||
@@ -68,7 +71,7 @@ public final class FormatStringConcatenation extends BugChecker
|
||||
anyMethod()
|
||||
.anyClass()
|
||||
.withAnyName()
|
||||
.withParameters(String.class.getName(), Throwable.class.getName());
|
||||
.withParameters(String.class.getCanonicalName(), Throwable.class.getCanonicalName());
|
||||
|
||||
// XXX: Drop some of these methods if we use Refaster to replace some with others.
|
||||
private static final Matcher<ExpressionTree> ASSERTJ_FORMAT_METHOD =
|
||||
@@ -118,14 +121,14 @@ public final class FormatStringConcatenation extends BugChecker
|
||||
private static final Matcher<ExpressionTree> GUAVA_FORMAT_METHOD =
|
||||
anyOf(
|
||||
staticMethod()
|
||||
.onClass("com.google.common.base.Preconditions")
|
||||
.onClass(Preconditions.class.getCanonicalName())
|
||||
.namedAnyOf("checkArgument", "checkNotNull", "checkState"),
|
||||
staticMethod().onClass("com.google.common.base.Verify").named("verify"));
|
||||
staticMethod().onClass(Verify.class.getCanonicalName()).named("verify"));
|
||||
// XXX: Add `PrintWriter`, maybe others.
|
||||
private static final Matcher<ExpressionTree> JDK_FORMAT_METHOD =
|
||||
anyOf(
|
||||
staticMethod().onClass("java.lang.String").named("format"),
|
||||
instanceMethod().onExactClass("java.util.Formatter").named("format"));
|
||||
staticMethod().onClass(String.class.getCanonicalName()).named("format"),
|
||||
instanceMethod().onExactClass(Formatter.class.getCanonicalName()).named("format"));
|
||||
private static final Matcher<ExpressionTree> SLF4J_FORMAT_METHOD =
|
||||
instanceMethod()
|
||||
.onDescendantOf("org.slf4j.Logger")
|
||||
@@ -242,8 +245,8 @@ public final class FormatStringConcatenation extends BugChecker
|
||||
}
|
||||
|
||||
private void appendExpression(Tree tree) {
|
||||
if (tree instanceof LiteralTree) {
|
||||
formatString.append(((LiteralTree) tree).getValue());
|
||||
if (tree instanceof LiteralTree literal) {
|
||||
formatString.append(literal.getValue());
|
||||
} else {
|
||||
formatString.append(formatSpecifier);
|
||||
formatArguments.add(tree);
|
||||
|
||||
@@ -7,9 +7,20 @@ import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.staticMethod;
|
||||
import static com.google.errorprone.suppliers.Suppliers.OBJECT_TYPE;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableMultiset;
|
||||
import com.google.common.collect.ImmutableRangeMap;
|
||||
import com.google.common.collect.ImmutableRangeSet;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.ImmutableTable;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
@@ -20,6 +31,7 @@ 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.matchers.Matchers;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.google.errorprone.util.ASTHelpers.TargetType;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
@@ -29,7 +41,7 @@ import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.code.Types;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} that flags redundant identity conversions. */
|
||||
// XXX: Consider detecting cases where a flagged expression is passed to a method, and where removal
|
||||
@@ -56,24 +68,22 @@ public final class IdentityConversion extends BugChecker implements MethodInvoca
|
||||
.map(Class::getName)
|
||||
.collect(toImmutableSet()))
|
||||
.named("valueOf"),
|
||||
staticMethod().onClass(String.class.getName()).named("valueOf"),
|
||||
staticMethod().onClass(String.class.getCanonicalName()).named("valueOf"),
|
||||
staticMethod()
|
||||
.onClassAny(
|
||||
"com.google.common.collect.ImmutableBiMap",
|
||||
"com.google.common.collect.ImmutableList",
|
||||
"com.google.common.collect.ImmutableListMultimap",
|
||||
"com.google.common.collect.ImmutableMap",
|
||||
"com.google.common.collect.ImmutableMultimap",
|
||||
"com.google.common.collect.ImmutableMultiset",
|
||||
"com.google.common.collect.ImmutableRangeMap",
|
||||
"com.google.common.collect.ImmutableRangeSet",
|
||||
"com.google.common.collect.ImmutableSet",
|
||||
"com.google.common.collect.ImmutableSetMultimap",
|
||||
"com.google.common.collect.ImmutableTable")
|
||||
ImmutableBiMap.class.getCanonicalName(),
|
||||
ImmutableList.class.getCanonicalName(),
|
||||
ImmutableListMultimap.class.getCanonicalName(),
|
||||
ImmutableMap.class.getCanonicalName(),
|
||||
ImmutableMultimap.class.getCanonicalName(),
|
||||
ImmutableMultiset.class.getCanonicalName(),
|
||||
ImmutableRangeMap.class.getCanonicalName(),
|
||||
ImmutableRangeSet.class.getCanonicalName(),
|
||||
ImmutableSet.class.getCanonicalName(),
|
||||
ImmutableSetMultimap.class.getCanonicalName(),
|
||||
ImmutableTable.class.getCanonicalName())
|
||||
.named("copyOf"),
|
||||
staticMethod()
|
||||
.onClass("com.google.errorprone.matchers.Matchers")
|
||||
.namedAnyOf("allOf", "anyOf"),
|
||||
staticMethod().onClass(Matchers.class.getCanonicalName()).namedAnyOf("allOf", "anyOf"),
|
||||
staticMethod().onClass("reactor.adapter.rxjava.RxJava2Adapter"),
|
||||
staticMethod()
|
||||
.onClass("reactor.core.publisher.Flux")
|
||||
@@ -114,8 +124,9 @@ public final class IdentityConversion extends BugChecker implements MethodInvoca
|
||||
|
||||
return buildDescription(tree)
|
||||
.setMessage(
|
||||
"This method invocation appears redundant; remove it or suppress this warning and "
|
||||
+ "add a comment explaining its purpose")
|
||||
"""
|
||||
This method invocation appears redundant; remove it or suppress this warning and add a \
|
||||
comment explaining its purpose""")
|
||||
.addFix(SuggestedFix.replace(tree, SourceCode.treeToString(sourceTree, state)))
|
||||
.addFix(SuggestedFixes.addSuppressWarnings(state, canonicalName()))
|
||||
.build();
|
||||
|
||||
@@ -11,7 +11,7 @@ import static com.google.errorprone.matchers.Matchers.hasModifier;
|
||||
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
|
||||
import static com.google.errorprone.matchers.Matchers.methodReturns;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -43,8 +43,9 @@ import javax.lang.model.element.Modifier;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"`SortedSet` properties of a `@Value.Immutable` or `@Value.Modifiable` type must be "
|
||||
+ "annotated with `@Value.NaturalOrder` or `@Value.ReverseOrder`",
|
||||
"""
|
||||
`SortedSet` properties of a `@Value.Immutable` or `@Value.Modifiable` type must be \
|
||||
annotated with `@Value.NaturalOrder` or `@Value.ReverseOrder`""",
|
||||
link = BUG_PATTERNS_BASE_URL + "ImmutablesSortedSetComparator",
|
||||
linkType = CUSTOM,
|
||||
severity = ERROR,
|
||||
|
||||
@@ -3,7 +3,7 @@ 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.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -16,17 +16,15 @@ import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.InstanceOfTree;
|
||||
import com.sun.source.tree.LambdaExpressionTree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import com.sun.source.tree.VariableTree;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
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::isInstance}.
|
||||
*
|
||||
* @see MethodReferenceUsage
|
||||
*/
|
||||
// XXX: Consider folding this logic into the `MethodReferenceUsage` check.
|
||||
// XXX: Consider folding this logic into the `MethodReferenceUsage` check of the
|
||||
// `error-prone-experimental` module.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Prefer `Class::isInstance` method reference over equivalent lambda expression",
|
||||
@@ -42,12 +40,12 @@ public final class IsInstanceLambdaUsage extends BugChecker implements LambdaExp
|
||||
|
||||
@Override
|
||||
public Description matchLambdaExpression(LambdaExpressionTree tree, VisitorState state) {
|
||||
if (tree.getParameters().size() != 1 || tree.getBody().getKind() != Kind.INSTANCE_OF) {
|
||||
if (tree.getParameters().size() != 1
|
||||
|| !(tree.getBody() instanceof InstanceOfTree instanceOf)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
VariableTree param = Iterables.getOnlyElement(tree.getParameters());
|
||||
InstanceOfTree instanceOf = (InstanceOfTree) tree.getBody();
|
||||
if (!ASTHelpers.getSymbol(param).equals(ASTHelpers.getSymbol(instanceOf.getExpression()))) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ import static com.google.errorprone.matchers.Matchers.hasMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.hasModifier;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreJUnitMatchers.TEST_METHOD;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreMatchers.hasMetaAnnotation;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
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.ImmutableSet;
|
||||
|
||||
@@ -8,9 +8,9 @@ import static com.google.errorprone.matchers.Matchers.enclosingClass;
|
||||
import static com.google.errorprone.matchers.Matchers.hasModifier;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
import static java.util.function.Predicate.not;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreJUnitMatchers.SETUP_OR_TEARDOWN_METHOD;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreJUnitMatchers.TEST_METHOD;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.MoreJUnitMatchers.SETUP_OR_TEARDOWN_METHOD;
|
||||
import static tech.picnic.errorprone.utils.MoreJUnitMatchers.TEST_METHOD;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -28,7 +28,7 @@ import com.sun.source.tree.MethodTree;
|
||||
import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
||||
import java.util.Optional;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import tech.picnic.errorprone.bugpatterns.util.ConflictDetection;
|
||||
import tech.picnic.errorprone.utils.ConflictDetection;
|
||||
|
||||
/** A {@link BugChecker} that flags non-canonical JUnit method declarations. */
|
||||
// XXX: Consider introducing a class-level check that enforces that test classes:
|
||||
|
||||
@@ -7,8 +7,8 @@ import static com.google.errorprone.matchers.ChildMultiMatcher.MatchType.AT_LEAS
|
||||
import static com.google.errorprone.matchers.Matchers.annotations;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreMatchers.hasMetaAnnotation;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.MoreMatchers.hasMetaAnnotation;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -23,7 +23,7 @@ import com.google.errorprone.matchers.MultiMatcher;
|
||||
import com.google.errorprone.matchers.MultiMatcher.MultiMatchResult;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags nullary {@link
|
||||
|
||||
@@ -19,17 +19,20 @@ import static com.google.errorprone.matchers.Matchers.staticMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.toType;
|
||||
import static java.util.function.Predicate.not;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreJUnitMatchers.HAS_METHOD_SOURCE;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreJUnitMatchers.getMethodSourceFactoryNames;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.MoreJUnitMatchers.HAS_METHOD_SOURCE;
|
||||
import static tech.picnic.errorprone.utils.MoreJUnitMatchers.getMethodSourceFactoryNames;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
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.MethodTreeMatcher;
|
||||
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;
|
||||
@@ -55,7 +58,7 @@ import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
import java.util.stream.Stream;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags JUnit tests with a {@link
|
||||
@@ -99,14 +102,14 @@ public final class JUnitValueSource extends BugChecker implements MethodTreeMatc
|
||||
allOf(
|
||||
staticMethod()
|
||||
.onClassAny(
|
||||
Stream.class.getName(),
|
||||
IntStream.class.getName(),
|
||||
LongStream.class.getName(),
|
||||
DoubleStream.class.getName(),
|
||||
List.class.getName(),
|
||||
Set.class.getName(),
|
||||
"com.google.common.collect.ImmutableList",
|
||||
"com.google.common.collect.ImmutableSet")
|
||||
Stream.class.getCanonicalName(),
|
||||
IntStream.class.getCanonicalName(),
|
||||
LongStream.class.getCanonicalName(),
|
||||
DoubleStream.class.getCanonicalName(),
|
||||
List.class.getCanonicalName(),
|
||||
Set.class.getCanonicalName(),
|
||||
ImmutableList.class.getCanonicalName(),
|
||||
ImmutableSet.class.getCanonicalName())
|
||||
.named("of"),
|
||||
hasArguments(AT_LEAST_ONE, anything()),
|
||||
hasArguments(ALL, SUPPORTED_VALUE_FACTORY_VALUES)));
|
||||
@@ -199,16 +202,21 @@ public final class JUnitValueSource extends BugChecker implements MethodTreeMatc
|
||||
return getSingleReturnExpression(valueFactoryMethod)
|
||||
.flatMap(expression -> tryExtractValueSourceAttributeValue(expression, state))
|
||||
.map(
|
||||
valueSourceAttributeValue ->
|
||||
SuggestedFix.builder()
|
||||
.addImport("org.junit.jupiter.params.provider.ValueSource")
|
||||
.replace(
|
||||
methodSourceAnnotation,
|
||||
String.format(
|
||||
"@ValueSource(%s = %s)",
|
||||
toValueSourceAttributeName(parameterType), valueSourceAttributeValue))
|
||||
.delete(valueFactoryMethod)
|
||||
.build());
|
||||
valueSourceAttributeValue -> {
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder();
|
||||
String valueSource =
|
||||
SuggestedFixes.qualifyType(
|
||||
state, fix, "org.junit.jupiter.params.provider.ValueSource");
|
||||
return fix.replace(
|
||||
methodSourceAnnotation,
|
||||
String.format(
|
||||
"@%s(%s = %s)",
|
||||
valueSource,
|
||||
toValueSourceAttributeName(parameterType),
|
||||
valueSourceAttributeValue))
|
||||
.delete(valueFactoryMethod)
|
||||
.build();
|
||||
});
|
||||
}
|
||||
|
||||
// XXX: This pattern also occurs a few times inside Error Prone; contribute upstream.
|
||||
@@ -257,8 +265,8 @@ public final class JUnitValueSource extends BugChecker implements MethodTreeMatc
|
||||
arguments.stream()
|
||||
.map(
|
||||
arg ->
|
||||
arg instanceof MethodInvocationTree
|
||||
? Iterables.getOnlyElement(((MethodInvocationTree) arg).getArguments())
|
||||
arg instanceof MethodInvocationTree methodInvocation
|
||||
? Iterables.getOnlyElement(methodInvocation.getArguments())
|
||||
: arg)
|
||||
.map(argument -> SourceCode.treeToString(argument, state))
|
||||
.collect(joining(", ")))
|
||||
@@ -268,16 +276,12 @@ public final class JUnitValueSource extends BugChecker implements MethodTreeMatc
|
||||
private static String toValueSourceAttributeName(Type type) {
|
||||
String typeString = type.tsym.name.toString();
|
||||
|
||||
switch (typeString) {
|
||||
case "Class":
|
||||
return "classes";
|
||||
case "Character":
|
||||
return "chars";
|
||||
case "Integer":
|
||||
return "ints";
|
||||
default:
|
||||
return typeString.toLowerCase(Locale.ROOT) + 's';
|
||||
}
|
||||
return switch (typeString) {
|
||||
case "Class" -> "classes";
|
||||
case "Character" -> "chars";
|
||||
case "Integer" -> "ints";
|
||||
default -> typeString.toLowerCase(Locale.ROOT) + 's';
|
||||
};
|
||||
}
|
||||
|
||||
private static <T> Optional<T> getElementIfSingleton(Collection<T> collection) {
|
||||
@@ -289,11 +293,10 @@ public final class JUnitValueSource extends BugChecker implements MethodTreeMatc
|
||||
private static Matcher<ExpressionTree> isSingleDimensionArrayCreationWithAllElementsMatching(
|
||||
Matcher<? super ExpressionTree> elementMatcher) {
|
||||
return (tree, state) -> {
|
||||
if (!(tree instanceof NewArrayTree)) {
|
||||
if (!(tree instanceof NewArrayTree newArray)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NewArrayTree newArray = (NewArrayTree) tree;
|
||||
return newArray.getDimensions().isEmpty()
|
||||
&& !newArray.getInitializers().isEmpty()
|
||||
&& newArray.getInitializers().stream()
|
||||
|
||||
@@ -7,7 +7,7 @@ import static com.google.errorprone.BugPattern.StandardTags.STYLE;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.Splitter;
|
||||
@@ -31,7 +31,6 @@ import com.sun.source.tree.LiteralTree;
|
||||
import com.sun.source.tree.NewArrayTree;
|
||||
import com.sun.source.tree.PrimitiveTypeTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import com.sun.tools.javac.code.Symtab;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
@@ -42,9 +41,9 @@ import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.bugpatterns.util.AnnotationAttributeMatcher;
|
||||
import tech.picnic.errorprone.bugpatterns.util.Flags;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.AnnotationAttributeMatcher;
|
||||
import tech.picnic.errorprone.utils.Flags;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags annotation array listings which aren't sorted lexicographically.
|
||||
@@ -52,6 +51,9 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
* <p>The idea behind this checker is that maintaining a sorted sequence simplifies conflict
|
||||
* resolution, and can even avoid it if two branches add the same entry.
|
||||
*/
|
||||
// XXX: In some places we declare a `@SuppressWarnings` annotation with a final value of
|
||||
// `key-to-resolve-AnnotationUseStyle-and-TrailingComment-check-conflict`. That entry must stay
|
||||
// last. Consider adding (generic?) support for such cases.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Where possible, sort annotation array attributes lexicographically",
|
||||
@@ -65,14 +67,15 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final ImmutableSet<String> BLACKLISTED_ANNOTATIONS =
|
||||
ImmutableSet.of(
|
||||
// XXX: unless JsonPropertyOrder#alphabetic is true...
|
||||
// XXX: Unless `JsonPropertyOrder#alphabetic` is true...
|
||||
"com.fasterxml.jackson.annotation.JsonPropertyOrder#value",
|
||||
"io.swagger.annotations.ApiImplicitParams#value",
|
||||
"io.swagger.v3.oas.annotations.Parameters#value",
|
||||
"javax.xml.bind.annotation.XmlType#propOrder",
|
||||
"org.springframework.context.annotation.PropertySource#value",
|
||||
"org.springframework.test.context.TestPropertySource#locations",
|
||||
"org.springframework.test.context.TestPropertySource#value");
|
||||
"org.springframework.test.context.TestPropertySource#value",
|
||||
"picocli.CommandLine.Option#names");
|
||||
private static final String FLAG_PREFIX = "LexicographicalAnnotationAttributeListing:";
|
||||
private static final String INCLUDED_ANNOTATIONS_FLAG = FLAG_PREFIX + "Includes";
|
||||
private static final String EXCLUDED_ANNOTATIONS_FLAG = FLAG_PREFIX + "Excludes";
|
||||
@@ -122,13 +125,9 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
}
|
||||
|
||||
private static Optional<NewArrayTree> extractArray(ExpressionTree expr) {
|
||||
if (expr.getKind() == Kind.ASSIGNMENT) {
|
||||
return extractArray(((AssignmentTree) expr).getExpression());
|
||||
}
|
||||
|
||||
return Optional.of(expr)
|
||||
.filter(e -> e.getKind() == Kind.NEW_ARRAY)
|
||||
.map(NewArrayTree.class::cast);
|
||||
return expr instanceof AssignmentTree assignment
|
||||
? extractArray(assignment.getExpression())
|
||||
: Optional.of(expr).filter(NewArrayTree.class::isInstance).map(NewArrayTree.class::cast);
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix.Builder> suggestSorting(
|
||||
@@ -200,8 +199,8 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
public @Nullable Void visitLiteral(LiteralTree node, @Nullable Void unused) {
|
||||
Object value = ASTHelpers.constValue(node);
|
||||
nodes.add(
|
||||
value instanceof String
|
||||
? STRING_ARGUMENT_SPLITTER.splitToStream((String) value).collect(toImmutableList())
|
||||
value instanceof String str
|
||||
? STRING_ARGUMENT_SPLITTER.splitToStream(str).collect(toImmutableList())
|
||||
: ImmutableList.of(String.valueOf(value)));
|
||||
|
||||
return super.visitLiteral(node, unused);
|
||||
|
||||
@@ -7,7 +7,7 @@ import static com.google.errorprone.BugPattern.StandardTags.STYLE;
|
||||
import static com.sun.tools.javac.code.TypeAnnotations.AnnotationType.DECLARATION;
|
||||
import static com.sun.tools.javac.code.TypeAnnotations.AnnotationType.TYPE;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.VerifyException;
|
||||
@@ -28,7 +28,7 @@ import com.sun.tools.javac.code.TypeAnnotations.AnnotationType;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags annotations that are not lexicographically sorted.
|
||||
@@ -36,6 +36,10 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
* <p>The idea behind this checker is that maintaining a sorted sequence simplifies conflict
|
||||
* resolution, and can even avoid it if two branches add the same annotation.
|
||||
*/
|
||||
// XXX: Currently this checker only flags method-level annotations. It should likely also flag
|
||||
// type-, field- and parameter-level annotations.
|
||||
// XXX: Duplicate entries are often a mistake. Consider introducing a similar `BugChecker` that
|
||||
// flags duplicates.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Sort annotations lexicographically where possible",
|
||||
|
||||
@@ -9,7 +9,7 @@ import static com.google.errorprone.matchers.Matchers.isSameType;
|
||||
import static com.google.errorprone.matchers.Matchers.isVariable;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
import static com.google.errorprone.matchers.Matchers.staticMethod;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -25,7 +25,7 @@ import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.tree.VariableTree;
|
||||
import java.util.List;
|
||||
import tech.picnic.errorprone.bugpatterns.util.MoreASTHelpers;
|
||||
import tech.picnic.errorprone.utils.MoreASTHelpers;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags the use of {@link org.mockito.Mockito#mock(Class)} and {@link
|
||||
@@ -50,7 +50,7 @@ public final class MockitoMockClassReference extends BugChecker
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<MethodInvocationTree> MOCKITO_MOCK_OR_SPY_WITH_HARDCODED_TYPE =
|
||||
allOf(
|
||||
argument(0, allOf(isSameType(Class.class.getName()), not(isVariable()))),
|
||||
argument(0, allOf(isSameType(Class.class.getCanonicalName()), not(isVariable()))),
|
||||
staticMethod().onClass("org.mockito.Mockito").namedAnyOf("mock", "spy"));
|
||||
|
||||
/** Instantiates a new {@link MockitoMockClassReference} instance. */
|
||||
@@ -67,20 +67,19 @@ public final class MockitoMockClassReference extends BugChecker
|
||||
return describeMatch(tree, SuggestedFixes.removeElement(arguments.get(0), arguments, state));
|
||||
}
|
||||
|
||||
// XXX: Use switch pattern matching once the targeted JDK supports this.
|
||||
private static boolean isTypeDerivableFromContext(MethodInvocationTree tree, VisitorState state) {
|
||||
Tree parent = state.getPath().getParentPath().getLeaf();
|
||||
switch (parent.getKind()) {
|
||||
case VARIABLE:
|
||||
return !ASTHelpers.hasImplicitType((VariableTree) parent, state)
|
||||
&& MoreASTHelpers.areSameType(tree, parent, state);
|
||||
case ASSIGNMENT:
|
||||
return MoreASTHelpers.areSameType(tree, parent, state);
|
||||
case RETURN:
|
||||
return MoreASTHelpers.findMethodExitedOnReturn(state)
|
||||
.filter(m -> MoreASTHelpers.areSameType(tree, m.getReturnType(), state))
|
||||
.isPresent();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return switch (parent.getKind()) {
|
||||
case VARIABLE ->
|
||||
!ASTHelpers.hasImplicitType((VariableTree) parent, state)
|
||||
&& MoreASTHelpers.areSameType(tree, parent, state);
|
||||
case ASSIGNMENT -> MoreASTHelpers.areSameType(tree, parent, state);
|
||||
case RETURN ->
|
||||
MoreASTHelpers.findMethodExitedOnReturn(state)
|
||||
.filter(m -> MoreASTHelpers.areSameType(tree, m.getReturnType(), state))
|
||||
.isPresent();
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ 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 com.google.errorprone.matchers.Matchers.staticMethod;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -18,7 +18,7 @@ import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import java.util.List;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags method invocations for which all arguments are wrapped using
|
||||
|
||||
@@ -4,7 +4,7 @@ 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.matchers.method.MethodMatchers.staticMethod;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -24,7 +24,9 @@ import com.sun.source.tree.MethodInvocationTree;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"Avoid MongoDB's `$text` filter operator, as it can trigger heavy queries and even cause the server to run out of memory",
|
||||
"""
|
||||
Avoid MongoDB's `$text` filter operator, as it can trigger heavy queries and even cause \
|
||||
the server to run out of memory""",
|
||||
link = BUG_PATTERNS_BASE_URL + "MongoDBTextFilterUsage",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
|
||||
@@ -3,11 +3,11 @@ 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.FRAGILE_CODE;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreMatchers.isSubTypeOf;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.generic;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.raw;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.subOf;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.MoreMatchers.isSubTypeOf;
|
||||
import static tech.picnic.errorprone.utils.MoreTypes.generic;
|
||||
import static tech.picnic.errorprone.utils.MoreTypes.raw;
|
||||
import static tech.picnic.errorprone.utils.MoreTypes.subOf;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
|
||||
@@ -6,13 +6,13 @@ import static com.google.errorprone.BugPattern.StandardTags.FRAGILE_CODE;
|
||||
import static com.google.errorprone.matchers.Matchers.typePredicateMatcher;
|
||||
import static com.google.errorprone.predicates.TypePredicates.allOf;
|
||||
import static com.google.errorprone.predicates.TypePredicates.not;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreTypePredicates.hasTypeParameter;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreTypePredicates.isSubTypeOf;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.generic;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.raw;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.subOf;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.type;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.MoreTypePredicates.hasTypeParameter;
|
||||
import static tech.picnic.errorprone.utils.MoreTypePredicates.isSubTypeOf;
|
||||
import static tech.picnic.errorprone.utils.MoreTypes.generic;
|
||||
import static tech.picnic.errorprone.utils.MoreTypes.raw;
|
||||
import static tech.picnic.errorprone.utils.MoreTypes.subOf;
|
||||
import static tech.picnic.errorprone.utils.MoreTypes.type;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -34,8 +34,9 @@ import com.sun.tools.javac.code.Type;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"Avoid `Publisher`s that emit other `Publishers`s; "
|
||||
+ "the resultant code is hard to reason about",
|
||||
"""
|
||||
Avoid `Publisher`s that emit other `Publishers`s; the resultant code is hard to reason \
|
||||
about""",
|
||||
link = BUG_PATTERNS_BASE_URL + "NestedPublishers",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
|
||||
@@ -5,7 +5,7 @@ import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -20,7 +20,7 @@ import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import java.util.function.BiFunction;
|
||||
import reactor.core.publisher.Mono;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags {@link Mono} operations that are known to be vacuous, given that
|
||||
@@ -72,7 +72,7 @@ public final class NonEmptyMono extends BugChecker implements MethodInvocationTr
|
||||
instanceMethod()
|
||||
.onDescendantOf("reactor.core.publisher.Flux")
|
||||
.named("reduce")
|
||||
.withParameters(Object.class.getName(), BiFunction.class.getName()),
|
||||
.withParameters(Object.class.getCanonicalName(), BiFunction.class.getCanonicalName()),
|
||||
instanceMethod()
|
||||
.onDescendantOf("reactor.core.publisher.Mono")
|
||||
.namedAnyOf("defaultIfEmpty", "hasElement", "single"));
|
||||
|
||||
@@ -4,11 +4,13 @@ import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.STYLE;
|
||||
import static tech.picnic.errorprone.bugpatterns.StaticImport.STATIC_IMPORT_CANDIDATE_MEMBERS;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.ImmutableTable;
|
||||
@@ -27,8 +29,14 @@ import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import java.time.Clock;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags static imports of type members that should *not* be statically
|
||||
@@ -59,10 +67,14 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
|
||||
@VisibleForTesting
|
||||
static final ImmutableSet<String> NON_STATIC_IMPORT_CANDIDATE_TYPES =
|
||||
ImmutableSet.of(
|
||||
"com.google.common.base.Strings",
|
||||
ASTHelpers.class.getCanonicalName(),
|
||||
Clock.class.getCanonicalName(),
|
||||
Strings.class.getCanonicalName(),
|
||||
VisitorState.class.getCanonicalName(),
|
||||
ZoneOffset.class.getCanonicalName(),
|
||||
"com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode",
|
||||
"java.time.Clock",
|
||||
"java.time.ZoneOffset");
|
||||
"reactor.core.publisher.Flux",
|
||||
"reactor.core.publisher.Mono");
|
||||
|
||||
/**
|
||||
* Type members that should never be statically imported.
|
||||
@@ -81,9 +93,8 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
|
||||
// specific context is left out.
|
||||
static final ImmutableSetMultimap<String, String> NON_STATIC_IMPORT_CANDIDATE_MEMBERS =
|
||||
ImmutableSetMultimap.<String, String>builder()
|
||||
.put("com.google.common.base.Predicates", "contains")
|
||||
.putAll(
|
||||
"java.util.Collections",
|
||||
Collections.class.getCanonicalName(),
|
||||
"addAll",
|
||||
"copy",
|
||||
"fill",
|
||||
@@ -94,8 +105,10 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
|
||||
"rotate",
|
||||
"sort",
|
||||
"swap")
|
||||
.put("java.util.Locale", "ROOT")
|
||||
.putAll("java.util.regex.Pattern", "compile", "matches", "quote")
|
||||
.put(Locale.class.getCanonicalName(), "ROOT")
|
||||
.put(Optional.class.getCanonicalName(), "empty")
|
||||
.putAll(Pattern.class.getCanonicalName(), "compile", "matches", "quote")
|
||||
.put(Predicates.class.getCanonicalName(), "contains")
|
||||
.put("org.springframework.http.MediaType", "ALL")
|
||||
.build();
|
||||
|
||||
@@ -116,7 +129,6 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
|
||||
"builder",
|
||||
"copyOf",
|
||||
"create",
|
||||
"empty",
|
||||
"from",
|
||||
"getDefaultInstance",
|
||||
"INSTANCE",
|
||||
@@ -127,10 +139,8 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
|
||||
"newBuilder",
|
||||
"newInstance",
|
||||
"of",
|
||||
"ONE",
|
||||
"parse",
|
||||
"valueOf",
|
||||
"ZERO");
|
||||
"valueOf");
|
||||
|
||||
/** Instantiates a new {@link NonStaticImport} instance. */
|
||||
public NonStaticImport() {}
|
||||
@@ -159,10 +169,9 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
|
||||
ImmutableTable.builder();
|
||||
for (ImportTree importTree : tree.getImports()) {
|
||||
Tree qualifiedIdentifier = importTree.getQualifiedIdentifier();
|
||||
if (importTree.isStatic() && qualifiedIdentifier instanceof MemberSelectTree) {
|
||||
MemberSelectTree memberSelectTree = (MemberSelectTree) qualifiedIdentifier;
|
||||
String type = SourceCode.treeToString(memberSelectTree.getExpression(), state);
|
||||
String member = memberSelectTree.getIdentifier().toString();
|
||||
if (importTree.isStatic() && qualifiedIdentifier instanceof MemberSelectTree memberSelect) {
|
||||
String type = SourceCode.treeToString(memberSelect.getExpression(), state);
|
||||
String member = memberSelect.getIdentifier().toString();
|
||||
if (shouldNotBeStaticallyImported(type, member)) {
|
||||
imports.put(
|
||||
type,
|
||||
|
||||
@@ -7,7 +7,7 @@ import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.VerifyException;
|
||||
@@ -17,10 +17,12 @@ import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
|
||||
import com.google.errorprone.fixes.Fix;
|
||||
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.ExpressionTree;
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
import com.sun.source.tree.LambdaExpressionTree;
|
||||
import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
@@ -32,7 +34,7 @@ import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags {@code Comparator#comparing*} invocations that can be replaced
|
||||
@@ -43,8 +45,9 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"Ensure invocations of `Comparator#comparing{,Double,Int,Long}` match the return type"
|
||||
+ " of the provided function",
|
||||
"""
|
||||
Ensure invocations of `Comparator#comparing{,Double,Int,Long}` match the return type of \
|
||||
the provided function""",
|
||||
link = BUG_PATTERNS_BASE_URL + "PrimitiveComparison",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
@@ -55,21 +58,21 @@ public final class PrimitiveComparison extends BugChecker implements MethodInvoc
|
||||
private static final Matcher<ExpressionTree> STATIC_COMPARISON_METHOD =
|
||||
anyOf(
|
||||
staticMethod()
|
||||
.onClass(Comparator.class.getName())
|
||||
.onClass(Comparator.class.getCanonicalName())
|
||||
.namedAnyOf("comparingInt", "comparingLong", "comparingDouble"),
|
||||
staticMethod()
|
||||
.onClass(Comparator.class.getName())
|
||||
.onClass(Comparator.class.getCanonicalName())
|
||||
.named("comparing")
|
||||
.withParameters(Function.class.getName()));
|
||||
.withParameters(Function.class.getCanonicalName()));
|
||||
private static final Matcher<ExpressionTree> INSTANCE_COMPARISON_METHOD =
|
||||
anyOf(
|
||||
instanceMethod()
|
||||
.onDescendantOf(Comparator.class.getName())
|
||||
.onDescendantOf(Comparator.class.getCanonicalName())
|
||||
.namedAnyOf("thenComparingInt", "thenComparingLong", "thenComparingDouble"),
|
||||
instanceMethod()
|
||||
.onDescendantOf(Comparator.class.getName())
|
||||
.onDescendantOf(Comparator.class.getCanonicalName())
|
||||
.named("thenComparing")
|
||||
.withParameters(Function.class.getName()));
|
||||
.withParameters(Function.class.getCanonicalName()));
|
||||
|
||||
/** Instantiates a new {@link PrimitiveComparison} instance. */
|
||||
public PrimitiveComparison() {}
|
||||
@@ -146,37 +149,44 @@ public final class PrimitiveComparison extends BugChecker implements MethodInvoc
|
||||
return isStatic ? "comparing" : "thenComparing";
|
||||
}
|
||||
|
||||
// XXX: Use switch pattern matching once the targeted JDK supports this.
|
||||
private static Optional<Type> getPotentiallyBoxedReturnType(ExpressionTree tree) {
|
||||
switch (tree.getKind()) {
|
||||
case LAMBDA_EXPRESSION:
|
||||
/* Return the lambda expression's actual return type. */
|
||||
return Optional.ofNullable(ASTHelpers.getType(((LambdaExpressionTree) tree).getBody()));
|
||||
case MEMBER_REFERENCE:
|
||||
/* Return the method's declared return type. */
|
||||
// XXX: Very fragile. Do better.
|
||||
Type subType2 = ((JCMemberReference) tree).referentType;
|
||||
return Optional.of(subType2.getReturnType());
|
||||
default:
|
||||
/* This appears to be a genuine `{,ToInt,ToLong,ToDouble}Function`. */
|
||||
return Optional.empty();
|
||||
if (tree instanceof LambdaExpressionTree lambdaExpression) {
|
||||
/* Return the lambda expression's actual return type. */
|
||||
return Optional.ofNullable(ASTHelpers.getType(lambdaExpression.getBody()));
|
||||
}
|
||||
|
||||
// XXX: The match against a concrete type and reference to one of its fields is fragile. Do
|
||||
// better.
|
||||
if (tree instanceof JCMemberReference memberReference) {
|
||||
/* Return the method's declared return type. */
|
||||
Type subType = memberReference.referentType;
|
||||
return Optional.of(subType.getReturnType());
|
||||
}
|
||||
|
||||
/* This appears to be a genuine `{,ToInt,ToLong,ToDouble}Function`. */
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// XXX: Use switch pattern matching once the targeted JDK supports this.
|
||||
private static Fix suggestFix(
|
||||
MethodInvocationTree tree, String preferredMethodName, VisitorState state) {
|
||||
ExpressionTree expr = tree.getMethodSelect();
|
||||
switch (expr.getKind()) {
|
||||
case IDENTIFIER:
|
||||
return SuggestedFix.builder()
|
||||
.addStaticImport(Comparator.class.getName() + '.' + preferredMethodName)
|
||||
.replace(expr, preferredMethodName)
|
||||
.build();
|
||||
case MEMBER_SELECT:
|
||||
MemberSelectTree ms = (MemberSelectTree) tree.getMethodSelect();
|
||||
return SuggestedFix.replace(
|
||||
ms, SourceCode.treeToString(ms.getExpression(), state) + '.' + preferredMethodName);
|
||||
default:
|
||||
throw new VerifyException("Unexpected type of expression: " + expr.getKind());
|
||||
|
||||
if (expr instanceof IdentifierTree) {
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder();
|
||||
String replacement =
|
||||
SuggestedFixes.qualifyStaticImport(
|
||||
Comparator.class.getCanonicalName() + '.' + preferredMethodName, fix, state);
|
||||
return fix.replace(expr, replacement).build();
|
||||
}
|
||||
|
||||
if (expr instanceof MemberSelectTree memberSelect) {
|
||||
return SuggestedFix.replace(
|
||||
memberSelect,
|
||||
SourceCode.treeToString(memberSelect.getExpression(), state) + '.' + preferredMethodName);
|
||||
}
|
||||
|
||||
throw new VerifyException("Unexpected type of expression: " + expr.getKind());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,11 @@ import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Verify;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.primitives.Primitives;
|
||||
@@ -51,9 +53,9 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
import tech.picnic.errorprone.bugpatterns.util.Flags;
|
||||
import tech.picnic.errorprone.bugpatterns.util.MethodMatcherFactory;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.Flags;
|
||||
import tech.picnic.errorprone.utils.MethodMatcherFactory;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} that flags redundant explicit string conversions. */
|
||||
@AutoService(BugChecker.class)
|
||||
@@ -86,7 +88,7 @@ public final class RedundantStringConversion extends BugChecker
|
||||
private static final Matcher<MethodInvocationTree> WELL_KNOWN_STRING_CONVERSION_METHODS =
|
||||
anyOf(
|
||||
instanceMethod()
|
||||
.onDescendantOfAny(Object.class.getName())
|
||||
.onDescendantOfAny(Object.class.getCanonicalName())
|
||||
.named("toString")
|
||||
.withNoParameters(),
|
||||
allOf(
|
||||
@@ -100,7 +102,7 @@ public final class RedundantStringConversion extends BugChecker
|
||||
.collect(toImmutableSet()))
|
||||
.named("toString"),
|
||||
allOf(
|
||||
staticMethod().onClass(String.class.getName()).named("valueOf"),
|
||||
staticMethod().onClass(String.class.getCanonicalName()).named("valueOf"),
|
||||
not(
|
||||
anyMethod()
|
||||
.anyClass()
|
||||
@@ -109,35 +111,37 @@ public final class RedundantStringConversion extends BugChecker
|
||||
ImmutableList.of(Suppliers.arrayOf(Suppliers.CHAR_TYPE))))))));
|
||||
private static final Matcher<ExpressionTree> STRINGBUILDER_APPEND_INVOCATION =
|
||||
instanceMethod()
|
||||
.onDescendantOf(StringBuilder.class.getName())
|
||||
.onDescendantOf(StringBuilder.class.getCanonicalName())
|
||||
.named("append")
|
||||
.withParameters(String.class.getName());
|
||||
.withParameters(String.class.getCanonicalName());
|
||||
private static final Matcher<ExpressionTree> STRINGBUILDER_INSERT_INVOCATION =
|
||||
instanceMethod()
|
||||
.onDescendantOf(StringBuilder.class.getName())
|
||||
.onDescendantOf(StringBuilder.class.getCanonicalName())
|
||||
.named("insert")
|
||||
.withParameters(int.class.getName(), String.class.getName());
|
||||
.withParameters(int.class.getCanonicalName(), String.class.getCanonicalName());
|
||||
private static final Matcher<ExpressionTree> FORMATTER_INVOCATION =
|
||||
anyOf(
|
||||
staticMethod().onClass(String.class.getName()).named("format"),
|
||||
instanceMethod().onDescendantOf(Formatter.class.getName()).named("format"),
|
||||
staticMethod().onClass(String.class.getCanonicalName()).named("format"),
|
||||
instanceMethod().onDescendantOf(Formatter.class.getCanonicalName()).named("format"),
|
||||
instanceMethod()
|
||||
.onDescendantOfAny(PrintStream.class.getName(), PrintWriter.class.getName())
|
||||
.onDescendantOfAny(
|
||||
PrintStream.class.getCanonicalName(), PrintWriter.class.getCanonicalName())
|
||||
.namedAnyOf("format", "printf"),
|
||||
instanceMethod()
|
||||
.onDescendantOfAny(PrintStream.class.getName(), PrintWriter.class.getName())
|
||||
.onDescendantOfAny(
|
||||
PrintStream.class.getCanonicalName(), PrintWriter.class.getCanonicalName())
|
||||
.namedAnyOf("print", "println")
|
||||
.withParameters(Object.class.getName()),
|
||||
.withParameters(Object.class.getCanonicalName()),
|
||||
staticMethod()
|
||||
.onClass(Console.class.getName())
|
||||
.onClass(Console.class.getCanonicalName())
|
||||
.namedAnyOf("format", "printf", "readline", "readPassword"));
|
||||
private static final Matcher<ExpressionTree> GUAVA_GUARD_INVOCATION =
|
||||
anyOf(
|
||||
staticMethod()
|
||||
.onClass("com.google.common.base.Preconditions")
|
||||
.onClass(Preconditions.class.getCanonicalName())
|
||||
.namedAnyOf("checkArgument", "checkState", "checkNotNull"),
|
||||
staticMethod()
|
||||
.onClass("com.google.common.base.Verify")
|
||||
.onClass(Verify.class.getCanonicalName())
|
||||
.namedAnyOf("verify", "verifyNotNull"));
|
||||
private static final Matcher<ExpressionTree> SLF4J_LOGGER_INVOCATION =
|
||||
instanceMethod()
|
||||
@@ -327,36 +331,32 @@ public final class RedundantStringConversion extends BugChecker
|
||||
}
|
||||
|
||||
private Optional<ExpressionTree> trySimplify(ExpressionTree tree, VisitorState state) {
|
||||
if (tree.getKind() != Kind.METHOD_INVOCATION) {
|
||||
if (!(tree instanceof MethodInvocationTree methodInvocation)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
MethodInvocationTree methodInvocation = (MethodInvocationTree) tree;
|
||||
if (!conversionMethodMatcher.matches(methodInvocation, state)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
switch (methodInvocation.getArguments().size()) {
|
||||
case 0:
|
||||
return trySimplifyNullaryMethod(methodInvocation, state);
|
||||
case 1:
|
||||
return trySimplifyUnaryMethod(methodInvocation, state);
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
"Cannot simplify method call with two or more arguments: "
|
||||
+ SourceCode.treeToString(tree, state));
|
||||
}
|
||||
return switch (methodInvocation.getArguments().size()) {
|
||||
case 0 -> trySimplifyNullaryMethod(methodInvocation, state);
|
||||
case 1 -> trySimplifyUnaryMethod(methodInvocation, state);
|
||||
default ->
|
||||
throw new IllegalStateException(
|
||||
"Cannot simplify method call with two or more arguments: "
|
||||
+ SourceCode.treeToString(tree, state));
|
||||
};
|
||||
}
|
||||
|
||||
private static Optional<ExpressionTree> trySimplifyNullaryMethod(
|
||||
MethodInvocationTree methodInvocation, VisitorState state) {
|
||||
if (!instanceMethod().matches(methodInvocation, state)) {
|
||||
if (!instanceMethod().matches(methodInvocation, state)
|
||||
|| !(methodInvocation.getMethodSelect() instanceof MemberSelectTree memberSelect)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(methodInvocation.getMethodSelect())
|
||||
.filter(methodSelect -> methodSelect.getKind() == Kind.MEMBER_SELECT)
|
||||
.map(methodSelect -> ((MemberSelectTree) methodSelect).getExpression())
|
||||
return Optional.of(memberSelect.getExpression())
|
||||
.filter(expr -> !"super".equals(SourceCode.treeToString(expr, state)));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import static com.google.errorprone.matchers.Matchers.isSameType;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static com.google.errorprone.matchers.Matchers.methodHasParameters;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -22,6 +22,10 @@ import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import java.io.InputStream;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags {@code @RequestMapping} methods that have one or more parameters
|
||||
@@ -69,11 +73,13 @@ public final class RequestMappingAnnotation extends BugChecker implements Method
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestBody"),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestHeader"),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestParam"),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestPart"))),
|
||||
isSameType("java.io.InputStream"),
|
||||
isSameType("java.time.ZoneId"),
|
||||
isSameType("java.util.Locale"),
|
||||
isSameType("java.util.TimeZone"),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestPart"),
|
||||
isType(
|
||||
"org.springframework.security.core.annotation.CurrentSecurityContext"))),
|
||||
isSameType(InputStream.class.getCanonicalName()),
|
||||
isSameType(Locale.class.getCanonicalName()),
|
||||
isSameType(TimeZone.class.getCanonicalName()),
|
||||
isSameType(ZoneId.class.getCanonicalName()),
|
||||
isSameType("jakarta.servlet.http.HttpServletRequest"),
|
||||
isSameType("jakarta.servlet.http.HttpServletResponse"),
|
||||
isSameType("javax.servlet.http.HttpServletRequest"),
|
||||
@@ -99,9 +105,10 @@ public final class RequestMappingAnnotation extends BugChecker implements Method
|
||||
&& LACKS_PARAMETER_ANNOTATION.matches(tree, state)
|
||||
? buildDescription(tree)
|
||||
.setMessage(
|
||||
"Not all parameters of this request mapping method are annotated; this may be a "
|
||||
+ "mistake. If the unannotated parameters represent query string parameters, "
|
||||
+ "annotate them with `@RequestParam`.")
|
||||
"""
|
||||
Not all parameters of this request mapping method are annotated; this may be a \
|
||||
mistake. If the unannotated parameters represent query string parameters, annotate \
|
||||
them with `@RequestParam`.""")
|
||||
.build()
|
||||
: Description.NO_MATCH;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
@@ -28,13 +28,15 @@ import com.google.errorprone.suppliers.Suppliers;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.tree.VariableTree;
|
||||
import javax.inject.Inject;
|
||||
import tech.picnic.errorprone.bugpatterns.util.Flags;
|
||||
import tech.picnic.errorprone.utils.Flags;
|
||||
|
||||
/** A {@link BugChecker} that flags {@code @RequestParam} parameters with an unsupported type. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"By default, `@RequestParam` does not support `ImmutableCollection` and `ImmutableMap` subtypes",
|
||||
"""
|
||||
By default, `@RequestParam` does not support `ImmutableCollection` and `ImmutableMap` \
|
||||
subtypes""",
|
||||
link = BUG_PATTERNS_BASE_URL + "RequestParamType",
|
||||
linkType = CUSTOM,
|
||||
severity = ERROR,
|
||||
|
||||
@@ -6,7 +6,7 @@ import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.LIKELY_ERROR;
|
||||
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.Splitter;
|
||||
@@ -23,7 +23,7 @@ import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} that flags SLF4J usages that are likely to be in error. */
|
||||
// XXX: The special-casing of Throwable applies only to SLF4J 1.6.0+; see
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
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.SUGGESTION;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
|
||||
import static java.util.function.Predicate.not;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.VerifyException;
|
||||
@@ -18,16 +17,16 @@ import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.AnnotationTreeMatcher;
|
||||
import com.google.errorprone.fixes.Fix;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.AssignmentTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.NewArrayTree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.bugpatterns.util.AnnotationAttributeMatcher;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.AnnotationAttributeMatcher;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags {@code @RequestMapping} annotations that can be written more
|
||||
@@ -79,31 +78,25 @@ public final class SpringMvcAnnotation extends BugChecker implements AnnotationT
|
||||
}
|
||||
|
||||
private static Optional<String> extractUniqueMethod(ExpressionTree arg, VisitorState state) {
|
||||
verify(
|
||||
arg.getKind() == Kind.ASSIGNMENT,
|
||||
"Annotation attribute is not an assignment: %s",
|
||||
arg.getKind());
|
||||
|
||||
ExpressionTree expr = ((AssignmentTree) arg).getExpression();
|
||||
if (expr.getKind() != Kind.NEW_ARRAY) {
|
||||
return Optional.of(extractMethod(expr, state));
|
||||
if (!(arg instanceof AssignmentTree assignment)) {
|
||||
throw new VerifyException("Annotation attribute is not an assignment:" + arg.getKind());
|
||||
}
|
||||
|
||||
NewArrayTree newArray = (NewArrayTree) expr;
|
||||
return Optional.of(newArray.getInitializers())
|
||||
.filter(args -> args.size() == 1)
|
||||
.map(args -> extractMethod(args.get(0), state));
|
||||
ExpressionTree expr = assignment.getExpression();
|
||||
return expr instanceof NewArrayTree newArray
|
||||
? Optional.of(newArray.getInitializers())
|
||||
.filter(args -> args.size() == 1)
|
||||
.map(args -> extractMethod(args.get(0), state))
|
||||
: Optional.of(extractMethod(expr, state));
|
||||
}
|
||||
|
||||
// XXX: Use switch pattern matching once the targeted JDK supports this.
|
||||
private static String extractMethod(ExpressionTree expr, VisitorState state) {
|
||||
switch (expr.getKind()) {
|
||||
case IDENTIFIER:
|
||||
return SourceCode.treeToString(expr, state);
|
||||
case MEMBER_SELECT:
|
||||
return ((MemberSelectTree) expr).getIdentifier().toString();
|
||||
default:
|
||||
throw new VerifyException("Unexpected type of expression: " + expr.getKind());
|
||||
}
|
||||
return switch (expr.getKind()) {
|
||||
case IDENTIFIER -> SourceCode.treeToString(expr, state);
|
||||
case MEMBER_SELECT -> ((MemberSelectTree) expr).getIdentifier().toString();
|
||||
default -> throw new VerifyException("Unexpected type of expression: " + expr.getKind());
|
||||
};
|
||||
}
|
||||
|
||||
private static Fix replaceAnnotation(
|
||||
@@ -114,9 +107,8 @@ public final class SpringMvcAnnotation extends BugChecker implements AnnotationT
|
||||
.map(arg -> SourceCode.treeToString(arg, state))
|
||||
.collect(joining(", "));
|
||||
|
||||
return SuggestedFix.builder()
|
||||
.addImport(ANN_PACKAGE_PREFIX + newAnnotation)
|
||||
.replace(tree, String.format("@%s(%s)", newAnnotation, newArguments))
|
||||
.build();
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder();
|
||||
String annotation = SuggestedFixes.qualifyType(state, fix, ANN_PACKAGE_PREFIX + newAnnotation);
|
||||
return fix.replace(tree, String.format("@%s(%s)", annotation, newArguments)).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,28 @@ import static com.google.errorprone.BugPattern.StandardTags.STYLE;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static tech.picnic.errorprone.bugpatterns.NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_IDENTIFIERS;
|
||||
import static tech.picnic.errorprone.bugpatterns.NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_MEMBERS;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Functions;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.base.Verify;
|
||||
import com.google.common.collect.Comparators;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultiset;
|
||||
import com.google.common.collect.ImmutableRangeSet;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.ImmutableSortedMultiset;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.ImmutableTable;
|
||||
import com.google.common.collect.MoreCollectors;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
@@ -22,12 +38,27 @@ import com.google.errorprone.fixes.Fix;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matchers;
|
||||
import com.google.errorprone.predicates.TypePredicates;
|
||||
import com.google.errorprone.refaster.ImportPolicy;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** A {@link BugChecker} that flags type members that can and should be statically imported. */
|
||||
// XXX: This check is closely linked to `NonStaticImport`. Consider merging the two.
|
||||
@@ -57,15 +88,26 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa
|
||||
@VisibleForTesting
|
||||
static final ImmutableSet<String> STATIC_IMPORT_CANDIDATE_TYPES =
|
||||
ImmutableSet.of(
|
||||
"com.google.common.base.Preconditions",
|
||||
"com.google.common.base.Predicates",
|
||||
"com.google.common.base.Verify",
|
||||
"com.google.common.collect.MoreCollectors",
|
||||
"com.google.errorprone.BugPattern.LinkType",
|
||||
"com.google.errorprone.BugPattern.SeverityLevel",
|
||||
"com.google.errorprone.BugPattern.StandardTags",
|
||||
"com.google.errorprone.matchers.Matchers",
|
||||
"com.google.errorprone.refaster.ImportPolicy",
|
||||
BugPattern.LinkType.class.getCanonicalName(),
|
||||
BugPattern.SeverityLevel.class.getCanonicalName(),
|
||||
BugPattern.StandardTags.class.getCanonicalName(),
|
||||
Collections.class.getCanonicalName(),
|
||||
Collectors.class.getCanonicalName(),
|
||||
Comparator.class.getCanonicalName(),
|
||||
ImportPolicy.class.getCanonicalName(),
|
||||
Map.Entry.class.getCanonicalName(),
|
||||
Matchers.class.getCanonicalName(),
|
||||
MoreCollectors.class.getCanonicalName(),
|
||||
Pattern.class.getCanonicalName(),
|
||||
Preconditions.class.getCanonicalName(),
|
||||
Predicates.class.getCanonicalName(),
|
||||
StandardCharsets.class.getCanonicalName(),
|
||||
TypePredicates.class.getCanonicalName(),
|
||||
Verify.class.getCanonicalName(),
|
||||
"com.fasterxml.jackson.annotation.JsonCreator.Mode",
|
||||
"com.fasterxml.jackson.annotation.JsonFormat.Shape",
|
||||
"com.fasterxml.jackson.annotation.JsonInclude.Include",
|
||||
"com.fasterxml.jackson.annotation.JsonProperty.Access",
|
||||
"com.mongodb.client.model.Accumulators",
|
||||
"com.mongodb.client.model.Aggregates",
|
||||
"com.mongodb.client.model.Filters",
|
||||
@@ -73,12 +115,6 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa
|
||||
"com.mongodb.client.model.Projections",
|
||||
"com.mongodb.client.model.Sorts",
|
||||
"com.mongodb.client.model.Updates",
|
||||
"java.nio.charset.StandardCharsets",
|
||||
"java.util.Collections",
|
||||
"java.util.Comparator",
|
||||
"java.util.Map.Entry",
|
||||
"java.util.regex.Pattern",
|
||||
"java.util.stream.Collectors",
|
||||
"org.assertj.core.api.Assertions",
|
||||
"org.assertj.core.api.InstanceOfAssertFactories",
|
||||
"org.assertj.core.api.SoftAssertions",
|
||||
@@ -99,7 +135,7 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa
|
||||
"org.springframework.http.MediaType",
|
||||
"org.testng.Assert",
|
||||
"reactor.function.TupleUtils",
|
||||
"tech.picnic.errorprone.bugpatterns.util.MoreTypes");
|
||||
"tech.picnic.errorprone.utils.MoreTypes");
|
||||
|
||||
/**
|
||||
* Type members that should be statically imported.
|
||||
@@ -117,39 +153,39 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa
|
||||
*/
|
||||
static final ImmutableSetMultimap<String, String> STATIC_IMPORT_CANDIDATE_MEMBERS =
|
||||
ImmutableSetMultimap.<String, String>builder()
|
||||
.putAll(Comparators.class.getCanonicalName(), "emptiesFirst", "emptiesLast")
|
||||
.put(Function.class.getCanonicalName(), "identity")
|
||||
.put(Functions.class.getCanonicalName(), "identity")
|
||||
.put(ImmutableList.class.getCanonicalName(), "toImmutableList")
|
||||
.putAll(
|
||||
"com.google.common.collect.ImmutableListMultimap",
|
||||
ImmutableListMultimap.class.getCanonicalName(),
|
||||
"flatteningToImmutableListMultimap",
|
||||
"toImmutableListMultimap")
|
||||
.put("com.google.common.collect.ImmutableList", "toImmutableList")
|
||||
.put("com.google.common.collect.ImmutableMap", "toImmutableMap")
|
||||
.put("com.google.common.collect.ImmutableMultiset", "toImmutableMultiset")
|
||||
.put("com.google.common.collect.ImmutableRangeSet", "toImmutableRangeSet")
|
||||
.put(ImmutableMap.class.getCanonicalName(), "toImmutableMap")
|
||||
.put(ImmutableMultiset.class.getCanonicalName(), "toImmutableMultiset")
|
||||
.put(ImmutableRangeSet.class.getCanonicalName(), "toImmutableRangeSet")
|
||||
.put(ImmutableSet.class.getCanonicalName(), "toImmutableSet")
|
||||
.putAll(
|
||||
"com.google.common.collect.ImmutableSetMultimap",
|
||||
ImmutableSetMultimap.class.getCanonicalName(),
|
||||
"flatteningToImmutableSetMultimap",
|
||||
"toImmutableSetMultimap")
|
||||
.put("com.google.common.collect.ImmutableSet", "toImmutableSet")
|
||||
.put("com.google.common.collect.ImmutableSortedMap", "toImmutableSortedMap")
|
||||
.put("com.google.common.collect.ImmutableSortedMultiset", "toImmutableSortedMultiset")
|
||||
.put("com.google.common.collect.ImmutableSortedSet", "toImmutableSortedSet")
|
||||
.put("com.google.common.collect.ImmutableTable", "toImmutableTable")
|
||||
.put("com.google.common.collect.Sets", "toImmutableEnumSet")
|
||||
.put("com.google.common.base.Functions", "identity")
|
||||
.put("java.time.ZoneOffset", "UTC")
|
||||
.put("java.util.function.Function", "identity")
|
||||
.put("java.util.function.Predicate", "not")
|
||||
.put("java.util.UUID", "randomUUID")
|
||||
.put("org.junit.jupiter.params.provider.Arguments", "arguments")
|
||||
.put(ImmutableSortedMap.class.getCanonicalName(), "toImmutableSortedMap")
|
||||
.put(ImmutableSortedMultiset.class.getCanonicalName(), "toImmutableSortedMultiset")
|
||||
.put(ImmutableSortedSet.class.getCanonicalName(), "toImmutableSortedSet")
|
||||
.put(ImmutableTable.class.getCanonicalName(), "toImmutableTable")
|
||||
.putAll(
|
||||
"java.util.Objects",
|
||||
Objects.class.getCanonicalName(),
|
||||
"checkIndex",
|
||||
"checkFromIndexSize",
|
||||
"checkFromToIndex",
|
||||
"requireNonNull",
|
||||
"requireNonNullElse",
|
||||
"requireNonNullElseGet")
|
||||
.putAll("com.google.common.collect.Comparators", "emptiesFirst", "emptiesLast")
|
||||
.put(Predicate.class.getCanonicalName(), "not")
|
||||
.put(Sets.class.getCanonicalName(), "toImmutableEnumSet")
|
||||
.put(UUID.class.getCanonicalName(), "randomUUID")
|
||||
.put(ZoneOffset.class.getCanonicalName(), "UTC")
|
||||
.put("org.junit.jupiter.params.provider.Arguments", "arguments")
|
||||
.build();
|
||||
|
||||
/** Instantiates a new {@link StaticImport} instance. */
|
||||
@@ -176,15 +212,10 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa
|
||||
Tree parentTree =
|
||||
requireNonNull(state.getPath().getParentPath(), "MemberSelectTree lacks enclosing node")
|
||||
.getLeaf();
|
||||
switch (parentTree.getKind()) {
|
||||
case IMPORT:
|
||||
case MEMBER_SELECT:
|
||||
return false;
|
||||
case METHOD_INVOCATION:
|
||||
return ((MethodInvocationTree) parentTree).getTypeArguments().isEmpty();
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return parentTree instanceof MethodInvocationTree methodInvocation
|
||||
? methodInvocation.getTypeArguments().isEmpty()
|
||||
: (parentTree.getKind() != Kind.IMPORT && parentTree.getKind() != Kind.MEMBER_SELECT);
|
||||
}
|
||||
|
||||
private static boolean isCandidate(MemberSelectTree tree) {
|
||||
|
||||
@@ -4,7 +4,7 @@ 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 com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.Splitter;
|
||||
@@ -28,7 +28,7 @@ import java.util.Formattable;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags {@link String#format(String, Object...)} invocations which can be
|
||||
@@ -49,7 +49,7 @@ public final class StringJoin extends BugChecker implements MethodInvocationTree
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Splitter FORMAT_SPECIFIER_SPLITTER = Splitter.on("%s");
|
||||
private static final Matcher<ExpressionTree> STRING_FORMAT_INVOCATION =
|
||||
staticMethod().onClass(String.class.getName()).named("format");
|
||||
staticMethod().onClass(String.class.getCanonicalName()).named("format");
|
||||
private static final Supplier<Type> CHAR_SEQUENCE_TYPE =
|
||||
Suppliers.typeFromClass(CharSequence.class);
|
||||
private static final Supplier<Type> FORMATTABLE_TYPE = Suppliers.typeFromClass(Formattable.class);
|
||||
|
||||
@@ -10,7 +10,7 @@ import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
import static com.google.errorprone.matchers.Matchers.staticMethod;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -34,7 +34,9 @@ import java.time.ZonedDateTime;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"Derive the current time from an existing `Clock` Spring bean, and don't rely on a `Clock`'s time zone",
|
||||
"""
|
||||
Derive the current time from an existing `Clock` Spring bean, and don't rely on a \
|
||||
`Clock`'s time zone""",
|
||||
link = BUG_PATTERNS_BASE_URL + "TimeZoneUsage",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
@@ -45,11 +47,11 @@ public final class TimeZoneUsage extends BugChecker implements MethodInvocationT
|
||||
anyOf(
|
||||
allOf(
|
||||
instanceMethod()
|
||||
.onDescendantOf(Clock.class.getName())
|
||||
.onDescendantOf(Clock.class.getCanonicalName())
|
||||
.namedAnyOf("getZone", "withZone"),
|
||||
not(enclosingClass(isSubtypeOf(Clock.class)))),
|
||||
staticMethod()
|
||||
.onClass(Clock.class.getName())
|
||||
.onClass(Clock.class.getCanonicalName())
|
||||
.namedAnyOf(
|
||||
"system",
|
||||
"systemDefaultZone",
|
||||
@@ -59,14 +61,17 @@ public final class TimeZoneUsage extends BugChecker implements MethodInvocationT
|
||||
"tickSeconds"),
|
||||
staticMethod()
|
||||
.onClassAny(
|
||||
LocalDate.class.getName(),
|
||||
LocalDateTime.class.getName(),
|
||||
LocalTime.class.getName(),
|
||||
OffsetDateTime.class.getName(),
|
||||
OffsetTime.class.getName(),
|
||||
ZonedDateTime.class.getName())
|
||||
LocalDate.class.getCanonicalName(),
|
||||
LocalDateTime.class.getCanonicalName(),
|
||||
LocalTime.class.getCanonicalName(),
|
||||
OffsetDateTime.class.getCanonicalName(),
|
||||
OffsetTime.class.getCanonicalName(),
|
||||
ZonedDateTime.class.getCanonicalName())
|
||||
.named("now"),
|
||||
staticMethod().onClassAny(Instant.class.getName()).named("now").withNoParameters());
|
||||
staticMethod()
|
||||
.onClassAny(Instant.class.getCanonicalName())
|
||||
.named("now")
|
||||
.withNoParameters());
|
||||
|
||||
/** Instantiates a new {@link TimeZoneUsage} instance. */
|
||||
public TimeZoneUsage() {}
|
||||
|
||||
@@ -530,13 +530,13 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamContainsAnyElementsOf<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsAnyElementsOf(iterable);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsAnyElementsOf(iterable);
|
||||
}
|
||||
|
||||
@@ -551,13 +551,13 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamContainsAnyOf<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, U[] array) {
|
||||
Stream<S> stream, U[] array, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsAnyOf(array);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, U[] array) {
|
||||
Stream<S> stream, U[] array, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsAnyOf(array);
|
||||
}
|
||||
|
||||
@@ -573,14 +573,14 @@ final class AssertJRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamContainsAnyOf" /* Varargs converted to array. */)
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsAnyOf(Refaster.asVarargs(elements));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamContainsAnyOf" /* Varargs converted to array. */)
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsAnyOf(Refaster.asVarargs(elements));
|
||||
}
|
||||
|
||||
@@ -596,13 +596,13 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamContainsAll<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsAll(iterable);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsAll(iterable);
|
||||
}
|
||||
|
||||
@@ -617,13 +617,13 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamContains<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, U[] array) {
|
||||
Stream<S> stream, U[] array, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).contains(array);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, U[] array) {
|
||||
Stream<S> stream, U[] array, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).contains(array);
|
||||
}
|
||||
|
||||
@@ -639,14 +639,14 @@ final class AssertJRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamContains" /* Varargs converted to array. */)
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).contains(Refaster.asVarargs(elements));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamContains" /* Varargs converted to array. */)
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).contains(Refaster.asVarargs(elements));
|
||||
}
|
||||
|
||||
@@ -661,7 +661,7 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamContainsExactlyElementsOf<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsExactlyElementsOf(iterable);
|
||||
}
|
||||
|
||||
@@ -676,7 +676,7 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamContainsExactly<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, U[] array) {
|
||||
Stream<S> stream, U[] array, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsExactly(array);
|
||||
}
|
||||
|
||||
@@ -692,7 +692,7 @@ final class AssertJRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamContainsExactly" /* Varargs converted to array. */)
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsExactly(Refaster.asVarargs(elements));
|
||||
}
|
||||
|
||||
@@ -708,13 +708,13 @@ final class AssertJRules {
|
||||
S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsExactlyInAnyOrderElementsOf(iterable);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
AbstractCollectionAssert<?, ?, T, ?> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Multiset<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends Multiset<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsExactlyInAnyOrderElementsOf(iterable);
|
||||
}
|
||||
|
||||
@@ -729,13 +729,13 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamContainsExactlyInAnyOrder<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, U[] array) {
|
||||
Stream<S> stream, U[] array, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsExactlyInAnyOrder(array);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
AbstractCollectionAssert<?, ?, T, ?> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Multiset<T>> collector, U[] array) {
|
||||
Stream<S> stream, U[] array, Collector<S, ?, ? extends Multiset<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsExactlyInAnyOrder(array);
|
||||
}
|
||||
|
||||
@@ -751,7 +751,7 @@ final class AssertJRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamContainsExactlyInAnyOrder" /* Varargs converted to array. */)
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector))
|
||||
.containsExactlyInAnyOrder(Refaster.asVarargs(elements));
|
||||
}
|
||||
@@ -759,7 +759,7 @@ final class AssertJRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamContainsExactlyInAnyOrder" /* Varargs converted to array. */)
|
||||
AbstractCollectionAssert<?, ?, T, ?> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Multiset<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends Multiset<T>> collector) {
|
||||
return assertThat(stream.collect(collector))
|
||||
.containsExactlyInAnyOrder(Refaster.asVarargs(elements));
|
||||
}
|
||||
@@ -776,13 +776,13 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamContainsSequence<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsSequence(iterable);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, U[] iterable) {
|
||||
Stream<S> stream, U[] iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsSequence(iterable);
|
||||
}
|
||||
|
||||
@@ -798,7 +798,7 @@ final class AssertJRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamContainsSequence" /* Varargs converted to array. */)
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsSequence(Refaster.asVarargs(elements));
|
||||
}
|
||||
|
||||
@@ -814,13 +814,13 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamContainsSubsequence<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsSubsequence(iterable);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, U[] iterable) {
|
||||
Stream<S> stream, U[] iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsSubsequence(iterable);
|
||||
}
|
||||
|
||||
@@ -836,7 +836,7 @@ final class AssertJRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamContainsSubsequence" /* Varargs converted to array. */)
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector))
|
||||
.containsSubsequence(Refaster.asVarargs(elements));
|
||||
}
|
||||
@@ -853,13 +853,13 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamDoesNotContainAnyElementsOf<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).doesNotContainAnyElementsOf(iterable);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).doesNotContainAnyElementsOf(iterable);
|
||||
}
|
||||
|
||||
@@ -874,13 +874,13 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamDoesNotContain<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, U[] array) {
|
||||
Stream<S> stream, U[] array, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).doesNotContain(array);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, U[] array) {
|
||||
Stream<S> stream, U[] array, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).doesNotContain(array);
|
||||
}
|
||||
|
||||
@@ -896,14 +896,14 @@ final class AssertJRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamDoesNotContain" /* Varargs converted to array. */)
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).doesNotContain(Refaster.asVarargs(elements));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamDoesNotContain" /* Varargs converted to array. */)
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).doesNotContain(Refaster.asVarargs(elements));
|
||||
}
|
||||
|
||||
@@ -918,13 +918,13 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamDoesNotContainSequence<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).doesNotContainSequence(iterable);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, U[] iterable) {
|
||||
Stream<S> stream, U[] iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).doesNotContainSequence(iterable);
|
||||
}
|
||||
|
||||
@@ -940,7 +940,7 @@ final class AssertJRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamDoesNotContainSequence" /* Varargs converted to array. */)
|
||||
ListAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector))
|
||||
.doesNotContainSequence(Refaster.asVarargs(elements));
|
||||
}
|
||||
@@ -957,13 +957,13 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamHasSameElementsAs<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).hasSameElementsAs(iterable);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).hasSameElementsAs(iterable);
|
||||
}
|
||||
|
||||
@@ -978,13 +978,13 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamContainsOnly<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, U[] array) {
|
||||
Stream<S> stream, U[] array, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsOnly(array);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, U[] array) {
|
||||
Stream<S> stream, U[] array, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsOnly(array);
|
||||
}
|
||||
|
||||
@@ -1000,14 +1000,14 @@ final class AssertJRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamContainsOnly" /* Varargs converted to array. */)
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsOnly(Refaster.asVarargs(elements));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamContainsOnly" /* Varargs converted to array. */)
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).containsOnly(Refaster.asVarargs(elements));
|
||||
}
|
||||
|
||||
@@ -1022,25 +1022,25 @@ final class AssertJRules {
|
||||
static final class AssertThatStreamIsSubsetOf<S, T extends S, U extends T> {
|
||||
@BeforeTemplate
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).isSubsetOf(iterable);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, U[] iterable) {
|
||||
Stream<S> stream, U[] iterable, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).isSubsetOf(iterable);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, Iterable<U> iterable) {
|
||||
Stream<S> stream, Iterable<U> iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).isSubsetOf(iterable);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, U[] iterable) {
|
||||
Stream<S> stream, U[] iterable, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).isSubsetOf(iterable);
|
||||
}
|
||||
|
||||
@@ -1056,14 +1056,14 @@ final class AssertJRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamIsSubsetOf" /* Varargs converted to array. */)
|
||||
IterableAssert<T> before(
|
||||
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends Iterable<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).isSubsetOf(Refaster.asVarargs(elements));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatStreamIsSubsetOf" /* Varargs converted to array. */)
|
||||
ListAssert<T> before2(
|
||||
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
|
||||
Stream<S> stream, @Repeated U elements, Collector<S, ?, ? extends List<T>> collector) {
|
||||
return assertThat(stream.collect(collector)).isSubsetOf(Refaster.asVarargs(elements));
|
||||
}
|
||||
|
||||
|
||||
@@ -455,14 +455,14 @@ final class AssertJThrowingCallableRules {
|
||||
static final class AssertThatThrownBy {
|
||||
@BeforeTemplate
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
Class<? extends Throwable> exceptionType, ThrowingCallable throwingCallable) {
|
||||
ThrowingCallable throwingCallable, Class<? extends Throwable> exceptionType) {
|
||||
return assertThatExceptionOfType(exceptionType).isThrownBy(throwingCallable);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(
|
||||
Class<? extends Throwable> exceptionType, ThrowingCallable throwingCallable) {
|
||||
ThrowingCallable throwingCallable, Class<? extends Throwable> exceptionType) {
|
||||
return assertThatThrownBy(throwingCallable).isInstanceOf(exceptionType);
|
||||
}
|
||||
}
|
||||
@@ -471,8 +471,8 @@ final class AssertJThrowingCallableRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
Class<? extends Throwable> exceptionType,
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
String message) {
|
||||
return assertThatExceptionOfType(exceptionType)
|
||||
.isThrownBy(throwingCallable)
|
||||
@@ -482,8 +482,8 @@ final class AssertJThrowingCallableRules {
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(
|
||||
Class<? extends Throwable> exceptionType,
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
String message) {
|
||||
return assertThatThrownBy(throwingCallable).isInstanceOf(exceptionType).hasMessage(message);
|
||||
}
|
||||
@@ -493,8 +493,8 @@ final class AssertJThrowingCallableRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
Class<? extends Throwable> exceptionType,
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
String message,
|
||||
@Repeated Object parameters) {
|
||||
return assertThatExceptionOfType(exceptionType)
|
||||
@@ -505,8 +505,8 @@ final class AssertJThrowingCallableRules {
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(
|
||||
Class<? extends Throwable> exceptionType,
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
String message,
|
||||
@Repeated Object parameters) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
@@ -519,8 +519,8 @@ final class AssertJThrowingCallableRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
Class<? extends Throwable> exceptionType,
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
String message) {
|
||||
return assertThatExceptionOfType(exceptionType)
|
||||
.isThrownBy(throwingCallable)
|
||||
@@ -530,8 +530,8 @@ final class AssertJThrowingCallableRules {
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(
|
||||
Class<? extends Throwable> exceptionType,
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
String message) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
.isInstanceOf(exceptionType)
|
||||
@@ -543,8 +543,8 @@ final class AssertJThrowingCallableRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
Class<? extends Throwable> exceptionType,
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
String message) {
|
||||
return assertThatExceptionOfType(exceptionType)
|
||||
.isThrownBy(throwingCallable)
|
||||
@@ -554,8 +554,8 @@ final class AssertJThrowingCallableRules {
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(
|
||||
Class<? extends Throwable> exceptionType,
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
String message) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
.isInstanceOf(exceptionType)
|
||||
@@ -567,8 +567,8 @@ final class AssertJThrowingCallableRules {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
Class<? extends Throwable> exceptionType,
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
String message) {
|
||||
return assertThatExceptionOfType(exceptionType)
|
||||
.isThrownBy(throwingCallable)
|
||||
@@ -578,8 +578,8 @@ final class AssertJThrowingCallableRules {
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(
|
||||
Class<? extends Throwable> exceptionType,
|
||||
ThrowingCallable throwingCallable,
|
||||
Class<? extends Throwable> exceptionType,
|
||||
String message) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
.isInstanceOf(exceptionType)
|
||||
|
||||
@@ -35,13 +35,21 @@ final class CollectionRules {
|
||||
*/
|
||||
static final class CollectionIsEmpty<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("java:S1155" /* This violation will be rewritten. */)
|
||||
@SuppressWarnings({
|
||||
"java:S1155" /* This violation will be rewritten. */,
|
||||
"LexicographicalAnnotationAttributeListing" /* `key-*` entry must remain last. */,
|
||||
"OptionalFirstCollectionElement" /* This is a more specific template. */,
|
||||
"StreamIsEmpty" /* This is a more specific template. */,
|
||||
"key-to-resolve-AnnotationUseStyle-and-TrailingComment-check-conflict"
|
||||
})
|
||||
boolean before(Collection<T> collection) {
|
||||
return Refaster.anyOf(
|
||||
collection.size() == 0,
|
||||
collection.size() <= 0,
|
||||
collection.size() < 1,
|
||||
Iterables.isEmpty(collection));
|
||||
Iterables.isEmpty(collection),
|
||||
collection.stream().findAny().isEmpty(),
|
||||
collection.stream().findFirst().isEmpty());
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
@@ -337,7 +345,9 @@ final class CollectionRules {
|
||||
|
||||
/**
|
||||
* Don't use the ternary operator to extract the first element of a possibly-empty {@link
|
||||
* Collection} as an {@link Optional}.
|
||||
* Collection} as an {@link Optional}, and (when applicable) prefer {@link Stream#findFirst()}
|
||||
* over {@link Stream#findAny()} to communicate that the collection's first element (if any,
|
||||
* according to iteration order) will be returned.
|
||||
*/
|
||||
static final class OptionalFirstCollectionElement<T> {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -7,6 +7,8 @@ import static java.util.Comparator.comparingInt;
|
||||
import static java.util.Comparator.comparingLong;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.Comparator.reverseOrder;
|
||||
import static java.util.stream.Collectors.maxBy;
|
||||
import static java.util.stream.Collectors.minBy;
|
||||
|
||||
import com.google.common.collect.Comparators;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -21,11 +23,13 @@ import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.ToDoubleFunction;
|
||||
import java.util.function.ToIntFunction;
|
||||
import java.util.function.ToLongFunction;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
import tech.picnic.errorprone.refaster.matchers.IsIdentityOperation;
|
||||
@@ -383,4 +387,36 @@ final class ComparatorRules {
|
||||
return Comparators::max;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Comparator#naturalOrder()} over {@link Comparator#reverseOrder()} where possible.
|
||||
*/
|
||||
static final class MinByNaturalOrder<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
Collector<T, ?, Optional<T>> before() {
|
||||
return maxBy(reverseOrder());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
Collector<T, ?, Optional<T>> after() {
|
||||
return minBy(naturalOrder());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Comparator#naturalOrder()} over {@link Comparator#reverseOrder()} where possible.
|
||||
*/
|
||||
static final class MaxByNaturalOrder<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
Collector<T, ?, Optional<T>> before() {
|
||||
return minBy(reverseOrder());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
Collector<T, ?, Optional<T>> after() {
|
||||
return maxBy(naturalOrder());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,23 +157,13 @@ final class EqualityRules {
|
||||
}
|
||||
|
||||
/** Avoid contrived ways of handling {@code null} values during equality testing. */
|
||||
static final class EqualsLhsNullable<T, S> {
|
||||
static final class Equals<T, S> {
|
||||
@BeforeTemplate
|
||||
boolean before(T value1, S value2) {
|
||||
return Optional.ofNullable(value1).equals(Optional.of(value2));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
boolean after(T value1, S value2) {
|
||||
return value2.equals(value1);
|
||||
}
|
||||
}
|
||||
|
||||
/** Avoid contrived ways of handling {@code null} values during equality testing. */
|
||||
static final class EqualsRhsNullable<T, S> {
|
||||
@BeforeTemplate
|
||||
boolean before(T value1, S value2) {
|
||||
return Optional.of(value1).equals(Optional.ofNullable(value2));
|
||||
return Refaster.anyOf(
|
||||
Optional.of(value1).equals(Optional.of(value2)),
|
||||
Optional.of(value1).equals(Optional.ofNullable(value2)),
|
||||
Optional.ofNullable(value2).equals(Optional.of(value1)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@@ -183,7 +173,7 @@ final class EqualityRules {
|
||||
}
|
||||
|
||||
/** Avoid contrived ways of handling {@code null} values during equality testing. */
|
||||
static final class EqualsLhsAndRhsNullable<T, S> {
|
||||
static final class ObjectsEquals<T, S> {
|
||||
@BeforeTemplate
|
||||
boolean before(T value1, S value2) {
|
||||
return Optional.ofNullable(value1).equals(Optional.ofNullable(value2));
|
||||
|
||||
@@ -118,17 +118,17 @@ final class ImmutableListRules {
|
||||
*/
|
||||
static final class ImmutableListSortedCopyOfWithCustomComparator<T> {
|
||||
@BeforeTemplate
|
||||
ImmutableList<T> before(Iterable<T> iterable, Comparator<T> cmp) {
|
||||
ImmutableList<T> before(Comparator<T> cmp, Iterable<T> iterable) {
|
||||
return Streams.stream(iterable).sorted(cmp).collect(toImmutableList());
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableList<T> before(Collection<T> iterable, Comparator<T> cmp) {
|
||||
ImmutableList<T> before(Comparator<T> cmp, Collection<T> iterable) {
|
||||
return iterable.stream().sorted(cmp).collect(toImmutableList());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableList<T> after(Collection<T> iterable, Comparator<? super T> cmp) {
|
||||
ImmutableList<T> after(Comparator<? super T> cmp, Collection<T> iterable) {
|
||||
return ImmutableList.sortedCopyOf(cmp, iterable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
|
||||
/** Refaster rules related to expressions dealing with {@link InputStream}s. */
|
||||
@OnlineDocumentation
|
||||
final class InputStreamRules {
|
||||
private InputStreamRules() {}
|
||||
|
||||
static final class InputStreamTransferTo {
|
||||
@BeforeTemplate
|
||||
long before(InputStream in, OutputStream out) throws IOException {
|
||||
return ByteStreams.copy(in, out);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
long after(InputStream in, OutputStream out) throws IOException {
|
||||
return in.transferTo(out);
|
||||
}
|
||||
}
|
||||
|
||||
static final class InputStreamReadAllBytes {
|
||||
@BeforeTemplate
|
||||
byte[] before(InputStream in) throws IOException {
|
||||
return ByteStreams.toByteArray(in);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
byte[] after(InputStream in) throws IOException {
|
||||
return in.readAllBytes();
|
||||
}
|
||||
}
|
||||
|
||||
static final class InputStreamReadNBytes {
|
||||
@BeforeTemplate
|
||||
byte[] before(InputStream in, int n) throws IOException {
|
||||
return ByteStreams.limit(in, n).readAllBytes();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
byte[] after(InputStream in, int n) throws IOException {
|
||||
return in.readNBytes(n);
|
||||
}
|
||||
}
|
||||
|
||||
static final class InputStreamSkipNBytes {
|
||||
@BeforeTemplate
|
||||
void before(InputStream in, long n) throws IOException {
|
||||
ByteStreams.skipFully(in, n);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
void after(InputStream in, long n) throws IOException {
|
||||
in.skipNBytes(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.errorprone.annotations.DoNotCall;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
@@ -27,6 +26,7 @@ import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.function.Executable;
|
||||
import org.junit.jupiter.api.function.ThrowingSupplier;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
import tech.picnic.errorprone.refaster.annotation.TypeMigration;
|
||||
|
||||
/**
|
||||
* Refaster rules to replace JUnit assertions with AssertJ equivalents.
|
||||
@@ -41,24 +41,267 @@ import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
// `() -> toString()` match both `ThrowingSupplier` and `ThrowingCallable`, but `() -> "constant"`
|
||||
// is only compatible with the former.
|
||||
@OnlineDocumentation
|
||||
@TypeMigration(
|
||||
of = Assertions.class,
|
||||
unmigratedMethods = {
|
||||
"assertAll(Collection<Executable>)",
|
||||
"assertAll(Executable[])",
|
||||
"assertAll(Stream<Executable>)",
|
||||
"assertAll(String, Collection<Executable>)",
|
||||
"assertAll(String, Executable[])",
|
||||
"assertAll(String, Stream<Executable>)",
|
||||
"assertArrayEquals(boolean[], boolean[])",
|
||||
"assertArrayEquals(boolean[], boolean[], String)",
|
||||
"assertArrayEquals(boolean[], boolean[], Supplier<String>)",
|
||||
"assertArrayEquals(byte[], byte[])",
|
||||
"assertArrayEquals(byte[], byte[], String)",
|
||||
"assertArrayEquals(byte[], byte[], Supplier<String>)",
|
||||
"assertArrayEquals(char[], char[])",
|
||||
"assertArrayEquals(char[], char[], String)",
|
||||
"assertArrayEquals(char[], char[], Supplier<String>)",
|
||||
"assertArrayEquals(double[], double[])",
|
||||
"assertArrayEquals(double[], double[], double)",
|
||||
"assertArrayEquals(double[], double[], double, String)",
|
||||
"assertArrayEquals(double[], double[], double, Supplier<String>)",
|
||||
"assertArrayEquals(double[], double[], String)",
|
||||
"assertArrayEquals(double[], double[], Supplier<String>)",
|
||||
"assertArrayEquals(float[], float[])",
|
||||
"assertArrayEquals(float[], float[], float)",
|
||||
"assertArrayEquals(float[], float[], float, String)",
|
||||
"assertArrayEquals(float[], float[], float, Supplier<String>)",
|
||||
"assertArrayEquals(float[], float[], String)",
|
||||
"assertArrayEquals(float[], float[], Supplier<String>)",
|
||||
"assertArrayEquals(int[], int[])",
|
||||
"assertArrayEquals(int[], int[], String)",
|
||||
"assertArrayEquals(int[], int[], Supplier<String>)",
|
||||
"assertArrayEquals(long[], long[])",
|
||||
"assertArrayEquals(long[], long[], String)",
|
||||
"assertArrayEquals(long[], long[], Supplier<String>)",
|
||||
"assertArrayEquals(Object[], Object[])",
|
||||
"assertArrayEquals(Object[], Object[], String)",
|
||||
"assertArrayEquals(Object[], Object[], Supplier<String>)",
|
||||
"assertArrayEquals(short[], short[])",
|
||||
"assertArrayEquals(short[], short[], String)",
|
||||
"assertArrayEquals(short[], short[], Supplier<String>)",
|
||||
"assertEquals(Byte, Byte)",
|
||||
"assertEquals(Byte, byte)",
|
||||
"assertEquals(byte, Byte)",
|
||||
"assertEquals(byte, byte)",
|
||||
"assertEquals(Byte, Byte, String)",
|
||||
"assertEquals(Byte, byte, String)",
|
||||
"assertEquals(byte, Byte, String)",
|
||||
"assertEquals(byte, byte, String)",
|
||||
"assertEquals(Byte, Byte, Supplier<String>)",
|
||||
"assertEquals(Byte, byte, Supplier<String>)",
|
||||
"assertEquals(byte, Byte, Supplier<String>)",
|
||||
"assertEquals(byte, byte, Supplier<String>)",
|
||||
"assertEquals(char, char)",
|
||||
"assertEquals(char, char, String)",
|
||||
"assertEquals(char, char, Supplier<String>)",
|
||||
"assertEquals(char, Character)",
|
||||
"assertEquals(char, Character, String)",
|
||||
"assertEquals(char, Character, Supplier<String>)",
|
||||
"assertEquals(Character, char)",
|
||||
"assertEquals(Character, char, String)",
|
||||
"assertEquals(Character, char, Supplier<String>)",
|
||||
"assertEquals(Character, Character)",
|
||||
"assertEquals(Character, Character, String)",
|
||||
"assertEquals(Character, Character, Supplier<String>)",
|
||||
"assertEquals(Double, Double)",
|
||||
"assertEquals(Double, double)",
|
||||
"assertEquals(double, Double)",
|
||||
"assertEquals(double, double)",
|
||||
"assertEquals(double, double, double)",
|
||||
"assertEquals(double, double, double, String)",
|
||||
"assertEquals(double, double, double, Supplier<String>)",
|
||||
"assertEquals(Double, Double, String)",
|
||||
"assertEquals(Double, double, String)",
|
||||
"assertEquals(double, Double, String)",
|
||||
"assertEquals(double, double, String)",
|
||||
"assertEquals(Double, Double, Supplier<String>)",
|
||||
"assertEquals(Double, double, Supplier<String>)",
|
||||
"assertEquals(double, Double, Supplier<String>)",
|
||||
"assertEquals(double, double, Supplier<String>)",
|
||||
"assertEquals(Float, Float)",
|
||||
"assertEquals(Float, float)",
|
||||
"assertEquals(float, Float)",
|
||||
"assertEquals(float, float)",
|
||||
"assertEquals(float, float, float)",
|
||||
"assertEquals(float, float, float, String)",
|
||||
"assertEquals(float, float, float, Supplier<String>)",
|
||||
"assertEquals(Float, Float, String)",
|
||||
"assertEquals(Float, float, String)",
|
||||
"assertEquals(float, Float, String)",
|
||||
"assertEquals(float, float, String)",
|
||||
"assertEquals(Float, Float, Supplier<String>)",
|
||||
"assertEquals(Float, float, Supplier<String>)",
|
||||
"assertEquals(float, Float, Supplier<String>)",
|
||||
"assertEquals(float, float, Supplier<String>)",
|
||||
"assertEquals(int, int)",
|
||||
"assertEquals(int, int, String)",
|
||||
"assertEquals(int, int, Supplier<String>)",
|
||||
"assertEquals(int, Integer)",
|
||||
"assertEquals(int, Integer, String)",
|
||||
"assertEquals(int, Integer, Supplier<String>)",
|
||||
"assertEquals(Integer, int)",
|
||||
"assertEquals(Integer, int, String)",
|
||||
"assertEquals(Integer, int, Supplier<String>)",
|
||||
"assertEquals(Integer, Integer)",
|
||||
"assertEquals(Integer, Integer, String)",
|
||||
"assertEquals(Integer, Integer, Supplier<String>)",
|
||||
"assertEquals(Long, Long)",
|
||||
"assertEquals(Long, long)",
|
||||
"assertEquals(long, Long)",
|
||||
"assertEquals(long, long)",
|
||||
"assertEquals(Long, Long, String)",
|
||||
"assertEquals(Long, long, String)",
|
||||
"assertEquals(long, Long, String)",
|
||||
"assertEquals(long, long, String)",
|
||||
"assertEquals(Long, Long, Supplier<String>)",
|
||||
"assertEquals(Long, long, Supplier<String>)",
|
||||
"assertEquals(long, Long, Supplier<String>)",
|
||||
"assertEquals(long, long, Supplier<String>)",
|
||||
"assertEquals(Object, Object)",
|
||||
"assertEquals(Object, Object, String)",
|
||||
"assertEquals(Object, Object, Supplier<String>)",
|
||||
"assertEquals(Short, Short)",
|
||||
"assertEquals(Short, short)",
|
||||
"assertEquals(short, Short)",
|
||||
"assertEquals(short, short)",
|
||||
"assertEquals(Short, Short, String)",
|
||||
"assertEquals(Short, short, String)",
|
||||
"assertEquals(short, Short, String)",
|
||||
"assertEquals(short, short, String)",
|
||||
"assertEquals(Short, Short, Supplier<String>)",
|
||||
"assertEquals(Short, short, Supplier<String>)",
|
||||
"assertEquals(short, Short, Supplier<String>)",
|
||||
"assertEquals(short, short, Supplier<String>)",
|
||||
"assertFalse(BooleanSupplier)",
|
||||
"assertFalse(BooleanSupplier, String)",
|
||||
"assertFalse(BooleanSupplier, Supplier<String>)",
|
||||
"assertIterableEquals(Iterable<?>, Iterable<?>)",
|
||||
"assertIterableEquals(Iterable<?>, Iterable<?>, String)",
|
||||
"assertIterableEquals(Iterable<?>, Iterable<?>, Supplier<String>)",
|
||||
"assertLinesMatch(List<String>, List<String>)",
|
||||
"assertLinesMatch(List<String>, List<String>, String)",
|
||||
"assertLinesMatch(List<String>, List<String>, Supplier<String>)",
|
||||
"assertLinesMatch(Stream<String>, Stream<String>)",
|
||||
"assertLinesMatch(Stream<String>, Stream<String>, String)",
|
||||
"assertLinesMatch(Stream<String>, Stream<String>, Supplier<String>)",
|
||||
"assertNotEquals(Byte, Byte)",
|
||||
"assertNotEquals(Byte, byte)",
|
||||
"assertNotEquals(byte, Byte)",
|
||||
"assertNotEquals(byte, byte)",
|
||||
"assertNotEquals(Byte, Byte, String)",
|
||||
"assertNotEquals(Byte, byte, String)",
|
||||
"assertNotEquals(byte, Byte, String)",
|
||||
"assertNotEquals(byte, byte, String)",
|
||||
"assertNotEquals(Byte, Byte, Supplier<String>)",
|
||||
"assertNotEquals(Byte, byte, Supplier<String>)",
|
||||
"assertNotEquals(byte, Byte, Supplier<String>)",
|
||||
"assertNotEquals(byte, byte, Supplier<String>)",
|
||||
"assertNotEquals(char, char)",
|
||||
"assertNotEquals(char, char, String)",
|
||||
"assertNotEquals(char, char, Supplier<String>)",
|
||||
"assertNotEquals(char, Character)",
|
||||
"assertNotEquals(char, Character, String)",
|
||||
"assertNotEquals(char, Character, Supplier<String>)",
|
||||
"assertNotEquals(Character, char)",
|
||||
"assertNotEquals(Character, char, String)",
|
||||
"assertNotEquals(Character, char, Supplier<String>)",
|
||||
"assertNotEquals(Character, Character)",
|
||||
"assertNotEquals(Character, Character, String)",
|
||||
"assertNotEquals(Character, Character, Supplier<String>)",
|
||||
"assertNotEquals(Double, Double)",
|
||||
"assertNotEquals(Double, double)",
|
||||
"assertNotEquals(double, Double)",
|
||||
"assertNotEquals(double, double)",
|
||||
"assertNotEquals(double, double, double)",
|
||||
"assertNotEquals(double, double, double, String)",
|
||||
"assertNotEquals(double, double, double, Supplier<String>)",
|
||||
"assertNotEquals(Double, Double, String)",
|
||||
"assertNotEquals(Double, double, String)",
|
||||
"assertNotEquals(double, Double, String)",
|
||||
"assertNotEquals(double, double, String)",
|
||||
"assertNotEquals(Double, Double, Supplier<String>)",
|
||||
"assertNotEquals(Double, double, Supplier<String>)",
|
||||
"assertNotEquals(double, Double, Supplier<String>)",
|
||||
"assertNotEquals(double, double, Supplier<String>)",
|
||||
"assertNotEquals(Float, Float)",
|
||||
"assertNotEquals(Float, float)",
|
||||
"assertNotEquals(float, Float)",
|
||||
"assertNotEquals(float, float)",
|
||||
"assertNotEquals(float, float, float)",
|
||||
"assertNotEquals(float, float, float, String)",
|
||||
"assertNotEquals(float, float, float, Supplier<String>)",
|
||||
"assertNotEquals(Float, Float, String)",
|
||||
"assertNotEquals(Float, float, String)",
|
||||
"assertNotEquals(float, Float, String)",
|
||||
"assertNotEquals(float, float, String)",
|
||||
"assertNotEquals(Float, Float, Supplier<String>)",
|
||||
"assertNotEquals(Float, float, Supplier<String>)",
|
||||
"assertNotEquals(float, Float, Supplier<String>)",
|
||||
"assertNotEquals(float, float, Supplier<String>)",
|
||||
"assertNotEquals(int, int)",
|
||||
"assertNotEquals(int, int, String)",
|
||||
"assertNotEquals(int, int, Supplier<String>)",
|
||||
"assertNotEquals(int, Integer)",
|
||||
"assertNotEquals(int, Integer, String)",
|
||||
"assertNotEquals(int, Integer, Supplier<String>)",
|
||||
"assertNotEquals(Integer, int)",
|
||||
"assertNotEquals(Integer, int, String)",
|
||||
"assertNotEquals(Integer, int, Supplier<String>)",
|
||||
"assertNotEquals(Integer, Integer)",
|
||||
"assertNotEquals(Integer, Integer, String)",
|
||||
"assertNotEquals(Integer, Integer, Supplier<String>)",
|
||||
"assertNotEquals(Long, Long)",
|
||||
"assertNotEquals(Long, long)",
|
||||
"assertNotEquals(long, Long)",
|
||||
"assertNotEquals(long, long)",
|
||||
"assertNotEquals(Long, Long, String)",
|
||||
"assertNotEquals(Long, long, String)",
|
||||
"assertNotEquals(long, Long, String)",
|
||||
"assertNotEquals(long, long, String)",
|
||||
"assertNotEquals(Long, Long, Supplier<String>)",
|
||||
"assertNotEquals(Long, long, Supplier<String>)",
|
||||
"assertNotEquals(long, Long, Supplier<String>)",
|
||||
"assertNotEquals(long, long, Supplier<String>)",
|
||||
"assertNotEquals(Object, Object)",
|
||||
"assertNotEquals(Object, Object, String)",
|
||||
"assertNotEquals(Object, Object, Supplier<String>)",
|
||||
"assertNotEquals(Short, Short)",
|
||||
"assertNotEquals(Short, short)",
|
||||
"assertNotEquals(short, Short)",
|
||||
"assertNotEquals(short, short)",
|
||||
"assertNotEquals(Short, Short, String)",
|
||||
"assertNotEquals(Short, short, String)",
|
||||
"assertNotEquals(short, Short, String)",
|
||||
"assertNotEquals(short, short, String)",
|
||||
"assertNotEquals(Short, Short, Supplier<String>)",
|
||||
"assertNotEquals(Short, short, Supplier<String>)",
|
||||
"assertNotEquals(short, Short, Supplier<String>)",
|
||||
"assertNotEquals(short, short, Supplier<String>)",
|
||||
"assertTimeout(Duration, Executable)",
|
||||
"assertTimeout(Duration, Executable, String)",
|
||||
"assertTimeout(Duration, Executable, Supplier<String>)",
|
||||
"assertTimeout(Duration, ThrowingSupplier<T>)",
|
||||
"assertTimeout(Duration, ThrowingSupplier<T>, String)",
|
||||
"assertTimeout(Duration, ThrowingSupplier<T>, Supplier<String>)",
|
||||
"assertTimeoutPreemptively(Duration, Executable)",
|
||||
"assertTimeoutPreemptively(Duration, Executable, String)",
|
||||
"assertTimeoutPreemptively(Duration, Executable, Supplier<String>)",
|
||||
"assertTimeoutPreemptively(Duration, ThrowingSupplier<T>)",
|
||||
"assertTimeoutPreemptively(Duration, ThrowingSupplier<T>, String)",
|
||||
"assertTimeoutPreemptively(Duration, ThrowingSupplier<T>, Supplier<String>)",
|
||||
"assertTimeoutPreemptively(Duration, ThrowingSupplier<T>, Supplier<String>, TimeoutFailureFactory<E>)",
|
||||
"assertTrue(BooleanSupplier)",
|
||||
"assertTrue(BooleanSupplier, String)",
|
||||
"assertTrue(BooleanSupplier, Supplier<String>)",
|
||||
"fail(Supplier<String>)"
|
||||
})
|
||||
final class JUnitToAssertJRules {
|
||||
private JUnitToAssertJRules() {}
|
||||
|
||||
public ImmutableSet<Object> elidedTypesAndStaticImports() {
|
||||
return ImmutableSet.of(
|
||||
Assertions.class,
|
||||
assertDoesNotThrow(() -> null),
|
||||
assertInstanceOf(null, null),
|
||||
assertThrows(null, null),
|
||||
assertThrowsExactly(null, null),
|
||||
(Runnable) () -> assertFalse(true),
|
||||
(Runnable) () -> assertNotNull(null),
|
||||
(Runnable) () -> assertNotSame(null, null),
|
||||
(Runnable) () -> assertNull(null),
|
||||
(Runnable) () -> assertSame(null, null),
|
||||
(Runnable) () -> assertTrue(true));
|
||||
}
|
||||
|
||||
static final class ThrowNewAssertionError {
|
||||
@BeforeTemplate
|
||||
void before() {
|
||||
@@ -78,8 +321,13 @@ final class JUnitToAssertJRules {
|
||||
return Assertions.fail(message);
|
||||
}
|
||||
|
||||
// XXX: Add `@UseImportPolicy(STATIC_IMPORT_ALWAYS)` once
|
||||
// https://github.com/google/error-prone/pull/3584 is resolved. Until that time, statically
|
||||
// importing AssertJ's `fail` is likely to clash with an existing static import of JUnit's
|
||||
// `fail`. Note that combining Error Prone's `RemoveUnusedImports` and
|
||||
// `UnnecessarilyFullyQualified` checks and our `StaticImport` check will anyway cause the
|
||||
// method to be imported statically if possible; just in a less efficient manner.
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
T after(String message) {
|
||||
return fail(message);
|
||||
}
|
||||
@@ -91,8 +339,13 @@ final class JUnitToAssertJRules {
|
||||
return Assertions.fail(message, throwable);
|
||||
}
|
||||
|
||||
// XXX: Add `@UseImportPolicy(STATIC_IMPORT_ALWAYS)` once
|
||||
// https://github.com/google/error-prone/pull/3584 is resolved. Until that time, statically
|
||||
// importing AssertJ's `fail` is likely to clash with an existing static import of JUnit's
|
||||
// `fail`. Note that combining Error Prone's `RemoveUnusedImports` and
|
||||
// `UnnecessarilyFullyQualified` checks and our `StaticImport` check will anyway cause the
|
||||
// method to be imported statically if possible; just in a less efficient manner.
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
T after(String message, Throwable throwable) {
|
||||
return fail(message, throwable);
|
||||
}
|
||||
@@ -282,26 +535,26 @@ final class JUnitToAssertJRules {
|
||||
|
||||
static final class AssertThatWithFailMessageStringIsSameAs {
|
||||
@BeforeTemplate
|
||||
void before(Object actual, Object expected, String message) {
|
||||
void before(Object actual, String message, Object expected) {
|
||||
assertSame(expected, actual, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object actual, Object expected, String message) {
|
||||
void after(Object actual, String message, Object expected) {
|
||||
assertThat(actual).withFailMessage(message).isSameAs(expected);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatWithFailMessageSupplierIsSameAs {
|
||||
@BeforeTemplate
|
||||
void before(Object actual, Object expected, Supplier<String> supplier) {
|
||||
void before(Object actual, Supplier<String> supplier, Object expected) {
|
||||
assertSame(expected, actual, supplier);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object actual, Object expected, Supplier<String> supplier) {
|
||||
void after(Object actual, Supplier<String> supplier, Object expected) {
|
||||
assertThat(actual).withFailMessage(supplier).isSameAs(expected);
|
||||
}
|
||||
}
|
||||
@@ -321,26 +574,26 @@ final class JUnitToAssertJRules {
|
||||
|
||||
static final class AssertThatWithFailMessageStringIsNotSameAs {
|
||||
@BeforeTemplate
|
||||
void before(Object actual, Object expected, String message) {
|
||||
void before(Object actual, String message, Object expected) {
|
||||
assertNotSame(expected, actual, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object actual, Object expected, String message) {
|
||||
void after(Object actual, String message, Object expected) {
|
||||
assertThat(actual).withFailMessage(message).isNotSameAs(expected);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatWithFailMessageSupplierIsNotSameAs {
|
||||
@BeforeTemplate
|
||||
void before(Object actual, Object expected, Supplier<String> supplier) {
|
||||
void before(Object actual, Supplier<String> supplier, Object expected) {
|
||||
assertNotSame(expected, actual, supplier);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object actual, Object expected, Supplier<String> supplier) {
|
||||
void after(Object actual, Supplier<String> supplier, Object expected) {
|
||||
assertThat(actual).withFailMessage(supplier).isNotSameAs(expected);
|
||||
}
|
||||
}
|
||||
@@ -361,13 +614,13 @@ final class JUnitToAssertJRules {
|
||||
static final class AssertThatThrownByWithFailMessageStringIsExactlyInstanceOf<
|
||||
T extends Throwable> {
|
||||
@BeforeTemplate
|
||||
void before(Executable throwingCallable, Class<T> clazz, String message) {
|
||||
void before(Executable throwingCallable, String message, Class<T> clazz) {
|
||||
assertThrowsExactly(clazz, throwingCallable, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(ThrowingCallable throwingCallable, Class<T> clazz, String message) {
|
||||
void after(ThrowingCallable throwingCallable, String message, Class<T> clazz) {
|
||||
assertThatThrownBy(throwingCallable).withFailMessage(message).isExactlyInstanceOf(clazz);
|
||||
}
|
||||
}
|
||||
@@ -375,13 +628,13 @@ final class JUnitToAssertJRules {
|
||||
static final class AssertThatThrownByWithFailMessageSupplierIsExactlyInstanceOf<
|
||||
T extends Throwable> {
|
||||
@BeforeTemplate
|
||||
void before(Executable throwingCallable, Class<T> clazz, Supplier<String> supplier) {
|
||||
void before(Executable throwingCallable, Supplier<String> supplier, Class<T> clazz) {
|
||||
assertThrowsExactly(clazz, throwingCallable, supplier);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(ThrowingCallable throwingCallable, Class<T> clazz, Supplier<String> supplier) {
|
||||
void after(ThrowingCallable throwingCallable, Supplier<String> supplier, Class<T> clazz) {
|
||||
assertThatThrownBy(throwingCallable).withFailMessage(supplier).isExactlyInstanceOf(clazz);
|
||||
}
|
||||
}
|
||||
@@ -401,26 +654,26 @@ final class JUnitToAssertJRules {
|
||||
|
||||
static final class AssertThatThrownByWithFailMessageStringIsInstanceOf<T extends Throwable> {
|
||||
@BeforeTemplate
|
||||
void before(Executable throwingCallable, Class<T> clazz, String message) {
|
||||
void before(Executable throwingCallable, String message, Class<T> clazz) {
|
||||
assertThrows(clazz, throwingCallable, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(ThrowingCallable throwingCallable, Class<T> clazz, String message) {
|
||||
void after(ThrowingCallable throwingCallable, String message, Class<T> clazz) {
|
||||
assertThatThrownBy(throwingCallable).withFailMessage(message).isInstanceOf(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByWithFailMessageSupplierIsInstanceOf<T extends Throwable> {
|
||||
@BeforeTemplate
|
||||
void before(Executable throwingCallable, Class<T> clazz, Supplier<String> supplier) {
|
||||
void before(Executable throwingCallable, Supplier<String> supplier, Class<T> clazz) {
|
||||
assertThrows(clazz, throwingCallable, supplier);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(ThrowingCallable throwingCallable, Class<T> clazz, Supplier<String> supplier) {
|
||||
void after(ThrowingCallable throwingCallable, Supplier<String> supplier, Class<T> clazz) {
|
||||
assertThatThrownBy(throwingCallable).withFailMessage(supplier).isInstanceOf(clazz);
|
||||
}
|
||||
}
|
||||
@@ -494,26 +747,26 @@ final class JUnitToAssertJRules {
|
||||
|
||||
static final class AssertThatWithFailMessageStringIsInstanceOf<T> {
|
||||
@BeforeTemplate
|
||||
void before(Object actual, Class<T> clazz, String message) {
|
||||
void before(Object actual, String message, Class<T> clazz) {
|
||||
assertInstanceOf(clazz, actual, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object actual, Class<T> clazz, String message) {
|
||||
void after(Object actual, String message, Class<T> clazz) {
|
||||
assertThat(actual).withFailMessage(message).isInstanceOf(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatWithFailMessageSupplierIsInstanceOf<T> {
|
||||
@BeforeTemplate
|
||||
void before(Object actual, Class<T> clazz, Supplier<String> supplier) {
|
||||
void before(Object actual, Supplier<String> supplier, Class<T> clazz) {
|
||||
assertInstanceOf(clazz, actual, supplier);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object actual, Class<T> clazz, Supplier<String> supplier) {
|
||||
void after(Object actual, Supplier<String> supplier, Class<T> clazz) {
|
||||
assertThat(actual).withFailMessage(supplier).isInstanceOf(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
|
||||
@@ -28,6 +30,21 @@ final class MultimapRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Multimap#isEmpty()} over more contrived alternatives. */
|
||||
static final class MultimapIsEmpty<K, V> {
|
||||
@BeforeTemplate
|
||||
boolean before(Multimap<K, V> multimap) {
|
||||
return Refaster.anyOf(
|
||||
multimap.keySet(), multimap.keys(), multimap.values(), multimap.entries())
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
boolean after(Multimap<K, V> multimap) {
|
||||
return multimap.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Multimap#size()} over more contrived alternatives. */
|
||||
static final class MultimapSize<K, V> {
|
||||
@BeforeTemplate
|
||||
@@ -41,6 +58,32 @@ final class MultimapRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Multimap#containsKey(Object)} over more contrived alternatives. */
|
||||
static final class MultimapContainsKey<K, V, T> {
|
||||
@BeforeTemplate
|
||||
boolean before(Multimap<K, V> multimap, T key) {
|
||||
return Refaster.anyOf(multimap.keySet(), multimap.keys()).contains(key);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
boolean after(Multimap<K, V> multimap, T key) {
|
||||
return multimap.containsKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Multimap#containsValue(Object)} over more contrived alternatives. */
|
||||
static final class MultimapContainsValue<K, V, T> {
|
||||
@BeforeTemplate
|
||||
boolean before(Multimap<K, V> multimap, T value) {
|
||||
return multimap.values().contains(value);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
boolean after(Multimap<K, V> multimap, T value) {
|
||||
return multimap.containsValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Multimap#get(Object)} over more contrived alternatives.
|
||||
*
|
||||
@@ -59,4 +102,30 @@ final class MultimapRules {
|
||||
return multimap.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily use {@link Multimap#entries()}. */
|
||||
static final class MultimapKeysStream<K, V> {
|
||||
@BeforeTemplate
|
||||
Stream<K> before(Multimap<K, V> multimap) {
|
||||
return multimap.entries().stream().map(Map.Entry::getKey);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Stream<K> after(Multimap<K, V> multimap) {
|
||||
return multimap.keys().stream();
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily use {@link Multimap#entries()}. */
|
||||
static final class MultimapValuesStream<K, V> {
|
||||
@BeforeTemplate
|
||||
Stream<V> before(Multimap<K, V> multimap) {
|
||||
return multimap.entries().stream().map(Map.Entry::getValue);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Stream<V> after(Multimap<K, V> multimap) {
|
||||
return multimap.values().stream();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,6 +388,7 @@ final class OptionalRules {
|
||||
@BeforeTemplate
|
||||
Optional<T> before(Optional<T> optional, Comparator<? super T> comparator) {
|
||||
return Refaster.anyOf(
|
||||
optional.or(Refaster.anyOf(() -> Optional.empty(), Optional::empty)),
|
||||
optional.stream().findFirst(),
|
||||
optional.stream().findAny(),
|
||||
optional.stream().min(comparator),
|
||||
|
||||
@@ -5,7 +5,11 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.MoreCollectors.toOptional;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.Comparator.reverseOrder;
|
||||
import static java.util.function.Function.identity;
|
||||
import static java.util.stream.Collectors.maxBy;
|
||||
import static java.util.stream.Collectors.minBy;
|
||||
import static java.util.stream.Collectors.toCollection;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static reactor.function.TupleUtils.function;
|
||||
@@ -41,6 +45,7 @@ import org.jspecify.annotations.Nullable;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.math.MathFlux;
|
||||
import reactor.test.StepVerifier;
|
||||
import reactor.test.publisher.PublisherProbe;
|
||||
import reactor.util.context.Context;
|
||||
@@ -100,7 +105,7 @@ final class ReactorRules {
|
||||
}
|
||||
|
||||
/** Prefer {@link Mono#justOrEmpty(Object)} over more contrived alternatives. */
|
||||
static final class MonoJustOrEmptyObject<@Nullable T> {
|
||||
static final class MonoJustOrEmptyObject<T extends @Nullable Object> {
|
||||
@BeforeTemplate
|
||||
Mono<T> before(T value) {
|
||||
return Mono.justOrEmpty(Optional.ofNullable(value));
|
||||
@@ -437,10 +442,12 @@ final class ReactorRules {
|
||||
static final class FluxEmpty<T, S extends Comparable<? super S>> {
|
||||
@BeforeTemplate
|
||||
Flux<T> before(
|
||||
int prefetch,
|
||||
Function<? super Object[], ? extends T> combinator,
|
||||
int prefetch,
|
||||
Comparator<? super T> comparator) {
|
||||
return Refaster.anyOf(
|
||||
Flux.zip(combinator),
|
||||
Flux.zip(combinator, prefetch),
|
||||
Flux.concat(),
|
||||
Flux.concatDelayError(),
|
||||
Flux.firstWithSignal(),
|
||||
@@ -456,13 +463,11 @@ final class ReactorRules {
|
||||
Flux.mergePriorityDelayError(prefetch, comparator),
|
||||
Flux.mergeSequential(),
|
||||
Flux.mergeSequential(prefetch),
|
||||
Flux.mergeSequentialDelayError(prefetch),
|
||||
Flux.zip(combinator),
|
||||
Flux.zip(combinator, prefetch));
|
||||
Flux.mergeSequentialDelayError(prefetch));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
Flux<T> before(int prefetch, Function<Object[], T> combinator) {
|
||||
Flux<T> before(Function<Object[], T> combinator, int prefetch) {
|
||||
return Refaster.anyOf(
|
||||
Flux.combineLatest(combinator), Flux.combineLatest(combinator, prefetch));
|
||||
}
|
||||
@@ -596,6 +601,11 @@ final class ReactorRules {
|
||||
|
||||
/** Avoid contrived alternatives to {@link Mono#flatMapIterable(Function)}. */
|
||||
static final class MonoFlatMapIterable<T, S, I extends Iterable<? extends S>> {
|
||||
@BeforeTemplate
|
||||
Flux<S> before(Mono<T> mono, Function<? super T, I> function) {
|
||||
return mono.map(function).flatMapMany(Flux::fromIterable);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
Flux<S> before(
|
||||
Mono<T> mono,
|
||||
@@ -608,7 +618,7 @@ final class ReactorRules {
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Flux<S> after(Mono<T> mono, Function<? super T, ? extends Iterable<? extends S>> function) {
|
||||
Flux<S> after(Mono<T> mono, Function<? super T, I> function) {
|
||||
return mono.flatMapIterable(function);
|
||||
}
|
||||
}
|
||||
@@ -727,7 +737,7 @@ final class ReactorRules {
|
||||
abstract S transformation(@MayOptionallyUse T value);
|
||||
|
||||
@BeforeTemplate
|
||||
Flux<S> before(Flux<T> flux, boolean delayUntilEnd, int maxConcurrency, int prefetch) {
|
||||
Flux<S> before(Flux<T> flux, int prefetch, boolean delayUntilEnd, int maxConcurrency) {
|
||||
return Refaster.anyOf(
|
||||
flux.concatMap(x -> Mono.just(transformation(x))),
|
||||
flux.concatMap(x -> Flux.just(transformation(x))),
|
||||
@@ -795,7 +805,7 @@ final class ReactorRules {
|
||||
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("java:S138" /* Method is long, but not complex. */)
|
||||
Publisher<S> before(Flux<T> flux, boolean delayUntilEnd, int maxConcurrency, int prefetch) {
|
||||
Publisher<S> before(Flux<T> flux, int prefetch, boolean delayUntilEnd, int maxConcurrency) {
|
||||
return Refaster.anyOf(
|
||||
flux.concatMap(
|
||||
x ->
|
||||
@@ -950,11 +960,12 @@ final class ReactorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Avoid vacuous invocations of {@link Mono#ignoreElement()}. */
|
||||
/** Avoid vacuous operations prior to invocation of {@link Mono#thenMany(Publisher)}. */
|
||||
static final class MonoThenMany<T, S> {
|
||||
@BeforeTemplate
|
||||
Flux<S> before(Mono<T> mono, Publisher<S> publisher) {
|
||||
return mono.ignoreElement().thenMany(publisher);
|
||||
return Refaster.anyOf(
|
||||
mono.ignoreElement().thenMany(publisher), mono.flux().thenMany(publisher));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@@ -992,11 +1003,11 @@ final class ReactorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Avoid vacuous invocations of {@link Mono#ignoreElement()}. */
|
||||
/** Avoid vacuous operations prior to invocation of {@link Mono#then(Mono)}. */
|
||||
static final class MonoThenMono<T, S> {
|
||||
@BeforeTemplate
|
||||
Mono<S> before(Mono<T> mono1, Mono<S> mono2) {
|
||||
return mono1.ignoreElement().then(mono2);
|
||||
return Refaster.anyOf(mono1.ignoreElement().then(mono2), mono1.flux().then(mono2));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
@@ -1031,12 +1042,15 @@ final class ReactorRules {
|
||||
/** Prefer {@link Mono#singleOptional()} over more contrived alternatives. */
|
||||
// XXX: Consider creating a plugin that flags/discourages `Mono<Optional<T>>` method return
|
||||
// types, just as we discourage nullable `Boolean`s and `Optional`s.
|
||||
// XXX: The `mono.transform(Mono::singleOptional)` replacement is a special case of a more general
|
||||
// rule. Consider introducing an Error Prone check for this.
|
||||
static final class MonoSingleOptional<T> {
|
||||
@BeforeTemplate
|
||||
Mono<Optional<T>> before(Mono<T> mono) {
|
||||
return Refaster.anyOf(
|
||||
mono.flux().collect(toOptional()),
|
||||
mono.map(Optional::of).defaultIfEmpty(Optional.empty()));
|
||||
mono.map(Optional::of).defaultIfEmpty(Optional.empty()),
|
||||
mono.transform(Mono::singleOptional));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@@ -1125,9 +1139,9 @@ final class ReactorRules {
|
||||
Function<? super S, P> function,
|
||||
@Matches(IsIdentityOperation.class)
|
||||
Function<? super P, ? extends Publisher<? extends T>> identityOperation,
|
||||
int prefetch,
|
||||
boolean delayUntilEnd,
|
||||
int maxConcurrency,
|
||||
int prefetch) {
|
||||
int maxConcurrency) {
|
||||
return Refaster.anyOf(
|
||||
mono.map(function).flatMapMany(identityOperation),
|
||||
mono.flux().concatMap(function),
|
||||
@@ -1191,6 +1205,19 @@ final class ReactorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Flux#fromIterable(Iterable)} over less efficient alternatives. */
|
||||
static final class FluxFromIterable<T> {
|
||||
@BeforeTemplate
|
||||
Flux<T> before(Collection<T> collection) {
|
||||
return Flux.fromStream(collection.stream());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Flux<T> after(Collection<T> collection) {
|
||||
return Flux.fromIterable(collection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Flux#count()} followed by a conversion from {@code long} to {@code int} over
|
||||
* collecting into a list and counting its elements.
|
||||
@@ -1579,6 +1606,107 @@ final class ReactorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Flux#sort()} over more verbose alternatives. */
|
||||
static final class FluxSort<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
Flux<T> before(Flux<T> flux) {
|
||||
return flux.sort(naturalOrder());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Flux<T> after(Flux<T> flux) {
|
||||
return flux.sort();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link MathFlux#min(Publisher)} over less efficient alternatives. */
|
||||
static final class FluxTransformMin<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
Mono<T> before(Flux<T> flux) {
|
||||
return flux.sort().next();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<T> after(Flux<T> flux) {
|
||||
return flux.transform(MathFlux::min).singleOrEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link MathFlux#min(Publisher, Comparator)} over less efficient or more verbose
|
||||
* alternatives.
|
||||
*/
|
||||
static final class FluxTransformMinWithComparator<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
Mono<T> before(Flux<T> flux, Comparator<? super T> cmp) {
|
||||
return Refaster.anyOf(
|
||||
flux.sort(cmp).next(), flux.collect(minBy(cmp)).flatMap(Mono::justOrEmpty));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<T> after(Flux<T> flux, Comparator<? super T> cmp) {
|
||||
return flux.transform(f -> MathFlux.min(f, cmp)).singleOrEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link MathFlux#max(Publisher)} over less efficient alternatives. */
|
||||
static final class FluxTransformMax<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
Mono<T> before(Flux<T> flux) {
|
||||
return flux.sort().last();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<T> after(Flux<T> flux) {
|
||||
return flux.transform(MathFlux::max).singleOrEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link MathFlux#max(Publisher, Comparator)} over less efficient or more verbose
|
||||
* alternatives.
|
||||
*/
|
||||
static final class FluxTransformMaxWithComparator<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
Mono<T> before(Flux<T> flux, Comparator<? super T> cmp) {
|
||||
return Refaster.anyOf(
|
||||
flux.sort(cmp).last(), flux.collect(maxBy(cmp)).flatMap(Mono::justOrEmpty));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<T> after(Flux<T> flux, Comparator<? super T> cmp) {
|
||||
return flux.transform(f -> MathFlux.max(f, cmp)).singleOrEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link MathFlux#min(Publisher)} over more contrived alternatives. */
|
||||
static final class MathFluxMin<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
Mono<T> before(Publisher<T> publisher) {
|
||||
return Refaster.anyOf(
|
||||
MathFlux.min(publisher, naturalOrder()), MathFlux.max(publisher, reverseOrder()));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<T> after(Publisher<T> publisher) {
|
||||
return MathFlux.min(publisher);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link MathFlux#max(Publisher)} over more contrived alternatives. */
|
||||
static final class MathFluxMax<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
Mono<T> before(Publisher<T> publisher) {
|
||||
return Refaster.anyOf(
|
||||
MathFlux.min(publisher, reverseOrder()), MathFlux.max(publisher, naturalOrder()));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<T> after(Publisher<T> publisher) {
|
||||
return MathFlux.max(publisher);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link reactor.util.context.Context#empty()}} over more verbose alternatives. */
|
||||
// XXX: Introduce Refaster rules or a `BugChecker` that maps `(Immutable)Map.of(k, v)` to
|
||||
// `Context.of(k, v)` and likewise for multi-pair overloads.
|
||||
@@ -1611,7 +1739,7 @@ final class ReactorRules {
|
||||
static final class StepVerifierFromMono<T> {
|
||||
@BeforeTemplate
|
||||
StepVerifier.FirstStep<? extends T> before(Mono<T> mono) {
|
||||
return StepVerifier.create(mono);
|
||||
return Refaster.anyOf(StepVerifier.create(mono), mono.flux().as(StepVerifier::create));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@@ -1669,7 +1797,7 @@ final class ReactorRules {
|
||||
// a `@Matches(DoesNotDropElements.class)` or `@NotMatches(MayDropElements.class)` guard.
|
||||
static final class FluxAsStepVerifierExpectNext<T, L extends List<T>> {
|
||||
@BeforeTemplate
|
||||
StepVerifier.Step<L> before(Flux<T> flux, Collector<? super T, ?, L> listCollector, T object) {
|
||||
StepVerifier.Step<L> before(Flux<T> flux, T object, Collector<? super T, ?, L> listCollector) {
|
||||
return flux.collect(listCollector)
|
||||
.as(StepVerifier::create)
|
||||
.assertNext(list -> assertThat(list).containsExactly(object));
|
||||
|
||||
@@ -4,6 +4,7 @@ import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.Comparator.reverseOrder;
|
||||
import static java.util.function.Predicate.not;
|
||||
import static java.util.stream.Collectors.collectingAndThen;
|
||||
import static java.util.stream.Collectors.counting;
|
||||
import static java.util.stream.Collectors.filtering;
|
||||
import static java.util.stream.Collectors.flatMapping;
|
||||
@@ -37,6 +38,7 @@ import java.util.Comparator;
|
||||
import java.util.DoubleSummaryStatistics;
|
||||
import java.util.IntSummaryStatistics;
|
||||
import java.util.LongSummaryStatistics;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BinaryOperator;
|
||||
@@ -254,15 +256,21 @@ final class StreamRules {
|
||||
// XXX: This rule assumes that any matched `Collector` does not perform any filtering.
|
||||
// (Perhaps we could add a `@Matches` guard that validates that the collector expression does not
|
||||
// contain a `Collectors#filtering` call. That'd still not be 100% accurate, though.)
|
||||
static final class StreamIsEmpty<T> {
|
||||
static final class StreamIsEmpty<T, K, V, C extends Collection<K>, M extends Map<K, V>> {
|
||||
@BeforeTemplate
|
||||
boolean before(Stream<T> stream, Collector<? super T, ?, ? extends Collection<?>> collector) {
|
||||
boolean before(Stream<T> stream, Collector<? super T, ?, ? extends C> collector) {
|
||||
return Refaster.anyOf(
|
||||
stream.count() == 0,
|
||||
stream.count() <= 0,
|
||||
stream.count() < 1,
|
||||
stream.findFirst().isEmpty(),
|
||||
stream.collect(collector).isEmpty());
|
||||
stream.collect(collector).isEmpty(),
|
||||
stream.collect(collectingAndThen(collector, C::isEmpty)));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
boolean before2(Stream<T> stream, Collector<? super T, ?, ? extends M> collector) {
|
||||
return stream.collect(collectingAndThen(collector, M::isEmpty));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
|
||||
@@ -29,7 +29,9 @@ final class StringRules {
|
||||
private StringRules() {}
|
||||
|
||||
/** Prefer {@link String#isEmpty()} over alternatives that consult the string's length. */
|
||||
// XXX: Once we target JDK 15+, generalize this rule to cover all `CharSequence` subtypes.
|
||||
// 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
|
||||
boolean before(String str) {
|
||||
@@ -44,7 +46,9 @@ final class StringRules {
|
||||
}
|
||||
|
||||
/** Prefer a method reference to {@link String#isEmpty()} over the equivalent lambda function. */
|
||||
// XXX: Once we target JDK 15+, generalize this rule to cover all `CharSequence` subtypes.
|
||||
// XXX: Now that we build with JDK 15+, this rule can be generalized to cover all `CharSequence`
|
||||
// subtypes. However, `CharSequence::isEmpty` isn't as nice as `String::isEmpty`, so we might want
|
||||
// to introduce a rule that suggests `String::isEmpty` where possible.
|
||||
// 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.
|
||||
static final class StringIsEmptyPredicate {
|
||||
@@ -60,7 +64,9 @@ final class StringRules {
|
||||
}
|
||||
|
||||
/** Prefer a method reference to {@link String#isEmpty()} over the equivalent lambda function. */
|
||||
// XXX: Once we target JDK 15+, generalize this rule to cover all `CharSequence` subtypes.
|
||||
// XXX: Now that we build with JDK 15+, this rule can be generalized to cover all `CharSequence`
|
||||
// subtypes. However, `CharSequence::isEmpty` isn't as nice as `String::isEmpty`, so we might want
|
||||
// to introduce a rule that suggests `String::isEmpty` where possible.
|
||||
static final class StringIsNotEmptyPredicate {
|
||||
@BeforeTemplate
|
||||
Predicate<String> before() {
|
||||
@@ -162,6 +168,39 @@ final class StringRules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer direct invocation of {@link String#String(char[], int, int)} over the indirection
|
||||
* introduced by alternatives.
|
||||
*/
|
||||
static final class NewStringFromCharArraySubSequence {
|
||||
@BeforeTemplate
|
||||
String before(char[] data, int offset, int count) {
|
||||
return Refaster.anyOf(
|
||||
String.valueOf(data, offset, count), String.copyValueOf(data, offset, count));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(char[] data, int offset, int count) {
|
||||
return new String(data, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer direct invocation of {@link String#String(char[])} over the indirection introduced by
|
||||
* alternatives.
|
||||
*/
|
||||
static final class NewStringFromCharArray {
|
||||
@BeforeTemplate
|
||||
String before(char[] data) {
|
||||
return Refaster.anyOf(String.valueOf(data), new String(data, 0, data.length));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(char[] data) {
|
||||
return new String(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer direct delegation to {@link String#valueOf(Object)} over the indirection introduced by
|
||||
* {@link Objects#toString(Object)}.
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
||||
import org.testng.Assert;
|
||||
import org.testng.Assert.ThrowingRunnable;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
import tech.picnic.errorprone.refaster.annotation.TypeMigration;
|
||||
|
||||
/**
|
||||
* Refaster rules that replace TestNG assertions with equivalent AssertJ assertions.
|
||||
@@ -48,32 +49,107 @@ import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
* List<Map<String, Object>> myMaps = new ArrayList<>();
|
||||
* assertEquals(myMaps, ImmutableList.of(ImmutableMap.of()));
|
||||
* }</pre>
|
||||
*
|
||||
* <p>A few {@link Assert} methods are not rewritten:
|
||||
*
|
||||
* <ul>
|
||||
* <li>These methods cannot (easily) be expressed using AssertJ because they mix regular equality
|
||||
* and array equality:
|
||||
* <ul>
|
||||
* <li>{@link Assert#assertEqualsDeep(Map, Map)}
|
||||
* <li>{@link Assert#assertEqualsDeep(Map, Map, String)}
|
||||
* <li>{@link Assert#assertEqualsDeep(Set, Set, String)}
|
||||
* <li>{@link Assert#assertNotEqualsDeep(Map, Map)}
|
||||
* <li>{@link Assert#assertNotEqualsDeep(Map, Map, String)}
|
||||
* <li>{@link Assert#assertNotEqualsDeep(Set, Set)}
|
||||
* <li>{@link Assert#assertNotEqualsDeep(Set, Set, String)}
|
||||
* </ul>
|
||||
* <li>This method returns the caught exception; there is no direct counterpart for this in
|
||||
* AssertJ:
|
||||
* <ul>
|
||||
* <li>{@link Assert#expectThrows(Class, ThrowingRunnable)}
|
||||
* </ul>
|
||||
* </ul>
|
||||
*/
|
||||
// XXX: As-is these rules do not result in a complete migration:
|
||||
// - Expressions containing comments are skipped due to a limitation of Refaster.
|
||||
// - Assertions inside lambda expressions are also skipped. Unclear why.
|
||||
// XXX: The `assertEquals` tests for this class generally use the same expression for `expected` and
|
||||
// `actual`, which makes the validation weaker than necessary; fix this. (And investigate whether we
|
||||
// can introduce validation for this.)
|
||||
@OnlineDocumentation
|
||||
@TypeMigration(
|
||||
of = Assert.class,
|
||||
unmigratedMethods = {
|
||||
// XXX: Add migrations for the methods below.
|
||||
"assertEquals(Boolean, Boolean)",
|
||||
"assertEquals(Boolean, boolean)",
|
||||
"assertEquals(boolean, Boolean)",
|
||||
"assertEquals(Boolean, Boolean, String)",
|
||||
"assertEquals(Boolean, boolean, String)",
|
||||
"assertEquals(boolean, Boolean, String)",
|
||||
"assertEquals(Byte, Byte)",
|
||||
"assertEquals(Byte, byte)",
|
||||
"assertEquals(byte, Byte)",
|
||||
"assertEquals(Byte, Byte, String)",
|
||||
"assertEquals(Byte, byte, String)",
|
||||
"assertEquals(byte, Byte, String)",
|
||||
"assertEquals(char, Character)",
|
||||
"assertEquals(char, Character, String)",
|
||||
"assertEquals(Character, char)",
|
||||
"assertEquals(Character, char, String)",
|
||||
"assertEquals(Character, Character)",
|
||||
"assertEquals(Character, Character, String)",
|
||||
"assertEquals(Double, Double)",
|
||||
"assertEquals(Double, double)",
|
||||
"assertEquals(double, Double)",
|
||||
"assertEquals(Double, Double, String)",
|
||||
"assertEquals(Double, double, String)",
|
||||
"assertEquals(double, Double, String)",
|
||||
"assertEquals(double[], double[], double)",
|
||||
"assertEquals(double[], double[], double, String)",
|
||||
"assertEquals(Float, Float)",
|
||||
"assertEquals(Float, float)",
|
||||
"assertEquals(float, Float)",
|
||||
"assertEquals(Float, Float, String)",
|
||||
"assertEquals(Float, float, String)",
|
||||
"assertEquals(float, Float, String)",
|
||||
"assertEquals(float[], float[], float)",
|
||||
"assertEquals(float[], float[], float, String)",
|
||||
"assertEquals(int, Integer)",
|
||||
"assertEquals(int, Integer, String)",
|
||||
"assertEquals(Integer, int)",
|
||||
"assertEquals(Integer, int, String)",
|
||||
"assertEquals(Integer, Integer)",
|
||||
"assertEquals(Integer, Integer, String)",
|
||||
"assertEquals(Long, Long)",
|
||||
"assertEquals(Long, long)",
|
||||
"assertEquals(long, Long)",
|
||||
"assertEquals(Long, Long, String)",
|
||||
"assertEquals(Long, long, String)",
|
||||
"assertEquals(Short, Short)",
|
||||
"assertEquals(Short, short)",
|
||||
"assertEquals(short, Short)",
|
||||
"assertEquals(Short, Short, String)",
|
||||
"assertEquals(Short, short, String)",
|
||||
"assertEquals(short, Short, String)",
|
||||
/*
|
||||
* These `assertEqualsDeep` methods cannot (easily) be expressed using AssertJ because they
|
||||
* mix regular equality and array equality:
|
||||
*/
|
||||
"assertEqualsDeep(Map<?, ?>, Map<?, ?>)",
|
||||
"assertEqualsDeep(Map<?, ?>, Map<?, ?>, String)",
|
||||
"assertEqualsDeep(Set<?>, Set<?>, String)",
|
||||
// XXX: Add migrations for the methods below.
|
||||
"assertEqualsNoOrder(Collection<?>, Collection<?>)",
|
||||
"assertEqualsNoOrder(Collection<?>, Collection<?>, String)",
|
||||
"assertEqualsNoOrder(Iterator<?>, Iterator<?>)",
|
||||
"assertEqualsNoOrder(Iterator<?>, Iterator<?>, String)",
|
||||
"assertListContains(List<T>, Predicate<T>, String)",
|
||||
"assertListContainsObject(List<T>, T, String)",
|
||||
"assertListNotContains(List<T>, Predicate<T>, String)",
|
||||
"assertListNotContainsObject(List<T>, T, String)",
|
||||
"assertNotEquals(Collection<?>, Collection<?>)",
|
||||
"assertNotEquals(Collection<?>, Collection<?>, String)",
|
||||
"assertNotEquals(Iterator<?>, Iterator<?>)",
|
||||
"assertNotEquals(Iterator<?>, Iterator<?>, String)",
|
||||
"assertNotEquals(Object[], Object[], String)",
|
||||
/*
|
||||
* These `assertNotEqualsDeep` methods cannot (easily) be expressed using AssertJ because they
|
||||
* mix regular equality and array equality:
|
||||
*/
|
||||
"assertNotEqualsDeep(Map<?, ?>, Map<?, ?>)",
|
||||
"assertNotEqualsDeep(Map<?, ?>, Map<?, ?>, String)",
|
||||
"assertNotEqualsDeep(Set<?>, Set<?>)",
|
||||
"assertNotEqualsDeep(Set<?>, Set<?>, String)",
|
||||
// XXX: Add a migration for this `assertThrows` method.
|
||||
"assertThrows(String, Class<T>, ThrowingRunnable)",
|
||||
/*
|
||||
* These `expectThrows` methods return the caught exception; there is no direct counterpart
|
||||
* for this in AssertJ.
|
||||
*/
|
||||
"expectThrows(Class<T>, ThrowingRunnable)",
|
||||
"expectThrows(String, Class<T>, ThrowingRunnable)"
|
||||
})
|
||||
final class TestNGToAssertJRules {
|
||||
private TestNGToAssertJRules() {}
|
||||
|
||||
@@ -237,13 +313,13 @@ final class TestNGToAssertJRules {
|
||||
|
||||
static final class AssertSameWithMessage {
|
||||
@BeforeTemplate
|
||||
void before(Object actual, Object expected, String message) {
|
||||
void before(Object actual, String message, Object expected) {
|
||||
assertSame(actual, expected, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object actual, Object expected, String message) {
|
||||
void after(Object actual, String message, Object expected) {
|
||||
assertThat(actual).withFailMessage(message).isSameAs(expected);
|
||||
}
|
||||
}
|
||||
@@ -263,13 +339,13 @@ final class TestNGToAssertJRules {
|
||||
|
||||
static final class AssertNotSameWithMessage {
|
||||
@BeforeTemplate
|
||||
void before(Object actual, Object expected, String message) {
|
||||
void before(Object actual, String message, Object expected) {
|
||||
assertNotSame(actual, expected, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object actual, Object expected, String message) {
|
||||
void after(Object actual, String message, Object expected) {
|
||||
assertThat(actual).withFailMessage(message).isNotSameAs(expected);
|
||||
}
|
||||
}
|
||||
@@ -339,63 +415,63 @@ final class TestNGToAssertJRules {
|
||||
|
||||
static final class AssertEqualWithMessage {
|
||||
@BeforeTemplate
|
||||
void before(boolean actual, boolean expected, String message) {
|
||||
void before(boolean actual, String message, boolean expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(byte actual, byte expected, String message) {
|
||||
void before(byte actual, String message, byte expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(char actual, char expected, String message) {
|
||||
void before(char actual, String message, char expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(short actual, short expected, String message) {
|
||||
void before(short actual, String message, short expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(int actual, int expected, String message) {
|
||||
void before(int actual, String message, int expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(long actual, long expected, String message) {
|
||||
void before(long actual, String message, long expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(float actual, float expected, String message) {
|
||||
void before(float actual, String message, float expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(double actual, double expected, String message) {
|
||||
void before(double actual, String message, double expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(Object actual, Object expected, String message) {
|
||||
void before(Object actual, String message, Object expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(String actual, String expected, String message) {
|
||||
void before(String actual, String message, String expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(Map<?, ?> actual, Map<?, ?> expected, String message) {
|
||||
void before(Map<?, ?> actual, String message, Map<?, ?> expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object actual, Object expected, String message) {
|
||||
void after(Object actual, String message, Object expected) {
|
||||
assertThat(actual).withFailMessage(message).isEqualTo(expected);
|
||||
}
|
||||
}
|
||||
@@ -415,13 +491,13 @@ final class TestNGToAssertJRules {
|
||||
|
||||
static final class AssertEqualFloatsWithDeltaWithMessage {
|
||||
@BeforeTemplate
|
||||
void before(float actual, float expected, float delta, String message) {
|
||||
void before(float actual, String message, float expected, float delta) {
|
||||
assertEquals(actual, expected, delta, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(float actual, float expected, float delta, String message) {
|
||||
void after(float actual, String message, float expected, float delta) {
|
||||
assertThat(actual).withFailMessage(message).isCloseTo(expected, offset(delta));
|
||||
}
|
||||
}
|
||||
@@ -441,13 +517,13 @@ final class TestNGToAssertJRules {
|
||||
|
||||
static final class AssertEqualDoublesWithDeltaWithMessage {
|
||||
@BeforeTemplate
|
||||
void before(double actual, double expected, double delta, String message) {
|
||||
void before(double actual, String message, double expected, double delta) {
|
||||
assertEquals(actual, expected, delta, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(double actual, double expected, double delta, String message) {
|
||||
void after(double actual, String message, double expected, double delta) {
|
||||
assertThat(actual).withFailMessage(message).isCloseTo(expected, offset(delta));
|
||||
}
|
||||
}
|
||||
@@ -507,53 +583,53 @@ final class TestNGToAssertJRules {
|
||||
|
||||
static final class AssertEqualArrayIterationOrderWithMessage {
|
||||
@BeforeTemplate
|
||||
void before(boolean[] actual, boolean[] expected, String message) {
|
||||
void before(boolean[] actual, String message, boolean[] expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(byte[] actual, byte[] expected, String message) {
|
||||
void before(byte[] actual, String message, byte[] expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(char[] actual, char[] expected, String message) {
|
||||
void before(char[] actual, String message, char[] expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(short[] actual, short[] expected, String message) {
|
||||
void before(short[] actual, String message, short[] expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(int[] actual, int[] expected, String message) {
|
||||
void before(int[] actual, String message, int[] expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(long[] actual, long[] expected, String message) {
|
||||
void before(long[] actual, String message, long[] expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(float[] actual, float[] expected, String message) {
|
||||
void before(float[] actual, String message, float[] expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(double[] actual, double[] expected, String message) {
|
||||
void before(double[] actual, String message, double[] expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(Object[] actual, Object[] expected, String message) {
|
||||
void before(Object[] actual, String message, Object[] expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object[] actual, Object[] expected, String message) {
|
||||
void after(Object[] actual, String message, Object[] expected) {
|
||||
assertThat(actual).withFailMessage(message).containsExactly(expected);
|
||||
}
|
||||
}
|
||||
@@ -573,13 +649,13 @@ final class TestNGToAssertJRules {
|
||||
|
||||
static final class AssertEqualArraysIrrespectiveOfOrderWithMessage {
|
||||
@BeforeTemplate
|
||||
void before(Object[] actual, Object[] expected, String message) {
|
||||
void before(Object[] actual, String message, Object[] expected) {
|
||||
assertEqualsNoOrder(actual, expected, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object[] actual, Object[] expected, String message) {
|
||||
void after(Object[] actual, String message, Object[] expected) {
|
||||
assertThat(actual).withFailMessage(message).containsExactlyInAnyOrder(expected);
|
||||
}
|
||||
}
|
||||
@@ -601,13 +677,13 @@ final class TestNGToAssertJRules {
|
||||
|
||||
static final class AssertEqualIteratorIterationOrderWithMessage {
|
||||
@BeforeTemplate
|
||||
void before(Iterator<?> actual, Iterator<?> expected, String message) {
|
||||
void before(Iterator<?> actual, String message, Iterator<?> expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
<S, T extends S> void after(Iterator<S> actual, Iterator<T> expected, String message) {
|
||||
<S, T extends S> void after(Iterator<S> actual, String message, Iterator<T> expected) {
|
||||
// XXX: This is not `null`-safe.
|
||||
// XXX: The `ImmutableList.copyOf` should actually *not* be imported statically.
|
||||
assertThat(actual)
|
||||
@@ -639,18 +715,18 @@ final class TestNGToAssertJRules {
|
||||
|
||||
static final class AssertEqualIterableIterationOrderWithMessage {
|
||||
@BeforeTemplate
|
||||
void before(Iterable<?> actual, Iterable<?> expected, String message) {
|
||||
void before(Iterable<?> actual, String message, Iterable<?> expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(Collection<?> actual, Collection<?> expected, String message) {
|
||||
void before(Collection<?> actual, String message, Collection<?> expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
<S, T extends S> void after(Iterable<S> actual, Iterable<T> expected, String message) {
|
||||
<S, T extends S> void after(Iterable<S> actual, String message, Iterable<T> expected) {
|
||||
assertThat(actual).withFailMessage(message).containsExactlyElementsOf(expected);
|
||||
}
|
||||
}
|
||||
@@ -670,13 +746,13 @@ final class TestNGToAssertJRules {
|
||||
|
||||
static final class AssertEqualSetsWithMessage {
|
||||
@BeforeTemplate
|
||||
void before(Set<?> actual, Set<?> expected, String message) {
|
||||
void before(Set<?> actual, String message, Set<?> expected) {
|
||||
assertEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
<S, T extends S> void after(Set<S> actual, Set<T> expected, String message) {
|
||||
<S, T extends S> void after(Set<S> actual, String message, Set<T> expected) {
|
||||
assertThat(actual).withFailMessage(message).hasSameElementsAs(expected);
|
||||
}
|
||||
}
|
||||
@@ -751,68 +827,68 @@ final class TestNGToAssertJRules {
|
||||
|
||||
static final class AssertUnequalWithMessage {
|
||||
@BeforeTemplate
|
||||
void before(boolean actual, boolean expected, String message) {
|
||||
void before(boolean actual, String message, boolean expected) {
|
||||
assertNotEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(byte actual, byte expected, String message) {
|
||||
void before(byte actual, String message, byte expected) {
|
||||
assertNotEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(char actual, char expected, String message) {
|
||||
void before(char actual, String message, char expected) {
|
||||
assertNotEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(short actual, short expected, String message) {
|
||||
void before(short actual, String message, short expected) {
|
||||
assertNotEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(int actual, int expected, String message) {
|
||||
void before(int actual, String message, int expected) {
|
||||
assertNotEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(long actual, long expected, String message) {
|
||||
void before(long actual, String message, long expected) {
|
||||
assertNotEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(float actual, float expected, String message) {
|
||||
void before(float actual, String message, float expected) {
|
||||
assertNotEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(double actual, double expected, String message) {
|
||||
void before(double actual, String message, double expected) {
|
||||
assertNotEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(Object actual, Object expected, String message) {
|
||||
void before(Object actual, String message, Object expected) {
|
||||
assertNotEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(String actual, String expected, String message) {
|
||||
void before(String actual, String message, String expected) {
|
||||
assertNotEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(Set<?> actual, Set<?> expected, String message) {
|
||||
void before(Set<?> actual, String message, Set<?> expected) {
|
||||
assertNotEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before(Map<?, ?> actual, Map<?, ?> expected, String message) {
|
||||
void before(Map<?, ?> actual, String message, Map<?, ?> expected) {
|
||||
assertNotEquals(actual, expected, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(Object actual, Object expected, String message) {
|
||||
void after(Object actual, String message, Object expected) {
|
||||
assertThat(actual).withFailMessage(message).isNotEqualTo(expected);
|
||||
}
|
||||
}
|
||||
@@ -832,13 +908,13 @@ final class TestNGToAssertJRules {
|
||||
|
||||
static final class AssertUnequalFloatsWithDeltaWithMessage {
|
||||
@BeforeTemplate
|
||||
void before(float actual, float expected, float delta, String message) {
|
||||
void before(float actual, String message, float expected, float delta) {
|
||||
assertNotEquals(actual, expected, delta, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(float actual, float expected, float delta, String message) {
|
||||
void after(float actual, String message, float expected, float delta) {
|
||||
assertThat(actual).withFailMessage(message).isNotCloseTo(expected, offset(delta));
|
||||
}
|
||||
}
|
||||
@@ -858,13 +934,13 @@ final class TestNGToAssertJRules {
|
||||
|
||||
static final class AssertUnequalDoublesWithDeltaWithMessage {
|
||||
@BeforeTemplate
|
||||
void before(double actual, double expected, double delta, String message) {
|
||||
void before(double actual, String message, double expected, double delta) {
|
||||
assertNotEquals(actual, expected, delta, message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
void after(double actual, double expected, double delta, String message) {
|
||||
void after(double actual, String message, double expected, double delta) {
|
||||
assertThat(actual).withFailMessage(message).isNotCloseTo(expected, offset(delta));
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
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 CanonicalClassNameUsageTest {
|
||||
@Test
|
||||
void identification() {
|
||||
CompilationTestHelper.newInstance(CanonicalClassNameUsage.class, getClass())
|
||||
.setArgs(
|
||||
"--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
|
||||
"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED")
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static com.google.errorprone.matchers.Matchers.instanceMethod;",
|
||||
"",
|
||||
"import com.google.errorprone.VisitorState;",
|
||||
"import tech.picnic.errorprone.utils.MoreTypes;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m(VisitorState state) {",
|
||||
" String a = A.class.getName();",
|
||||
" String b = getClass().getName();",
|
||||
" A.class.getName().toString();",
|
||||
" System.out.println(A.class.getName());",
|
||||
" methodInUnnamedPackage(A.class.getName());",
|
||||
" instanceMethod().onExactClass(A.class.getCanonicalName());",
|
||||
" MoreTypes.type(A.class.getCanonicalName());",
|
||||
" MoreTypes.type(A.class.getCanonicalName() + \".SubType\");",
|
||||
" instanceMethod().onExactClass(new Object() {}.getClass().getName());",
|
||||
" instanceMethod().onExactClass(methodInUnnamedPackage(A.class.getName()));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" instanceMethod().onExactClass(A.class.getName());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" MoreTypes.type(A.class.getName());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" state.binaryNameFromClassname(A.class.getName() + \".SubType\");",
|
||||
" }",
|
||||
"",
|
||||
" String methodInUnnamedPackage(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
BugCheckerRefactoringTestHelper.newInstance(CanonicalClassNameUsage.class, getClass())
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import static com.google.errorprone.matchers.Matchers.instanceMethod;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"import tech.picnic.errorprone.utils.MoreTypes;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() {",
|
||||
" instanceMethod().onDescendantOfAny(A.class.getName(), BugPattern.LinkType.class.getName());",
|
||||
" MoreTypes.type(String.class.getName());",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import static com.google.errorprone.matchers.Matchers.instanceMethod;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"import tech.picnic.errorprone.utils.MoreTypes;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() {",
|
||||
" instanceMethod()",
|
||||
" .onDescendantOfAny(",
|
||||
" A.class.getCanonicalName(), BugPattern.LinkType.class.getCanonicalName());",
|
||||
" MoreTypes.type(String.class.getCanonicalName());",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class EmptyMonoZipTest {
|
||||
@Test
|
||||
void identification() {
|
||||
CompilationTestHelper.newInstance(EmptyMonoZip.class, getClass())
|
||||
.expectErrorMessage(
|
||||
"ARGUMENT",
|
||||
m ->
|
||||
m.contains(
|
||||
"Don't pass a `Mono<Void>` or `Mono.empty()` argument to `Mono#{zip,With}`"))
|
||||
.expectErrorMessage(
|
||||
"RECEIVER",
|
||||
m ->
|
||||
m.contains(
|
||||
"Invoking `Mono#zipWith` on `Mono#empty()` or a `Mono<Void>` is a no-op"))
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static reactor.core.publisher.Mono.zip;",
|
||||
"",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() {",
|
||||
" Flux.just(1).zip(Mono.empty(), Flux.just(2));",
|
||||
"",
|
||||
" Mono<Void> voidMono = Mono.empty();",
|
||||
" Mono<Integer> integerMono = Mono.empty();",
|
||||
"",
|
||||
" zip(Mono.just(1), Mono.just(2));",
|
||||
" Mono.zip(Mono.just(1), Mono.just(2));",
|
||||
" Mono.zip(Mono.just(1), Mono.just(2), Mono.just(3));",
|
||||
" Mono.zip(integerMono, integerMono);",
|
||||
"",
|
||||
" // BUG: Diagnostic matches: ARGUMENT",
|
||||
" zip(Mono.empty(), Mono.empty());",
|
||||
" // BUG: Diagnostic matches: ARGUMENT",
|
||||
" Mono.zip(Mono.empty(), Mono.empty());",
|
||||
" // BUG: Diagnostic matches: ARGUMENT",
|
||||
" Mono.zip(voidMono, Mono.just(1));",
|
||||
" // BUG: Diagnostic matches: ARGUMENT",
|
||||
" Mono.zip(voidMono, voidMono);",
|
||||
" // BUG: Diagnostic matches: ARGUMENT",
|
||||
" Mono.zip(Mono.just(1).then(), Mono.just(2));",
|
||||
" // BUG: Diagnostic matches: ARGUMENT",
|
||||
" Mono.zip(Mono.just(1), Mono.just(2), voidMono);",
|
||||
"",
|
||||
" Mono.just(1).zipWith(Mono.just(2));",
|
||||
" Mono.just(1).zipWith(integerMono);",
|
||||
" Mono.just(1).zipWith(integerMono, (a, b) -> a + b);",
|
||||
"",
|
||||
" // BUG: Diagnostic matches: ARGUMENT",
|
||||
" Mono.just(1).zipWith(Mono.empty());",
|
||||
" // BUG: Diagnostic matches: ARGUMENT",
|
||||
" Mono.just(1).zipWith(voidMono);",
|
||||
" // BUG: Diagnostic matches: RECEIVER",
|
||||
" Mono.empty().zipWith(Mono.just(1));",
|
||||
" // BUG: Diagnostic matches: RECEIVER",
|
||||
" voidMono.zipWith(Mono.just(1));",
|
||||
" }",
|
||||
"",
|
||||
" abstract class MyMono extends Mono<Object> {",
|
||||
" void m() {",
|
||||
" zip(Mono.just(1), Mono.just(2));",
|
||||
" // BUG: Diagnostic matches: ARGUMENT",
|
||||
" zip(Mono.empty(), Mono.empty());",
|
||||
"",
|
||||
" zipWith(Mono.just(1));",
|
||||
" // BUG: Diagnostic matches: ARGUMENT",
|
||||
" zipWith(Mono.empty());",
|
||||
" }",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
}
|
||||
@@ -75,6 +75,8 @@ final class ExplicitEnumOrderingTest {
|
||||
" Ordering.explicit(IsoEra.BCE, SOURCE, RetentionPolicy.CLASS);",
|
||||
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE, IsoEra.BCE",
|
||||
" Ordering.explicit(CLASS, RUNTIME, CE);",
|
||||
"",
|
||||
" Ordering.explicit(BCE, null, CE);",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
|
||||
@@ -65,6 +65,8 @@ final class NonStaticImportTest {
|
||||
"// BUG: Diagnostic contains:",
|
||||
"import static java.util.Optional.empty;",
|
||||
"import static pkg.A.WithMethodThatIsSelectivelyFlagged.list;",
|
||||
"// BUG: Diagnostic contains:",
|
||||
"import static reactor.core.publisher.Flux.just;",
|
||||
"",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
@@ -72,7 +74,7 @@ final class NonStaticImportTest {
|
||||
"import java.time.ZoneOffset;",
|
||||
"import java.util.Locale;",
|
||||
"import java.util.Map;",
|
||||
"import pkg.A.Wrapper.ZERO;",
|
||||
"import pkg.A.Wrapper.INSTANCE;",
|
||||
"",
|
||||
"class A {",
|
||||
" private Integer MIN_VALUE = 12;",
|
||||
@@ -90,9 +92,10 @@ final class NonStaticImportTest {
|
||||
" Locale english = ENGLISH;",
|
||||
" Locale root = ROOT;",
|
||||
" empty();",
|
||||
" just();",
|
||||
"",
|
||||
" list();",
|
||||
" new ZERO();",
|
||||
" new INSTANCE();",
|
||||
" }",
|
||||
"",
|
||||
" static final class WithMethodThatIsSelectivelyFlagged {",
|
||||
@@ -102,7 +105,7 @@ final class NonStaticImportTest {
|
||||
" }",
|
||||
"",
|
||||
" static final class Wrapper {",
|
||||
" static final class ZERO {}",
|
||||
" static final class INSTANCE {}",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
|
||||
@@ -16,6 +16,7 @@ final class RequestMappingAnnotationTest {
|
||||
"import java.util.Locale;",
|
||||
"import java.util.TimeZone;",
|
||||
"import org.springframework.http.HttpMethod;",
|
||||
"import org.springframework.security.core.annotation.CurrentSecurityContext;",
|
||||
"import org.springframework.ui.Model;",
|
||||
"import org.springframework.validation.BindingResult;",
|
||||
"import org.springframework.web.bind.annotation.DeleteMapping;",
|
||||
@@ -63,6 +64,10 @@ final class RequestMappingAnnotationTest {
|
||||
" A properRequestPart(@RequestPart String part);",
|
||||
"",
|
||||
" @RequestMapping",
|
||||
" A properCurrentSecurityContext(",
|
||||
" @CurrentSecurityContext(expression = \"authentication.name\") String user);",
|
||||
"",
|
||||
" @RequestMapping",
|
||||
" A properInputStream(InputStream input);",
|
||||
"",
|
||||
" @RequestMapping",
|
||||
|
||||
@@ -40,12 +40,14 @@ final class StaticImportTest {
|
||||
"import static java.util.function.Predicate.not;",
|
||||
"import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;",
|
||||
"",
|
||||
"import com.fasterxml.jackson.annotation.JsonCreator;",
|
||||
"import com.google.common.base.Predicates;",
|
||||
"import com.google.common.collect.ImmutableMap;",
|
||||
"import com.google.common.collect.ImmutableMultiset;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import com.google.errorprone.refaster.ImportPolicy;",
|
||||
"import com.google.errorprone.refaster.annotation.UseImportPolicy;",
|
||||
"import com.mongodb.client.model.Filters;",
|
||||
"import java.nio.charset.StandardCharsets;",
|
||||
"import java.time.ZoneOffset;",
|
||||
"import java.util.Optional;",
|
||||
@@ -98,6 +100,8 @@ final class StaticImportTest {
|
||||
" // BUG: Diagnostic contains:",
|
||||
" MediaType t2 = MediaType.APPLICATION_JSON;",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Filters.empty();",
|
||||
" Optional.empty();",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
@@ -106,6 +110,12 @@ final class StaticImportTest {
|
||||
" }",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @JsonCreator(mode = JsonCreator.Mode.DELEGATING)",
|
||||
" private static A jsonCreator(int a) {",
|
||||
" return new A();",
|
||||
" }",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @UseImportPolicy(ImportPolicy.IMPORT_TOP_LEVEL)",
|
||||
" void refasterAfterTemplate() {}",
|
||||
"",
|
||||
@@ -121,6 +131,7 @@ final class StaticImportTest {
|
||||
"A.java",
|
||||
"import static java.util.function.Predicate.not;",
|
||||
"",
|
||||
"import com.fasterxml.jackson.annotation.JsonCreator;",
|
||||
"import com.google.common.base.Predicates;",
|
||||
"import com.google.common.collect.ImmutableMap;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
@@ -177,6 +188,11 @@ final class StaticImportTest {
|
||||
" @DateTimeFormat(iso = ISO.DATE_TIME) String dateTime,",
|
||||
" @DateTimeFormat(iso = ISO.TIME) String time) {}",
|
||||
"",
|
||||
" @JsonCreator(mode = JsonCreator.Mode.DELEGATING)",
|
||||
" private static A jsonCreator(int a) {",
|
||||
" return new A();",
|
||||
" }",
|
||||
"",
|
||||
" @BugPattern(",
|
||||
" summary = \"\",",
|
||||
" linkType = BugPattern.LinkType.NONE,",
|
||||
@@ -189,6 +205,7 @@ final class StaticImportTest {
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import static com.fasterxml.jackson.annotation.JsonCreator.Mode.DELEGATING;",
|
||||
"import static com.google.common.collect.ImmutableMap.toImmutableMap;",
|
||||
"import static com.google.common.collect.ImmutableSet.toImmutableSet;",
|
||||
"import static com.google.errorprone.BugPattern.LinkType.NONE;",
|
||||
@@ -208,6 +225,7 @@ final class StaticImportTest {
|
||||
"import static org.springframework.http.MediaType.APPLICATION_XHTML_XML;",
|
||||
"import static org.springframework.http.MediaType.TEXT_HTML;",
|
||||
"",
|
||||
"import com.fasterxml.jackson.annotation.JsonCreator;",
|
||||
"import com.google.common.base.Predicates;",
|
||||
"import com.google.common.collect.ImmutableMap;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
@@ -261,6 +279,11 @@ final class StaticImportTest {
|
||||
" @DateTimeFormat(iso = DATE_TIME) String dateTime,",
|
||||
" @DateTimeFormat(iso = TIME) String time) {}",
|
||||
"",
|
||||
" @JsonCreator(mode = DELEGATING)",
|
||||
" private static A jsonCreator(int a) {",
|
||||
" return new A();",
|
||||
" }",
|
||||
"",
|
||||
" @BugPattern(summary = \"\", linkType = NONE, severity = SUGGESTION, tags = SIMPLIFICATION)",
|
||||
" static final class TestBugPattern {}",
|
||||
"",
|
||||
|
||||
@@ -42,6 +42,7 @@ final class RefasterRulesTest {
|
||||
DoubleStreamRules.class,
|
||||
EqualityRules.class,
|
||||
FileRules.class,
|
||||
InputStreamRules.class,
|
||||
ImmutableListRules.class,
|
||||
ImmutableListMultimapRules.class,
|
||||
ImmutableMapRules.class,
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.openrewrite.java.Assertions.java;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import java.io.IOException;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openrewrite.java.JavaParser;
|
||||
import org.openrewrite.test.RecipeSpec;
|
||||
import org.openrewrite.test.RewriteTest;
|
||||
|
||||
// XXX: This class currently validates the OpenRewrite recipe generation by applying a single
|
||||
// recipe. Generalize this setup to cover all generated recipes (for _all_ Refaster rule
|
||||
// collections), ideally by reusing the `RefasterRulesTest` test resources. (This may introduce
|
||||
// additional hurdles, as OpenRewrite removes obsolete imports, while Refaster doesn't.)
|
||||
final class StringRulesRecipesTest implements RewriteTest {
|
||||
@Override
|
||||
public void defaults(RecipeSpec spec) {
|
||||
spec.recipe(new StringRulesRecipes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void stringValueOf() {
|
||||
// XXX: Use text blocks once supported.
|
||||
rewriteRun(
|
||||
java(
|
||||
"import java.util.Objects;\n"
|
||||
+ '\n'
|
||||
+ "class Test {\n"
|
||||
+ " String test(Object object) {\n"
|
||||
+ " return Objects.toString(object);\n"
|
||||
+ " }\n"
|
||||
+ '}',
|
||||
"class Test {\n"
|
||||
+ " String test(Object object) {\n"
|
||||
+ " return String.valueOf(object);\n"
|
||||
+ " }\n"
|
||||
+ '}'));
|
||||
}
|
||||
|
||||
@Disabled("Not all rules are currently supported")
|
||||
@Test
|
||||
void allRules() throws IOException {
|
||||
rewriteRun(
|
||||
spec ->
|
||||
spec.parser(JavaParser.fromJavaVersion().classpath("guava", "refaster-test-support")),
|
||||
java(
|
||||
loadResource("StringRulesTestInput.java"), loadResource("StringRulesTestOutput.java")));
|
||||
}
|
||||
|
||||
private String loadResource(String resource) throws IOException {
|
||||
return Resources.toString(Resources.getResource(getClass(), resource), UTF_8);
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,9 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
|
||||
ImmutableSet.of(5).size() > 0,
|
||||
ImmutableSet.of(6).size() >= 1,
|
||||
Iterables.isEmpty(ImmutableSet.of(7)),
|
||||
ImmutableSet.of(8).asList().isEmpty());
|
||||
ImmutableSet.of(8).stream().findAny().isEmpty(),
|
||||
ImmutableSet.of(9).stream().findFirst().isEmpty(),
|
||||
ImmutableSet.of(10).asList().isEmpty());
|
||||
}
|
||||
|
||||
ImmutableSet<Integer> testCollectionSize() {
|
||||
|
||||
@@ -29,7 +29,9 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
|
||||
!ImmutableSet.of(5).isEmpty(),
|
||||
!ImmutableSet.of(6).isEmpty(),
|
||||
ImmutableSet.of(7).isEmpty(),
|
||||
ImmutableSet.of(8).isEmpty());
|
||||
ImmutableSet.of(8).isEmpty(),
|
||||
ImmutableSet.of(9).isEmpty(),
|
||||
ImmutableSet.of(10).isEmpty());
|
||||
}
|
||||
|
||||
ImmutableSet<Integer> testCollectionSize() {
|
||||
|
||||
@@ -3,6 +3,8 @@ package tech.picnic.errorprone.refasterrules;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.Comparator.reverseOrder;
|
||||
import static java.util.function.Function.identity;
|
||||
import static java.util.stream.Collectors.maxBy;
|
||||
import static java.util.stream.Collectors.minBy;
|
||||
|
||||
import com.google.common.collect.Comparators;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -10,7 +12,9 @@ import com.google.common.collect.ImmutableSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
@@ -161,4 +165,12 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
BinaryOperator<String> testComparatorsMax() {
|
||||
return BinaryOperator.maxBy(naturalOrder());
|
||||
}
|
||||
|
||||
Collector<Integer, ?, Optional<Integer>> testMinByNaturalOrder() {
|
||||
return maxBy(reverseOrder());
|
||||
}
|
||||
|
||||
Collector<Integer, ?, Optional<Integer>> testMaxByNaturalOrder() {
|
||||
return minBy(reverseOrder());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package tech.picnic.errorprone.refasterrules;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.Comparator.reverseOrder;
|
||||
import static java.util.function.Function.identity;
|
||||
import static java.util.stream.Collectors.maxBy;
|
||||
import static java.util.stream.Collectors.minBy;
|
||||
|
||||
import com.google.common.collect.Comparators;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -10,7 +12,9 @@ import com.google.common.collect.ImmutableSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
@@ -151,4 +155,12 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
BinaryOperator<String> testComparatorsMax() {
|
||||
return Comparators::max;
|
||||
}
|
||||
|
||||
Collector<Integer, ?, Optional<Integer>> testMinByNaturalOrder() {
|
||||
return minBy(naturalOrder());
|
||||
}
|
||||
|
||||
Collector<Integer, ?, Optional<Integer>> testMaxByNaturalOrder() {
|
||||
return maxBy(naturalOrder());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,15 +69,14 @@ final class EqualityRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return not(v -> v.isEmpty());
|
||||
}
|
||||
|
||||
boolean testEqualsLhsNullable() {
|
||||
return Optional.ofNullable("foo").equals(Optional.of("bar"));
|
||||
ImmutableSet<Boolean> testEquals() {
|
||||
return ImmutableSet.of(
|
||||
Optional.of("foo").equals(Optional.of("bar")),
|
||||
Optional.of("baz").equals(Optional.ofNullable("qux")),
|
||||
Optional.ofNullable("quux").equals(Optional.of("quuz")));
|
||||
}
|
||||
|
||||
boolean testEqualsRhsNullable() {
|
||||
return Optional.of("foo").equals(Optional.ofNullable("bar"));
|
||||
}
|
||||
|
||||
boolean testEqualsLhsAndRhsNullable() {
|
||||
boolean testObjectsEquals() {
|
||||
return Optional.ofNullable("foo").equals(Optional.ofNullable("bar"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,15 +69,11 @@ final class EqualityRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return v -> !v.isEmpty();
|
||||
}
|
||||
|
||||
boolean testEqualsLhsNullable() {
|
||||
return "bar".equals("foo");
|
||||
ImmutableSet<Boolean> testEquals() {
|
||||
return ImmutableSet.of("foo".equals("bar"), "baz".equals("qux"), "quuz".equals("quux"));
|
||||
}
|
||||
|
||||
boolean testEqualsRhsNullable() {
|
||||
return "foo".equals("bar");
|
||||
}
|
||||
|
||||
boolean testEqualsLhsAndRhsNullable() {
|
||||
boolean testObjectsEquals() {
|
||||
return Objects.equals("foo", "bar");
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user