mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 08:11:25 +00:00
Compare commits
191 Commits
sschroever
...
sschroever
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5f825b9ff | ||
|
|
d85f2dd6a8 | ||
|
|
2b7a555c05 | ||
|
|
ca85533b0d | ||
|
|
e192dacdfb | ||
|
|
8418652de0 | ||
|
|
1d8ac35660 | ||
|
|
913cd2ee3a | ||
|
|
6adaa6c4f6 | ||
|
|
08dbb8c298 | ||
|
|
e7bc0e113c | ||
|
|
6d2c926b0e | ||
|
|
60e15cb569 | ||
|
|
22f61d3032 | ||
|
|
5d2a726aec | ||
|
|
192322a982 | ||
|
|
ddf5d803bd | ||
|
|
02fb6d468a | ||
|
|
e7d50c247d | ||
|
|
85cfb4b4b7 | ||
|
|
0684c577ac | ||
|
|
32778edc74 | ||
|
|
1e6780afc1 | ||
|
|
ef4e004141 | ||
|
|
72c5a42feb | ||
|
|
271e01a02c | ||
|
|
d47549d68f | ||
|
|
01687c7f3e | ||
|
|
85cb7ffdb1 | ||
|
|
0367037f0a | ||
|
|
6669a2e1ec | ||
|
|
eb36c1e493 | ||
|
|
8f5faf0f6a | ||
|
|
5fad0ea04f | ||
|
|
4558f8affb | ||
|
|
7be27614da | ||
|
|
7118d6bf03 | ||
|
|
eb84ddf500 | ||
|
|
032109756d | ||
|
|
9e230302e9 | ||
|
|
4708fec201 | ||
|
|
d102d6acbb | ||
|
|
bc67883579 | ||
|
|
069d6ff2f4 | ||
|
|
6fbf4d81f0 | ||
|
|
3d51acd613 | ||
|
|
d2fb576ecc | ||
|
|
d658901231 | ||
|
|
76d1ca7bdf | ||
|
|
341977b227 | ||
|
|
75872dc2f5 | ||
|
|
b609537a52 | ||
|
|
1469d1e157 | ||
|
|
111b7d04f2 | ||
|
|
9e297df1c7 | ||
|
|
7babb48751 | ||
|
|
dfaffacbb5 | ||
|
|
769779cf21 | ||
|
|
9d8a5af44a | ||
|
|
8a84acca7b | ||
|
|
b551f90d38 | ||
|
|
789a9cc0aa | ||
|
|
13e35338af | ||
|
|
281a003dd7 | ||
|
|
e40df7e1b8 | ||
|
|
bb2b1e6034 | ||
|
|
f8cac19330 | ||
|
|
52fe79c343 | ||
|
|
0b696b95b6 | ||
|
|
63bc903f83 | ||
|
|
b166d0daea | ||
|
|
6914dae822 | ||
|
|
c5fb53d725 | ||
|
|
d36d20da08 | ||
|
|
502281f4d3 | ||
|
|
daa4f19c57 | ||
|
|
02f726f43c | ||
|
|
b9e8186159 | ||
|
|
85baadd5df | ||
|
|
ab871ec9bb | ||
|
|
753928f4da | ||
|
|
fe84bada33 | ||
|
|
5b8d6ed9c5 | ||
|
|
2ad2fdfb0f | ||
|
|
a10558a044 | ||
|
|
7316d05c22 | ||
|
|
3177db55b8 | ||
|
|
a6a63f9553 | ||
|
|
df0eb9ee2f | ||
|
|
e3cda3ea49 | ||
|
|
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 |
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
|
||||
|
||||
|
||||
8
.github/release.yml
vendored
8
.github/release.yml
vendored
@@ -3,16 +3,16 @@ changelog:
|
||||
labels:
|
||||
- "ignore-changelog"
|
||||
categories:
|
||||
- title: ":warning: Update considerations and deprecations"
|
||||
labels:
|
||||
- "breaking change"
|
||||
- "deprecation"
|
||||
- title: ":rocket: New Error Prone checks and Refaster rules"
|
||||
labels:
|
||||
- "new feature"
|
||||
- title: ":sparkles: Improvements"
|
||||
labels:
|
||||
- "improvement"
|
||||
- title: ":warning: Update considerations and deprecations"
|
||||
labels:
|
||||
- "breaking change"
|
||||
- "deprecation"
|
||||
- title: ":bug: Bug fixes"
|
||||
labels:
|
||||
- "bug"
|
||||
|
||||
19
.github/workflows/build.yml
vendored
19
.github/workflows/build.yml
vendored
@@ -10,28 +10,37 @@ 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@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
|
||||
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 and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@fa2c7e4517ed008b1f73e7e0195a9eecf5582cd4 # v1.11.0
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
with:
|
||||
java-version: ${{ matrix.jdk }}
|
||||
java-distribution: ${{ matrix.distribution }}
|
||||
|
||||
21
.github/workflows/codeql.yml
vendored
21
.github/workflows/codeql.yml
vendored
@@ -21,20 +21,31 @@ jobs:
|
||||
security-events: write
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@fa2c7e4517ed008b1f73e7e0195a9eecf5582cd4 # v1.11.0
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
|
||||
with:
|
||||
java-version: 17.0.8
|
||||
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.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.6
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12
|
||||
uses: github/codeql-action/init@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Perform minimal build
|
||||
if: matrix.language == 'java'
|
||||
run: mvn -T1C clean package -DskipTests -Dverification.skip
|
||||
- name: Perform CodeQL analysis
|
||||
uses: github/codeql-action/analyze@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12
|
||||
uses: github/codeql-action/analyze@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
|
||||
with:
|
||||
category: /language:${{ matrix.language }}
|
||||
|
||||
44
.github/workflows/deploy-website.yml
vendored
44
.github/workflows/deploy-website.yml
vendored
@@ -11,16 +11,43 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
|
||||
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
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: ruby/setup-ruby@360dc864d5da99d54fcb8e9148c14a84b90d3e88 # v1.165.1
|
||||
- uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0
|
||||
with:
|
||||
working-directory: ./website
|
||||
bundler-cache: true
|
||||
- name: Configure Github Pages
|
||||
uses: actions/configure-pages@1f0c5cde4bc74cd7e1254d0cb4de8d49e9068c7d # v4.0.0
|
||||
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
|
||||
- name: Generate documentation
|
||||
run: ./generate-docs.sh
|
||||
- name: Build website with Jekyll
|
||||
@@ -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@0252fc4ba7626f0298f0cf00902a25c6afc77fa8 # v3.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@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.github.com:443
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@87c3283f01cd6fe19a0ab93a23b2f6fcba5a8e42 # v4.0.3
|
||||
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
|
||||
|
||||
19
.github/workflows/openssf-scorecard.yml
vendored
19
.github/workflows/openssf-scorecard.yml
vendored
@@ -20,8 +20,23 @@ jobs:
|
||||
id-token: write
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
|
||||
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
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run OpenSSF Scorecard analysis
|
||||
@@ -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@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12
|
||||
uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
14
.github/workflows/pitest-analyze-pr.yml
vendored
14
.github/workflows/pitest-analyze-pr.yml
vendored
@@ -11,11 +11,19 @@ jobs:
|
||||
analyze-pr:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
|
||||
with:
|
||||
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@fa2c7e4517ed008b1f73e7e0195a9eecf5582cd4 # v1.11.0
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
with:
|
||||
checkout-fetch-depth: 2
|
||||
java-version: 17.0.8
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.6
|
||||
- name: Run Pitest
|
||||
@@ -28,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@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
with:
|
||||
name: pitest-reports
|
||||
path: ./target/pit-reports-ci
|
||||
|
||||
17
.github/workflows/pitest-update-pr.yml
vendored
17
.github/workflows/pitest-update-pr.yml
vendored
@@ -19,14 +19,23 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@fa2c7e4517ed008b1f73e7e0195a9eecf5582cd4 # v1.11.0
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
|
||||
with:
|
||||
java-version: 17.0.8
|
||||
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.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@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
name: pitest-reports
|
||||
|
||||
22
.github/workflows/run-integration-tests.yml
vendored
22
.github/workflows/run-integration-tests.yml
vendored
@@ -18,22 +18,34 @@ jobs:
|
||||
github.event.issue.pull_request && contains(github.event.comment.body, '/integration-test')
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Harden-Runner
|
||||
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
|
||||
with:
|
||||
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@fa2c7e4517ed008b1f73e7e0195a9eecf5582cd4 # v1.11.0
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
with:
|
||||
checkout-ref: "refs/pull/${{ github.event.issue.number }}/head"
|
||||
java-version: 17.0.8
|
||||
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@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
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
|
||||
|
||||
18
.github/workflows/sonarcloud.yml
vendored
18
.github/workflows/sonarcloud.yml
vendored
@@ -13,16 +13,28 @@ 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.pull_request.head.repo.full_name == github.repository
|
||||
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: Install Harden-Runner
|
||||
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
|
||||
with:
|
||||
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@fa2c7e4517ed008b1f73e7e0195a9eecf5582cd4 # v1.11.0
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
with:
|
||||
checkout-fetch-depth: 0
|
||||
java-version: 17.0.8
|
||||
java-version: 17.0.10
|
||||
java-distribution: temurin
|
||||
maven-version: 3.9.6
|
||||
- name: Create missing `test` directory
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
.DS_Store
|
||||
.factorypath
|
||||
.idea
|
||||
!.idea/icon.svg
|
||||
.project
|
||||
.settings
|
||||
target
|
||||
|
||||
65
.idea/icon.svg
generated
Normal file
65
.idea/icon.svg
generated
Normal file
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 1259 1199" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1,0,0,1,-624.154,-988.431)">
|
||||
<g transform="matrix(1,0,0,1,-70.1122,-35.0561)">
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1227.03,1988.79C1237.78,2070.45 1225.83,2190.1 1192.24,2194.53C1158.65,2198.95 1116.14,2086.46 1105.39,2004.81C1094.64,1923.16 1128.44,1902.11 1153.32,1898.84C1178.18,1895.56 1216.28,1907.14 1227.03,1988.79Z" style="fill:rgb(219,220,211);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1151.08,1881.86C1134.93,1883.99 1114.77,1892.69 1101.6,1913.17C1088.42,1933.64 1082.71,1963.73 1088.42,2007.04C1094.04,2049.75 1107.59,2099.16 1124.51,2138.68C1132.97,2158.45 1142.15,2175.68 1152.59,2188.86C1163.04,2202.05 1176.31,2213.89 1194.48,2211.5C1212.65,2209.11 1222.39,2194.23 1229.07,2178.8C1235.75,2163.36 1240.15,2144.34 1243.21,2123.05C1249.32,2080.5 1249.63,2029.27 1244,1986.56C1238.3,1943.24 1225,1915.66 1206.98,1899.29C1188.95,1882.93 1167.22,1879.74 1151.08,1881.86ZM1155.55,1915.81C1164.27,1914.66 1174.03,1915.62 1183.96,1924.64C1193.89,1933.66 1205.01,1952.69 1210.06,1991.03C1215.18,2029.97 1214.89,2079.4 1209.32,2118.19C1206.53,2137.58 1202.32,2154.4 1197.65,2165.2C1194.14,2173.29 1190.82,2176.3 1189.96,2177.22C1188.89,2176.55 1184.91,2174.51 1179.43,2167.6C1172.12,2158.38 1163.7,2143.22 1155.99,2125.21C1140.57,2089.18 1127.49,2041.51 1122.36,2002.57C1117.32,1964.24 1123.13,1942.97 1130.39,1931.69C1137.65,1920.42 1146.82,1916.96 1155.55,1915.81Z" style="fill:rgb(26,26,26);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1516.33,1963.1C1466.19,1897.75 1427.4,1906.77 1407.5,1922.05C1387.59,1937.32 1368.84,1972.45 1418.98,2037.8C1431.75,2054.44 1447.26,2071.84 1463.69,2088.19C1495.18,2119.52 1534.33,2139.39 1582.98,2126.14C1606.4,2119.76 1622.19,2110.46 1623.75,2098.64C1625.79,2083.16 1603,2065.78 1569.69,2050.47C1554.75,2019.83 1535.59,1988.2 1516.33,1963.1Z" style="fill:rgb(219,220,211);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1397.07,1908.46C1409.99,1898.55 1430.41,1890.44 1454.2,1895.61C1478,1900.77 1503.31,1918 1529.91,1952.67C1548.92,1977.44 1567.3,2007.65 1582.28,2037.56C1597.47,2044.87 1610.74,2052.64 1621.09,2061.47C1632.68,2071.35 1642.93,2084.12 1640.73,2100.88C1639.05,2113.64 1630.31,2122.66 1620.9,2128.78C1611.49,2134.9 1600.29,2139.17 1587.48,2142.66C1532.39,2157.66 1485.57,2134.11 1451.61,2100.32C1434.7,2083.49 1418.73,2065.6 1405.39,2048.22C1378.79,2013.56 1368.69,1984.64 1369.86,1960.32C1371.04,1936 1384.15,1918.38 1397.07,1908.46ZM1417.92,1935.63C1410.94,1940.99 1404.71,1948.57 1404.07,1961.97C1403.43,1975.37 1409.02,1996.69 1432.56,2027.38C1444.76,2043.27 1459.82,2060.18 1475.77,2076.05C1504.8,2104.93 1536.26,2121.12 1578.48,2109.62C1589.1,2106.73 1597.5,2103.16 1602.23,2100.08C1605.14,2098.18 1606.16,2096.97 1606.54,2096.46C1606.07,2095.66 1604.57,2092.39 1598.86,2087.52C1591.24,2081.02 1578.31,2073.28 1562.54,2066.03L1556.98,2063.47L1554.29,2057.97C1539.86,2028.35 1521.12,1997.46 1502.75,1973.52C1479.2,1942.84 1460.05,1931.91 1446.94,1929.07C1433.84,1926.23 1424.9,1930.27 1417.92,1935.63Z" style="fill:rgb(26,26,26);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M917.121,1633.14C845.801,1674.32 730.68,1709.07 713.738,1679.73C696.797,1650.39 784.453,1568.07 855.777,1526.89C927.102,1485.71 959.48,1508.89 972.02,1530.62C984.562,1552.34 988.445,1591.97 917.121,1633.14Z" style="fill:rgb(219,220,211);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M986.848,1522.06C978.707,1507.95 962.949,1492.66 938.992,1488.32C915.031,1483.98 885.055,1490.22 847.219,1512.07C809.906,1533.61 769.453,1565.03 739.41,1595.79C724.391,1611.17 711.977,1626.24 703.797,1640.94C695.617,1655.64 689.746,1672.42 698.914,1688.29C708.078,1704.17 725.547,1707.48 742.363,1707.74C759.184,1708.01 778.445,1704.79 799.273,1699.47C840.934,1688.83 888.375,1669.51 925.684,1647.97C963.52,1626.12 983.91,1603.29 992.137,1580.37C1000.36,1557.45 994.988,1536.16 986.848,1522.06ZM957.195,1539.18C961.594,1546.79 964.438,1556.18 959.906,1568.8C955.379,1581.43 942.047,1598.98 908.562,1618.32C874.551,1637.96 828.77,1656.6 790.801,1666.3C771.816,1671.14 754.664,1673.69 742.902,1673.5C734.082,1673.37 730.035,1671.45 728.859,1671C729.062,1669.76 729.426,1665.3 733.715,1657.59C739.434,1647.31 750.215,1633.73 763.906,1619.72C791.285,1591.68 830.324,1561.36 864.336,1541.72C897.824,1522.38 919.695,1519.62 932.895,1522.01C946.09,1524.4 952.797,1531.56 957.195,1539.18Z" style="fill:rgb(26,26,26);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1791.57,1526.89C1862.89,1568.07 1950.54,1650.39 1933.61,1679.74C1916.66,1709.08 1801.54,1674.33 1730.22,1633.15C1658.9,1591.97 1662.78,1552.34 1675.32,1530.62C1687.86,1508.89 1720.24,1485.72 1791.57,1526.89Z" style="fill:rgb(219,220,211);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1660.5,1522.06C1652.35,1536.16 1646.98,1557.45 1655.21,1580.37C1663.43,1603.29 1683.82,1626.13 1721.66,1647.97C1758.97,1669.52 1806.41,1688.84 1848.07,1699.48C1868.9,1704.79 1888.16,1708.01 1904.98,1707.75C1921.79,1707.48 1939.27,1704.17 1948.43,1688.3C1957.59,1672.42 1951.73,1655.64 1943.55,1640.94C1935.37,1626.25 1922.95,1611.17 1907.93,1595.79C1877.89,1565.04 1837.43,1533.61 1800.12,1512.07C1762.29,1490.22 1732.31,1483.98 1708.35,1488.32C1684.39,1492.66 1668.64,1507.95 1660.5,1522.06ZM1690.15,1539.18C1694.55,1531.56 1701.25,1524.4 1714.45,1522.02C1727.64,1519.62 1749.52,1522.39 1783,1541.72C1817.02,1561.36 1856.06,1591.68 1883.44,1619.72C1897.12,1633.73 1907.91,1647.32 1913.63,1657.59C1917.92,1665.3 1918.28,1669.77 1918.48,1671.01C1917.31,1671.45 1913.26,1673.37 1904.44,1673.51C1892.68,1673.69 1875.52,1671.15 1856.54,1666.3C1818.57,1656.61 1772.79,1637.96 1738.78,1618.32C1705.29,1598.99 1691.97,1581.43 1687.43,1568.81C1682.91,1556.18 1685.75,1546.8 1690.15,1539.18Z" style="fill:rgb(26,26,26);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1322.81,1013.67C1058.16,1014.17 843.293,1229.45 843.297,1494.11L843.223,1862.19C843.223,1955.32 919.055,2031.16 1012.19,2031.16C1054.55,2031.16 1093.39,2015.51 1123.09,1989.7C1169.54,2049.44 1242.17,2087.79 1323.7,2087.79C1405.22,2087.79 1477.84,2049.45 1524.28,1989.73C1553.98,2015.52 1592.81,2031.16 1635.15,2031.16C1728.29,2031.16 1804.12,1955.32 1804.12,1862.19L1804.12,1494.1C1804.12,1229.09 1588.7,1013.67 1323.69,1013.67L1322.84,1013.67L1322.81,1013.67ZM1322.92,1068.46L1323.69,1068.46C1559.09,1068.46 1749.33,1258.7 1749.33,1494.11L1749.33,1862.19C1749.33,1925.92 1698.88,1976.37 1635.15,1976.37C1596.91,1976.37 1563.67,1958.03 1542.94,1929.68L1517.91,1895.48L1497,1932.34C1462.85,1992.53 1398.48,2033 1323.7,2033C1248.9,2033 1184.52,1992.51 1150.38,1932.3L1129.45,1895.41L1104.43,1929.65C1083.69,1958.02 1050.44,1976.37 1012.19,1976.37C948.461,1976.37 898.016,1925.93 898.012,1862.2L898.086,1494.11C898.086,1259.03 1087.84,1068.92 1322.92,1068.47L1322.92,1068.46Z" style="fill:rgb(26,26,26);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1322.86,1041.07C1072.99,1041.54 870.684,1244.23 870.688,1494.11L870.648,1862.19C870.648,1940.62 933.789,2003.77 1012.22,2003.77C1073.65,2003.77 1125.69,1965.03 1145.37,1910.56C1201.73,1934.69 1262.41,1947.14 1323.72,1947.14C1385.02,1947.14 1445.69,1934.69 1502.04,1910.57C1521.72,1965.04 1573.76,2003.77 1635.19,2003.77C1713.62,2003.77 1776.76,1940.62 1776.76,1862.19L1776.76,1494.11C1776.76,1243.9 1573.93,1041.07 1323.72,1041.07L1322.86,1041.07Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1323.7,1494.1C1449.2,1494.1 1550.24,1595.14 1550.24,1720.64L1550.24,1833.86C1550.24,1959.36 1449.2,2060.4 1323.7,2060.4C1198.2,2060.4 1097.16,1959.36 1097.16,1833.86L1097.16,1720.64C1097.16,1595.14 1198.2,1494.1 1323.7,1494.1Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1322.86,1041.07C1262.14,1041.18 1204.25,1053.27 1151.36,1075.05C1316.07,1142.87 1432.03,1304.93 1432.03,1494.11L1432.03,1811.95C1432.03,2003.63 1209.62,2024.85 1126.54,1945.82C1177.52,2034.1 1254.55,2060.4 1323.7,2060.4C1408.4,2060.4 1481.95,2014.37 1520.82,1945.86C1546.53,1981.01 1588.08,2003.77 1635.15,2003.77C1713.58,2003.77 1776.72,1940.62 1776.72,1862.19L1776.72,1494.11C1776.72,1243.9 1573.89,1041.07 1323.68,1041.07L1322.86,1041.07Z" style="fill:rgb(219,220,211);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1322.86,1041.07C1304.33,1041.1 1286.06,1042.28 1268.11,1044.48C1492.04,1071.93 1665.46,1262.75 1665.46,1494.11L1665.46,1862.19C1665.46,1920.85 1630.14,1970.94 1579.54,1992.48C1596.59,1999.74 1615.38,2003.77 1635.15,2003.77C1713.58,2003.77 1776.72,1940.62 1776.72,1862.19L1776.72,1494.11C1776.72,1243.9 1573.89,1041.07 1323.68,1041.07L1322.86,1041.07Z" style="fill:rgb(189,191,175);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1322.85,1034.22C1069.29,1034.69 863.84,1240.54 863.84,1494.11L863.766,1862.19C863.766,1944.3 930.078,2010.61 1012.19,2010.61C1057.88,2010.61 1098.53,1989.94 1125.71,1957.58C1166.88,2023.5 1240.02,2067.25 1323.7,2067.25C1407.36,2067.25 1480.48,2023.52 1521.66,1957.62C1548.84,1989.96 1589.47,2010.61 1635.15,2010.61C1717.25,2010.61 1783.57,1944.3 1783.57,1862.19L1783.57,1494.11C1783.57,1240.2 1577.59,1034.21 1323.68,1034.22L1322.85,1034.22ZM1322.86,1047.92L1323.68,1047.92C1570.19,1047.92 1769.87,1247.6 1769.87,1494.11L1769.87,1862.19C1769.87,1936.95 1709.91,1996.92 1635.15,1996.92C1590.29,1996.92 1550.82,1975.26 1526.36,1941.82L1520.1,1933.27L1514.87,1942.48C1477.18,2008.91 1405.92,2053.55 1323.7,2053.55C1241.46,2053.55 1170.19,2008.89 1132.5,1942.44L1127.27,1933.21L1121.02,1941.77C1096.56,1975.24 1057.07,1996.92 1012.19,1996.92C937.43,1996.92 877.469,1936.95 877.465,1862.19L877.539,1494.11C877.539,1247.94 1076.7,1048.39 1322.86,1047.92Z" style="fill:rgb(26,26,26);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1576.29,1470.72C1576.29,1481.36 1568.75,1491.56 1555.31,1499.08C1541.88,1506.6 1523.67,1510.83 1504.68,1510.83C1465.12,1510.83 1433.06,1492.87 1433.06,1470.72C1433.06,1448.57 1465.12,1430.62 1504.68,1430.62C1523.67,1430.62 1541.88,1434.84 1555.31,1442.36C1568.75,1449.89 1576.29,1460.09 1576.29,1470.72Z" style="fill:rgb(255,155,173);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1214.28,1470.72C1214.28,1481.36 1206.73,1491.56 1193.31,1499.08C1179.87,1506.6 1161.66,1510.83 1142.66,1510.83C1103.11,1510.83 1071.05,1492.87 1071.05,1470.72C1071.05,1448.57 1103.11,1430.62 1142.66,1430.62C1161.66,1430.62 1179.87,1434.84 1193.31,1442.36C1206.73,1449.89 1214.28,1460.09 1214.28,1470.72Z" style="fill:rgb(255,155,173);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1104.39,1401.46C1104.39,1375.15 1118.45,1350.79 1141.24,1337.63C1164.03,1324.48 1192.16,1324.48 1214.95,1337.63C1237.74,1350.79 1251.81,1375.15 1251.81,1401.46L1224.41,1401.46C1224.41,1384.9 1215.6,1369.64 1201.25,1361.36C1186.9,1353.07 1169.29,1353.07 1154.94,1361.36C1140.59,1369.64 1131.78,1384.9 1131.78,1401.46L1104.39,1401.46Z" style="fill:rgb(26,26,26);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1395.54,1401.46C1395.54,1375.15 1409.61,1350.79 1432.39,1337.63C1455.18,1324.48 1483.32,1324.48 1506.11,1337.63C1528.89,1350.79 1542.96,1375.15 1542.96,1401.46L1515.56,1401.46C1515.56,1384.9 1506.75,1369.64 1492.41,1361.36C1478.06,1353.07 1460.44,1353.07 1446.09,1361.36C1431.74,1369.64 1422.93,1384.9 1422.93,1401.46L1395.54,1401.46Z" style="fill:rgb(26,26,26);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,9.81959)">
|
||||
<path d="M1256.81,1448.61C1256.81,1472.48 1269.56,1494.57 1290.24,1506.51C1310.92,1518.45 1336.42,1518.45 1357.1,1506.51C1377.78,1494.57 1390.53,1472.48 1390.53,1448.61L1376.83,1448.61C1376.83,1467.61 1366.71,1485.15 1350.25,1494.65C1333.79,1504.15 1313.55,1504.15 1297.09,1494.65C1280.63,1485.15 1270.51,1467.61 1270.51,1448.61L1256.81,1448.61Z" style="fill:rgb(26,26,26);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
@@ -13,6 +13,7 @@
|
||||
},
|
||||
{
|
||||
"matchDepNames": [
|
||||
"dawidd6/action-download-artifact",
|
||||
"github/codeql-action",
|
||||
"ruby/setup-ruby"
|
||||
],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.14.1-SNAPSHOT</version>
|
||||
<version>0.16.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>documentation-support</artifactId>
|
||||
|
||||
@@ -135,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();
|
||||
}
|
||||
|
||||
@@ -154,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.14.1-SNAPSHOT</version>
|
||||
<version>0.16.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>error-prone-contrib</artifactId>
|
||||
@@ -48,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>
|
||||
@@ -73,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>
|
||||
@@ -179,6 +179,11 @@
|
||||
<artifactId>mongodb-driver-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
<artifactId>rewrite-core</artifactId>
|
||||
@@ -239,6 +244,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>
|
||||
@@ -267,6 +277,11 @@
|
||||
<artifactId>refaster-compiler</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>refaster-rule-benchmark-generator</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>refaster-support</artifactId>
|
||||
@@ -274,8 +289,8 @@
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs combine.children="append">
|
||||
<arg>-Xplugin:RefasterRuleCompiler</arg>
|
||||
<arg>-Xplugin:DocumentationGenerator -XoutputDirectory=${project.build.directory}/docs</arg>
|
||||
<arg>-Xplugin:RefasterRuleBenchmarkGenerator -XoutputDirectory=${project.build.directory}/generated-test-sources/test-annotations</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -9,7 +9,7 @@ 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.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;
|
||||
@@ -81,9 +81,8 @@ public final class CanonicalClassNameUsage extends BugChecker
|
||||
path = path.getParentPath();
|
||||
}
|
||||
|
||||
return path.getLeaf() instanceof MethodInvocationTree
|
||||
&& isOwnedByCanonicalNameUsingType(
|
||||
ASTHelpers.getSymbol((MethodInvocationTree) path.getLeaf()));
|
||||
return path.getLeaf() instanceof MethodInvocationTree methodInvocation
|
||||
&& isOwnedByCanonicalNameUsingType(ASTHelpers.getSymbol(methodInvocation));
|
||||
}
|
||||
|
||||
private static boolean isOwnedByCanonicalNameUsingType(MethodSymbol symbol) {
|
||||
|
||||
@@ -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.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;
|
||||
@@ -25,7 +25,7 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
import tech.picnic.errorprone.bugpatterns.util.ThirdPartyLibrary;
|
||||
import tech.picnic.errorprone.utils.ThirdPartyLibrary;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags {@link Collector Collectors} that don't clearly express
|
||||
|
||||
@@ -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
|
||||
@@ -101,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())
|
||||
@@ -151,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)
|
||||
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
|
||||
@@ -6,7 +6,7 @@ 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;
|
||||
@@ -27,7 +27,7 @@ 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
|
||||
|
||||
@@ -10,7 +10,7 @@ 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;
|
||||
@@ -36,7 +36,7 @@ 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
|
||||
@@ -245,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,7 +7,7 @@ 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;
|
||||
@@ -41,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
|
||||
@@ -124,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,9 +19,9 @@ 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;
|
||||
@@ -58,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
|
||||
@@ -232,7 +232,7 @@ public final class JUnitValueSource extends BugChecker implements MethodTreeMatc
|
||||
@Override
|
||||
public @Nullable Void visitReturn(ReturnTree node, @Nullable Void unused) {
|
||||
returnExpressions.add(node.getExpression());
|
||||
return super.visitReturn(node, unused);
|
||||
return super.visitReturn(node, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -265,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(", ")))
|
||||
@@ -276,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) {
|
||||
@@ -297,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(
|
||||
@@ -193,24 +192,24 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
@Override
|
||||
public @Nullable Void visitIdentifier(IdentifierTree node, @Nullable Void unused) {
|
||||
nodes.add(ImmutableList.of(node.getName().toString()));
|
||||
return super.visitIdentifier(node, unused);
|
||||
return super.visitIdentifier(node, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
return super.visitLiteral(node, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Void visitPrimitiveType(PrimitiveTypeTree node, @Nullable Void unused) {
|
||||
nodes.add(ImmutableList.of(node.getPrimitiveTypeKind().toString()));
|
||||
return super.visitPrimitiveType(node, unused);
|
||||
return super.visitPrimitiveType(node, null);
|
||||
}
|
||||
}.scan(array, null);
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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.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;
|
||||
@@ -33,9 +33,10 @@ 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
|
||||
@@ -71,7 +72,9 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
|
||||
Strings.class.getCanonicalName(),
|
||||
VisitorState.class.getCanonicalName(),
|
||||
ZoneOffset.class.getCanonicalName(),
|
||||
"com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode");
|
||||
"com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode",
|
||||
"reactor.core.publisher.Flux",
|
||||
"reactor.core.publisher.Mono");
|
||||
|
||||
/**
|
||||
* Type members that should never be statically imported.
|
||||
@@ -103,6 +106,7 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
|
||||
"sort",
|
||||
"swap")
|
||||
.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")
|
||||
@@ -125,7 +129,6 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
|
||||
"builder",
|
||||
"copyOf",
|
||||
"create",
|
||||
"empty",
|
||||
"from",
|
||||
"getDefaultInstance",
|
||||
"INSTANCE",
|
||||
@@ -136,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() {}
|
||||
@@ -168,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,
|
||||
@@ -210,7 +210,7 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
|
||||
}
|
||||
}
|
||||
|
||||
return super.visitIdentifier(node, unused);
|
||||
return super.visitIdentifier(node, null);
|
||||
}
|
||||
}.scan(tree, null);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.PERFORMANCE;
|
||||
import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.staticMethod;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
import com.sun.source.tree.LiteralTree;
|
||||
import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags arguments to {@link Optional#orElse(Object)} that should be
|
||||
* deferred using {@link Optional#orElseGet(Supplier)}.
|
||||
*
|
||||
* <p>The suggested fix assumes that the argument to {@code orElse} does not have side effects. If
|
||||
* it does, the suggested fix changes the program's semantics. Such fragile code must instead be
|
||||
* refactored such that the side-effectful code does not appear accidental.
|
||||
*/
|
||||
// XXX: Consider also implementing the inverse, in which `.orElseGet(() -> someConstant)` is
|
||||
// flagged.
|
||||
// XXX: Once the `MethodReferenceUsageCheck` becomes generally usable, consider leaving the method
|
||||
// reference cleanup to that check, and express the remainder of the logic in this class using a
|
||||
// Refaster template, i.c.w. a `@Matches` constraint that implements the `requiresComputation`
|
||||
// logic.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"""
|
||||
Prefer `Optional#orElseGet` over `Optional#orElse` if the fallback requires additional \
|
||||
computation""",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = PERFORMANCE)
|
||||
public final class OptionalOrElse extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> OPTIONAL_OR_ELSE_METHOD =
|
||||
instanceMethod().onExactClass(Optional.class.getCanonicalName()).namedAnyOf("orElse");
|
||||
// XXX: Also exclude invocations of `@Placeholder`-annotated methods.
|
||||
private static final Matcher<ExpressionTree> REFASTER_METHOD =
|
||||
staticMethod().onClass(Refaster.class.getCanonicalName());
|
||||
|
||||
/** Instantiates a new {@link OptionalOrElse} instance. */
|
||||
public OptionalOrElse() {}
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
if (!OPTIONAL_OR_ELSE_METHOD.matches(tree, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
ExpressionTree argument = Iterables.getOnlyElement(tree.getArguments());
|
||||
if (!requiresComputation(argument) || REFASTER_METHOD.matches(argument, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a match. Construct the method reference or lambda expression to be passed to the
|
||||
* replacement `#orElseGet` invocation.
|
||||
*/
|
||||
String newArgument =
|
||||
tryMethodReferenceConversion(argument, state)
|
||||
.orElseGet(() -> "() -> " + SourceCode.treeToString(argument, state));
|
||||
|
||||
/* Construct the suggested fix, replacing the method invocation and its argument. */
|
||||
SuggestedFix fix =
|
||||
SuggestedFix.builder()
|
||||
.merge(SuggestedFixes.renameMethodInvocation(tree, "orElseGet", state))
|
||||
.replace(argument, newArgument)
|
||||
.build();
|
||||
|
||||
return describeMatch(tree, fix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the given expression contains anything other than a literal or a (possibly
|
||||
* dereferenced) variable or constant.
|
||||
*/
|
||||
private static boolean requiresComputation(ExpressionTree tree) {
|
||||
return !(tree instanceof IdentifierTree
|
||||
|| tree instanceof LiteralTree
|
||||
|| (tree instanceof MemberSelectTree memberSelect
|
||||
&& !requiresComputation(memberSelect.getExpression()))
|
||||
|| ASTHelpers.constValue(tree) != null);
|
||||
}
|
||||
|
||||
/** Returns the nullary method reference matching the given expression, if any. */
|
||||
private static Optional<String> tryMethodReferenceConversion(
|
||||
ExpressionTree tree, VisitorState state) {
|
||||
if (!(tree instanceof MethodInvocationTree methodInvocation)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (!methodInvocation.getArguments().isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (!(methodInvocation.getMethodSelect() instanceof MemberSelectTree memberSelect)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (requiresComputation(memberSelect.getExpression())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(
|
||||
SourceCode.treeToString(memberSelect.getExpression(), state)
|
||||
+ "::"
|
||||
+ (methodInvocation.getTypeArguments().isEmpty()
|
||||
? ""
|
||||
: methodInvocation.getTypeArguments().stream()
|
||||
.map(arg -> SourceCode.treeToString(arg, state))
|
||||
.collect(joining(",", "<", ">")))
|
||||
+ memberSelect.getIdentifier());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -22,6 +22,7 @@ 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;
|
||||
@@ -33,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
|
||||
@@ -44,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,
|
||||
@@ -147,38 +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:
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder();
|
||||
String replacement =
|
||||
SuggestedFixes.qualifyStaticImport(
|
||||
Comparator.class.getCanonicalName() + '.' + preferredMethodName, fix, state);
|
||||
return fix.replace(expr, replacement).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,7 +15,7 @@ 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;
|
||||
@@ -53,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)
|
||||
@@ -331,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;
|
||||
@@ -73,7 +73,9 @@ 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"))),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestPart"),
|
||||
isType(
|
||||
"org.springframework.security.core.annotation.CurrentSecurityContext"))),
|
||||
isSameType(InputStream.class.getCanonicalName()),
|
||||
isSameType(Locale.class.getCanonicalName()),
|
||||
isSameType(TimeZone.class.getCanonicalName()),
|
||||
@@ -103,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;
|
||||
@@ -25,10 +24,9 @@ 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
|
||||
@@ -80,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(
|
||||
|
||||
@@ -6,7 +6,7 @@ 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;
|
||||
@@ -39,11 +39,13 @@ 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;
|
||||
@@ -100,6 +102,7 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa
|
||||
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",
|
||||
@@ -132,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.
|
||||
@@ -209,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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -22,9 +22,12 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import reactor.core.publisher.Mono;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
|
||||
/**
|
||||
@@ -211,6 +214,27 @@ final class AssortedRules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't defer to subscription-time non-reactive operations that can efficiently be performed
|
||||
* during assembly.
|
||||
*/
|
||||
// XXX: There are all kinds of variations on this theme. Generalize.
|
||||
// XXX: Drop or move to `ReactorRules`.
|
||||
static final class MonoFromSupplierOptionalMapOrElse<T, S> {
|
||||
@BeforeTemplate
|
||||
Mono<S> before(Optional<T> optional, Function<? super T, S> transformer, S value) {
|
||||
return Refaster.anyOf(
|
||||
Mono.justOrEmpty(optional).map(transformer),
|
||||
Mono.justOrEmpty(optional).mapNotNull(transformer))
|
||||
.defaultIfEmpty(value);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<? extends S> after(Optional<T> optional, Function<? super T, S> transformer, S value) {
|
||||
return Mono.fromSupplier(() -> optional.map(transformer).orElse(value));
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Don't unnecessarily pass a method reference to {@link Supplier#get()} or wrap this method
|
||||
// * in a lambda expression.
|
||||
|
||||
@@ -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. */,
|
||||
"StreamFindAnyIsEmpty" /* 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
|
||||
|
||||
@@ -16,8 +16,11 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.AlsoNegation;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.Matches;
|
||||
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
|
||||
import com.google.errorprone.refaster.annotation.Placeholder;
|
||||
import com.google.errorprone.refaster.annotation.Repeated;
|
||||
import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import java.util.Arrays;
|
||||
@@ -92,6 +95,24 @@ final class ComparatorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't explicitly compare enums by their ordinal. */
|
||||
abstract static class ComparingEnum<E extends Enum<E>, T> {
|
||||
@Placeholder(allowsIdentity = true)
|
||||
abstract E toEnumFunction(@MayOptionallyUse T value);
|
||||
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("EnumOrdinal" /* This violation will be rewritten. */)
|
||||
Comparator<T> before() {
|
||||
return comparingInt(v -> toEnumFunction(v).ordinal());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
Comparator<T> after() {
|
||||
return comparing(v -> toEnumFunction(v));
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't explicitly create {@link Comparator}s unnecessarily. */
|
||||
static final class ThenComparing<S, T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
@@ -269,7 +290,7 @@ final class ComparatorRules {
|
||||
static final class MinOfPairCustomOrder<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("java:S1067" /* The conditional operators are independent. */)
|
||||
T before(T value1, T value2, Comparator<T> cmp) {
|
||||
T before(T value1, T value2, Comparator<? super T> cmp) {
|
||||
return Refaster.anyOf(
|
||||
cmp.compare(value1, value2) <= 0 ? value1 : value2,
|
||||
cmp.compare(value1, value2) > 0 ? value2 : value1,
|
||||
@@ -284,7 +305,7 @@ final class ComparatorRules {
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(T value1, T value2, Comparator<T> cmp) {
|
||||
T after(T value1, T value2, Comparator<? super T> cmp) {
|
||||
return Comparators.min(value1, value2, cmp);
|
||||
}
|
||||
}
|
||||
@@ -336,7 +357,7 @@ final class ComparatorRules {
|
||||
static final class MaxOfPairCustomOrder<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("java:S1067" /* The conditional operators are independent. */)
|
||||
T before(T value1, T value2, Comparator<T> cmp) {
|
||||
T before(T value1, T value2, Comparator<? super T> cmp) {
|
||||
return Refaster.anyOf(
|
||||
cmp.compare(value1, value2) >= 0 ? value1 : value2,
|
||||
cmp.compare(value1, value2) < 0 ? value2 : value1,
|
||||
@@ -351,7 +372,7 @@ final class ComparatorRules {
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
T after(T value1, T value2, Comparator<T> cmp) {
|
||||
T after(T value1, T value2, Comparator<? super T> cmp) {
|
||||
return Comparators.max(value1, value2, cmp);
|
||||
}
|
||||
}
|
||||
@@ -419,4 +440,34 @@ final class ComparatorRules {
|
||||
return maxBy(naturalOrder());
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't explicitly compare enums by their ordinal. */
|
||||
static final class IsLessThan<E extends Enum<E>> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("EnumOrdinal" /* This violation will be rewritten. */)
|
||||
boolean before(E value1, E value2) {
|
||||
return value1.ordinal() < value2.ordinal();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@AlsoNegation
|
||||
boolean after(E value1, E value2) {
|
||||
return value1.compareTo(value2) < 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't explicitly compare enums by their ordinal. */
|
||||
static final class IsLessThanOrEqualTo<E extends Enum<E>> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("EnumOrdinal" /* This violation will be rewritten. */)
|
||||
boolean before(E value1, E value2) {
|
||||
return value1.ordinal() <= value2.ordinal();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@AlsoNegation
|
||||
boolean after(E value1, E value2) {
|
||||
return value1.compareTo(value2) <= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,9 @@ final class EqualityRules {
|
||||
// XXX: This Refaster rule is the topic of https://github.com/google/error-prone/issues/559. We
|
||||
// work around the issue by selecting the "largest replacements". See the `Refaster` check.
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("EnumOrdinal" /* This violation will be rewritten. */)
|
||||
boolean before(T a, T b) {
|
||||
return Refaster.anyOf(a.equals(b), Objects.equals(a, b));
|
||||
return Refaster.anyOf(a.equals(b), Objects.equals(a, b), a.ordinal() == b.ordinal());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
|
||||
@@ -9,8 +9,6 @@ import java.io.OutputStream;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
|
||||
/** Refaster rules related to expressions dealing with {@link InputStream}s. */
|
||||
// XXX: Add a rule for `ByteStreams.skipFully(in, n)` -> `in.skipNBytes(n)` once we have a way to
|
||||
// target JDK 12+ APIs.
|
||||
@OnlineDocumentation
|
||||
final class InputStreamRules {
|
||||
private InputStreamRules() {}
|
||||
@@ -38,4 +36,28 @@ final class InputStreamRules {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,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.
|
||||
@@ -40,6 +41,264 @@ 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() {}
|
||||
|
||||
|
||||
@@ -27,6 +27,19 @@ import tech.picnic.errorprone.refaster.matchers.IsLikelyTrivialComputation;
|
||||
final class OptionalRules {
|
||||
private OptionalRules() {}
|
||||
|
||||
/** Prefer {@link Optional#empty()} over the more contrived alternative. */
|
||||
static final class OptionalEmpty<T> {
|
||||
@BeforeTemplate
|
||||
Optional<T> before() {
|
||||
return Optional.ofNullable(null);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Optional<T> after() {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
static final class OptionalOfNullable<T> {
|
||||
// XXX: Refaster should be smart enough to also rewrite occurrences in which there are
|
||||
// parentheses around the null check, but that's currently not the case. Try to fix that.
|
||||
@@ -114,6 +127,20 @@ final class OptionalRules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Optional#equals(Object)} over more contrived alternatives. */
|
||||
static final class NotOptionalEqualsOptional<T, S> {
|
||||
@BeforeTemplate
|
||||
boolean before(Optional<T> optional, S value) {
|
||||
return Refaster.anyOf(
|
||||
optional.filter(value::equals).isEmpty(), optional.stream().noneMatch(value::equals));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
boolean after(Optional<T> optional, S value) {
|
||||
return !optional.equals(Optional.of(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't use the ternary operator to extract the first element of a possibly-empty {@link
|
||||
* Iterator} as an {@link Optional}.
|
||||
@@ -360,7 +387,7 @@ final class OptionalRules {
|
||||
/** Prefer {@link Optional#or(Supplier)} over more verbose alternatives. */
|
||||
static final class OptionalOrOtherOptional<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("NestedOptionals" /* Auto-fix for the `NestedOptionals` check. */)
|
||||
@SuppressWarnings("NestedOptionals")
|
||||
Optional<T> before(Optional<T> optional1, Optional<T> optional2) {
|
||||
// XXX: Note that rewriting the first and third variant will change the code's behavior if
|
||||
// `optional2` has side-effects.
|
||||
@@ -386,9 +413,13 @@ final class OptionalRules {
|
||||
*/
|
||||
static final class OptionalIdentity<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("NestedOptionals")
|
||||
Optional<T> before(Optional<T> optional, Comparator<? super T> comparator) {
|
||||
return Refaster.anyOf(
|
||||
optional.or(Refaster.anyOf(() -> Optional.empty(), Optional::empty)),
|
||||
optional
|
||||
.map(Optional::of)
|
||||
.orElseGet(Refaster.anyOf(() -> Optional.empty(), Optional::empty)),
|
||||
optional.stream().findFirst(),
|
||||
optional.stream().findAny(),
|
||||
optional.stream().min(comparator),
|
||||
@@ -442,9 +473,7 @@ final class OptionalRules {
|
||||
static final class OptionalStream<T> {
|
||||
@BeforeTemplate
|
||||
Stream<T> before(Optional<T> optional) {
|
||||
return Refaster.anyOf(
|
||||
optional.map(Stream::of).orElse(Stream.empty()),
|
||||
optional.map(Stream::of).orElseGet(Stream::empty));
|
||||
return optional.map(Stream::of).orElseGet(Stream::empty);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
|
||||
@@ -3,7 +3,6 @@ package tech.picnic.errorprone.refasterrules;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.MoreCollectors.toOptional;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.Comparator.reverseOrder;
|
||||
@@ -52,9 +51,9 @@ import reactor.util.context.Context;
|
||||
import reactor.util.function.Tuple2;
|
||||
import tech.picnic.errorprone.refaster.annotation.Description;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
import tech.picnic.errorprone.refaster.annotation.Severity;
|
||||
import tech.picnic.errorprone.refaster.matchers.IsEmpty;
|
||||
import tech.picnic.errorprone.refaster.matchers.IsIdentityOperation;
|
||||
import tech.picnic.errorprone.refaster.matchers.IsLikelyTrivialComputation;
|
||||
import tech.picnic.errorprone.refaster.matchers.ThrowsCheckedException;
|
||||
|
||||
/** Refaster rules related to Reactor expressions and statements. */
|
||||
@@ -174,6 +173,7 @@ final class ReactorRules {
|
||||
*
|
||||
* <p>In particular, avoid mixing of the {@link Optional} and {@link Mono} APIs.
|
||||
*/
|
||||
// XXX: Below we now have the opposite advice. Test.
|
||||
static final class MonoFromOptionalSwitchIfEmpty<T> {
|
||||
@BeforeTemplate
|
||||
Mono<T> before(Optional<T> optional, Mono<T> mono) {
|
||||
@@ -303,6 +303,38 @@ final class ReactorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't eagerly instantiate {@link Throwable}s for {@link Mono#error(Throwable)}; let the
|
||||
* framework do it on subscription.
|
||||
*/
|
||||
static final class MonoError<T> {
|
||||
@BeforeTemplate
|
||||
Mono<T> before(@NotMatches(IsLikelyTrivialComputation.class) Throwable throwable) {
|
||||
return Mono.error(throwable);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<T> after(Throwable throwable) {
|
||||
return Mono.error(() -> throwable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't eagerly instantiate {@link Throwable}s for {@link Flux#error(Throwable)}; let the
|
||||
* framework do it on subscription.
|
||||
*/
|
||||
static final class FluxError<T> {
|
||||
@BeforeTemplate
|
||||
Flux<T> before(@NotMatches(IsLikelyTrivialComputation.class) Throwable throwable) {
|
||||
return Flux.error(throwable);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Flux<T> after(Throwable throwable) {
|
||||
return Flux.error(() -> throwable);
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily defer {@link Mono#error(Throwable)}. */
|
||||
static final class MonoDeferredError<T> {
|
||||
@BeforeTemplate
|
||||
@@ -380,30 +412,23 @@ final class ReactorRules {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Flux#take(long, boolean)} over {@link Flux#take(long)}.
|
||||
* Prefer {@link Flux#take(long)} over {@link Flux#take(long, boolean)} where relevant.
|
||||
*
|
||||
* <p>In Reactor versions prior to 3.5.0, {@code Flux#take(long)} makes an unbounded request
|
||||
* upstream, and is equivalent to {@code Flux#take(long, false)}. In 3.5.0, the behavior of {@code
|
||||
* Flux#take(long)} will change to that of {@code Flux#take(long, true)}.
|
||||
*
|
||||
* <p>The intent with this Refaster rule is to get the new behavior before upgrading to Reactor
|
||||
* 3.5.0.
|
||||
* upstream, and is equivalent to {@code Flux#take(long, false)}. From version 3.5.0 onwards, the
|
||||
* behavior of {@code Flux#take(long)} instead matches {@code Flux#take(long, true)}.
|
||||
*/
|
||||
// XXX: Drop this rule some time after upgrading to Reactor 3.6.0, or introduce a way to apply
|
||||
// this rule only when an older version of Reactor is on the classpath.
|
||||
// XXX: Once Reactor 3.6.0 is out, introduce a rule that rewrites code in the opposite direction.
|
||||
@Description(
|
||||
"Prior to Reactor 3.5.0, `take(n)` requests and unbounded number of elements upstream.")
|
||||
@Severity(WARNING)
|
||||
"From Reactor 3.5.0 onwards, `take(n)` no longer requests an unbounded number of elements upstream.")
|
||||
static final class FluxTake<T> {
|
||||
@BeforeTemplate
|
||||
Flux<T> before(Flux<T> flux, long n) {
|
||||
return flux.take(n);
|
||||
return flux.take(n, /* limitRequest= */ true);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Flux<T> after(Flux<T> flux, long n) {
|
||||
return flux.take(n, /* limitRequest= */ true);
|
||||
return flux.take(n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1205,6 +1230,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.
|
||||
@@ -1694,6 +1732,22 @@ final class ReactorRules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't defer to subscription-time non-reactive operations that can efficiently be performed
|
||||
* during assembly.
|
||||
*/
|
||||
static final class OptionalMapOrElse<T, S, M extends Mono<? extends S>> {
|
||||
@BeforeTemplate
|
||||
Mono<S> before(Optional<T> optional, Function<? super T, M> transformer, M mono) {
|
||||
return Mono.justOrEmpty(optional).flatMap(transformer).switchIfEmpty(mono);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<? extends S> after(Optional<T> optional, Function<? super T, M> transformer, M mono) {
|
||||
return optional.map(transformer).orElse(mono);
|
||||
}
|
||||
}
|
||||
|
||||
/** 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.
|
||||
|
||||
@@ -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;
|
||||
@@ -24,6 +25,7 @@ import com.google.common.collect.Streams;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.AlsoNegation;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.Matches;
|
||||
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
|
||||
@@ -37,6 +39,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,32 +257,38 @@ 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 StreamFindAnyIsEmpty<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
|
||||
@AlsoNegation
|
||||
boolean after(Stream<T> stream) {
|
||||
return stream.findAny().isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/** In order to test whether a stream has any element, simply try to find one. */
|
||||
static final class StreamIsNotEmpty<T> {
|
||||
/**
|
||||
* Prefer {@link Stream#findAny()} over {@link Stream#findFirst()} if one only cares whether the
|
||||
* stream is nonempty.
|
||||
*/
|
||||
static final class StreamFindAnyIsPresent<T> {
|
||||
@BeforeTemplate
|
||||
boolean before(Stream<T> stream) {
|
||||
return Refaster.anyOf(
|
||||
stream.count() != 0,
|
||||
stream.count() > 0,
|
||||
stream.count() >= 1,
|
||||
stream.findFirst().isPresent());
|
||||
return stream.findFirst().isPresent();
|
||||
}
|
||||
|
||||
@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() {}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ final class CanonicalClassNameUsageTest {
|
||||
"import static com.google.errorprone.matchers.Matchers.instanceMethod;",
|
||||
"",
|
||||
"import com.google.errorprone.VisitorState;",
|
||||
"import tech.picnic.errorprone.bugpatterns.util.MoreTypes;",
|
||||
"import tech.picnic.errorprone.utils.MoreTypes;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m(VisitorState state) {",
|
||||
@@ -54,7 +54,7 @@ final class CanonicalClassNameUsageTest {
|
||||
"import static com.google.errorprone.matchers.Matchers.instanceMethod;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"import tech.picnic.errorprone.bugpatterns.util.MoreTypes;",
|
||||
"import tech.picnic.errorprone.utils.MoreTypes;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() {",
|
||||
@@ -67,7 +67,7 @@ final class CanonicalClassNameUsageTest {
|
||||
"import static com.google.errorprone.matchers.Matchers.instanceMethod;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"import tech.picnic.errorprone.bugpatterns.util.MoreTypes;",
|
||||
"import tech.picnic.errorprone.utils.MoreTypes;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() {",
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class OptionalOrElseTest {
|
||||
@Test
|
||||
void identification() {
|
||||
CompilationTestHelper.newInstance(OptionalOrElse.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import com.google.errorprone.refaster.Refaster;",
|
||||
"import java.util.Optional;",
|
||||
"",
|
||||
"class A {",
|
||||
" private final Optional<Object> optional = Optional.empty();",
|
||||
" private final String string = optional.toString();",
|
||||
"",
|
||||
" void m() {",
|
||||
" Optional.empty().orElse(null);",
|
||||
" optional.orElse(null);",
|
||||
" optional.orElse(\"constant\");",
|
||||
" optional.orElse(\"constant\" + 0);",
|
||||
" optional.orElse(Boolean.TRUE);",
|
||||
" optional.orElse(string);",
|
||||
" optional.orElse(this.string);",
|
||||
" optional.orElse(Refaster.anyOf(\"constant\", \"another\"));",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Optional.empty().orElse(string + \"constant\");",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" optional.orElse(string + \"constant\");",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" optional.orElse(\"constant\".toString());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" optional.orElse(string.toString());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" optional.orElse(this.string.toString());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" optional.orElse(String.valueOf(42));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" optional.orElse(string.toString().length());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" optional.orElse(\"constant\".equals(string));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" optional.orElse(string.equals(string));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" optional.orElse(this.string.equals(string));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" optional.orElse(foo());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" optional.orElse(this.foo());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" optional.orElse(new Object() {});",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" optional.orElse(new int[0].length);",
|
||||
" }",
|
||||
"",
|
||||
" private <T> T foo() {",
|
||||
" return null;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
BugCheckerRefactoringTestHelper.newInstance(OptionalOrElse.class, getClass())
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import java.util.Optional;",
|
||||
"",
|
||||
"class A {",
|
||||
" private final Optional<Object> optional = Optional.empty();",
|
||||
" private final String string = optional.toString();",
|
||||
"",
|
||||
" void m() {",
|
||||
" optional.orElse(string + \"constant\");",
|
||||
" optional.orElse(\"constant\".toString());",
|
||||
" optional.orElse(string.toString());",
|
||||
" optional.orElse(this.string.toString());",
|
||||
" optional.orElse(String.valueOf(42));",
|
||||
" optional.orElse(string.toString().length());",
|
||||
" optional.orElse(string.equals(string));",
|
||||
" optional.orElse(foo());",
|
||||
" optional.orElse(this.<Number>foo());",
|
||||
" optional.orElse(this.<String, Integer>bar());",
|
||||
" optional.orElse(new Object() {});",
|
||||
" optional.orElse(new int[0].length);",
|
||||
" }",
|
||||
"",
|
||||
" private <T> T foo() {",
|
||||
" return null;",
|
||||
" }",
|
||||
"",
|
||||
" private <S, T> T bar() {",
|
||||
" return null;",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import java.util.Optional;",
|
||||
"",
|
||||
"class A {",
|
||||
" private final Optional<Object> optional = Optional.empty();",
|
||||
" private final String string = optional.toString();",
|
||||
"",
|
||||
" void m() {",
|
||||
" optional.orElseGet(() -> string + \"constant\");",
|
||||
" optional.orElseGet(\"constant\"::toString);",
|
||||
" optional.orElseGet(string::toString);",
|
||||
" optional.orElseGet(this.string::toString);",
|
||||
" optional.orElseGet(() -> String.valueOf(42));",
|
||||
" optional.orElseGet(() -> string.toString().length());",
|
||||
" optional.orElseGet(() -> string.equals(string));",
|
||||
" optional.orElseGet(() -> foo());",
|
||||
" optional.orElseGet(this::<Number>foo);",
|
||||
" optional.orElseGet(this::<String, Integer>bar);",
|
||||
" optional.orElseGet(() -> new Object() {});",
|
||||
" optional.orElseGet(() -> new int[0].length);",
|
||||
" }",
|
||||
"",
|
||||
" private <T> T foo() {",
|
||||
" return null;",
|
||||
" }",
|
||||
"",
|
||||
" private <S, T> T bar() {",
|
||||
" return null;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -19,7 +19,6 @@ final class Slf4jLogStatementTest {
|
||||
"class A {",
|
||||
" private static final String FMT0 = \"format-string-without-placeholders\";",
|
||||
" private static final String FMT1 = \"format-string-with-{}-placeholder\";",
|
||||
" private static final String FMT2 = \"format-string-with-{}-{}-placeholders\";",
|
||||
" private static final String FMT_ERR = \"format-string-with-%s-placeholder\";",
|
||||
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
|
||||
"",
|
||||
|
||||
@@ -47,6 +47,7 @@ final class StaticImportTest {
|
||||
"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;",
|
||||
@@ -99,6 +100,8 @@ final class StaticImportTest {
|
||||
" // BUG: Diagnostic contains:",
|
||||
" MediaType t2 = MediaType.APPLICATION_JSON;",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Filters.empty();",
|
||||
" Optional.empty();",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static org.openjdk.jmh.results.format.ResultFormatType.JSON;
|
||||
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
|
||||
import com.google.errorprone.refaster.annotation.Placeholder;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
import org.openjdk.jmh.profile.GCProfiler;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.RunnerException;
|
||||
import org.openjdk.jmh.runner.options.OptionsBuilder;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import tech.picnic.errorprone.refaster.annotation.Benchmarked;
|
||||
|
||||
// XXX: Fix warmup and measurements etc.
|
||||
// XXX: Flag cases where the `before` code is faster, taking into account variation.
|
||||
@SuppressWarnings("FieldCanBeFinal") // XXX: Triggers CompilesWithFix!!!
|
||||
@State(Scope.Benchmark)
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@Fork(
|
||||
jvmArgs = {"-Xms256M", "-Xmx256M"},
|
||||
value = 1)
|
||||
@Warmup(iterations = 1)
|
||||
@Measurement(iterations = 10, time = 1)
|
||||
public class ReactorRulesBenchmarks {
|
||||
public static void main(String[] args) throws RunnerException {
|
||||
String testRegex = Pattern.quote(ReactorRulesBenchmarks.class.getCanonicalName());
|
||||
new Runner(
|
||||
new OptionsBuilder()
|
||||
.include(testRegex)
|
||||
.addProfiler(GCProfiler.class)
|
||||
.resultFormat(JSON) // XXX: Review.
|
||||
.build())
|
||||
.run();
|
||||
}
|
||||
|
||||
// XXX: What a benchmarked rule could look like.
|
||||
@Benchmarked
|
||||
static final class MonoFromOptionalSwitchIfEmpty<T> {
|
||||
// XXX: These parameters could perhaps be inferred in the common case.
|
||||
@Benchmarked.Param private final Optional<String> emptyOptional = Optional.empty();
|
||||
@Benchmarked.Param private final Optional<String> nonEmptyOptional = Optional.of("foo");
|
||||
@Benchmarked.Param private final Mono<String> emptyMono = Mono.<String>empty().hide();
|
||||
@Benchmarked.Param private final Mono<String> nonEmptyMono = Mono.just("bar").hide();
|
||||
|
||||
// XXX: Dropped to hide this class from `RefasterRuleCompilerTaskListener`: @BeforeTemplate
|
||||
Mono<T> before(Optional<T> optional, Mono<T> mono) {
|
||||
return optional.map(Mono::just).orElse(mono);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<T> after(Optional<T> optional, Mono<T> mono) {
|
||||
return Mono.justOrEmpty(optional).switchIfEmpty(mono);
|
||||
}
|
||||
|
||||
// XXX: Methods such as this one could perhaps be inferred in the common case.
|
||||
@Benchmarked.OnResult
|
||||
T subscribe(Mono<T> mono) {
|
||||
return mono.block();
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Variations like this would be generated.
|
||||
public static class MonoFromOptionalSwitchIfEmptyBenchmark extends ReactorRulesBenchmarks {
|
||||
private final MonoFromOptionalSwitchIfEmpty<String> rule = new MonoFromOptionalSwitchIfEmpty();
|
||||
// private final Optional<String> optional = Optional.of("foo");
|
||||
// private final Mono<String> mono = Mono.just("bar").hide();
|
||||
private final Optional<String> optional = Optional.empty();
|
||||
private final Mono<String> mono = Mono.<String>empty().hide();
|
||||
private final Mono<String> before = rule.before(optional, mono);
|
||||
private final Mono<String> after = rule.after(optional, mono);
|
||||
|
||||
@Benchmark
|
||||
public Mono<String> before() {
|
||||
return rule.before(optional, mono);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String beforeSubscribe1() {
|
||||
return before.block();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Mono<String> after() {
|
||||
return rule.after(optional, mono);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String afterSubscribe() {
|
||||
return after.block();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
abstract static class MonoFlatMapToFlux<T, S> {
|
||||
@Placeholder(allowsIdentity = true)
|
||||
abstract Mono<S> transformation(@MayOptionallyUse T value);
|
||||
|
||||
// XXX: Dropped to hide this class from `RefasterRuleCompilerTaskListener`: @BeforeTemplate
|
||||
Flux<S> before(Mono<T> mono) {
|
||||
return mono.flatMapMany(v -> transformation(v));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Flux<S> after(Mono<T> mono) {
|
||||
return mono.flatMap(v -> transformation(v)).flux();
|
||||
}
|
||||
|
||||
@Benchmarked.OnResult
|
||||
Long subscribe(Flux<T> flux) {
|
||||
return flux.count().block();
|
||||
}
|
||||
|
||||
@Benchmarked.MinimalPlaceholder
|
||||
<X> Mono<X> identity(X value) {
|
||||
return Mono.just(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static class MonoFlatMapToFluxBenchmarks extends ReactorRulesBenchmarks {
|
||||
abstract static class Rule<T, S> {
|
||||
abstract Mono<S> transformation(T value);
|
||||
|
||||
Flux<S> before(Mono<T> mono) {
|
||||
return mono.flatMapMany(v -> transformation(v));
|
||||
}
|
||||
|
||||
Flux<S> after(Mono<T> mono) {
|
||||
return mono.flatMap(v -> transformation(v)).flux();
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: For variantions, we could use `@Param(strings)` indirection, like in
|
||||
// https://github.com/openjdk/jmh/blob/master/jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_35_Profilers.java
|
||||
|
||||
// XXX: Or we can make the benchmark abstract and have subclasses for each variant:
|
||||
// https://github.com/openjdk/jmh/blob/master/jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_24_Inheritance.java
|
||||
|
||||
public static class MonoFlatMapToFluxStringStringBenchmark extends MonoFlatMapToFluxBenchmarks {
|
||||
private final Rule<String, String> rule =
|
||||
new Rule<>() {
|
||||
@Override
|
||||
Mono<String> transformation(String value) {
|
||||
return Mono.just(value);
|
||||
}
|
||||
};
|
||||
private final Mono<String> mono = Mono.just("foo");
|
||||
private final Flux<String> before = rule.before(mono);
|
||||
private final Flux<String> after = rule.after(mono);
|
||||
|
||||
@Benchmark
|
||||
public Flux<String> before() {
|
||||
return rule.before(mono);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Flux<String> after() {
|
||||
return rule.after(mono);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Long onResultOfBefore() {
|
||||
return before.count().block();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Long onResultOfAfter() {
|
||||
return after.count().block();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -9,6 +9,7 @@ import static java.util.stream.Collectors.minBy;
|
||||
import com.google.common.collect.Comparators;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -54,6 +55,10 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Comparator.comparing(s -> "foo", Comparator.comparingInt(String::length)));
|
||||
}
|
||||
|
||||
Comparator<String> testComparingEnum() {
|
||||
return Comparator.comparingInt(s -> RoundingMode.valueOf(s).ordinal());
|
||||
}
|
||||
|
||||
Comparator<String> testThenComparing() {
|
||||
return Comparator.<String>naturalOrder().thenComparing(Comparator.comparing(String::isEmpty));
|
||||
}
|
||||
@@ -173,4 +178,16 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Collector<Integer, ?, Optional<Integer>> testMaxByNaturalOrder() {
|
||||
return minBy(reverseOrder());
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testIsLessThan() {
|
||||
return ImmutableSet.of(
|
||||
RoundingMode.UP.ordinal() < RoundingMode.DOWN.ordinal(),
|
||||
RoundingMode.UP.ordinal() >= RoundingMode.DOWN.ordinal());
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testIsLessThanOrEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
RoundingMode.UP.ordinal() <= RoundingMode.DOWN.ordinal(),
|
||||
RoundingMode.UP.ordinal() > RoundingMode.DOWN.ordinal());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.Comparator.reverseOrder;
|
||||
import static java.util.function.Function.identity;
|
||||
@@ -9,6 +10,7 @@ import static java.util.stream.Collectors.minBy;
|
||||
import com.google.common.collect.Comparators;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -52,6 +54,10 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Comparator.comparing(s -> "foo", Comparator.comparingInt(String::length)));
|
||||
}
|
||||
|
||||
Comparator<String> testComparingEnum() {
|
||||
return comparing(s -> RoundingMode.valueOf(s));
|
||||
}
|
||||
|
||||
Comparator<String> testThenComparing() {
|
||||
return Comparator.<String>naturalOrder().thenComparing(String::isEmpty);
|
||||
}
|
||||
@@ -163,4 +169,16 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Collector<Integer, ?, Optional<Integer>> testMaxByNaturalOrder() {
|
||||
return maxBy(naturalOrder());
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testIsLessThan() {
|
||||
return ImmutableSet.of(
|
||||
RoundingMode.UP.compareTo(RoundingMode.DOWN) < 0,
|
||||
RoundingMode.UP.compareTo(RoundingMode.DOWN) >= 0);
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testIsLessThanOrEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
RoundingMode.UP.compareTo(RoundingMode.DOWN) <= 0,
|
||||
RoundingMode.UP.compareTo(RoundingMode.DOWN) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,10 @@ final class EqualityRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(
|
||||
RoundingMode.UP.equals(RoundingMode.DOWN),
|
||||
Objects.equals(RoundingMode.UP, RoundingMode.DOWN),
|
||||
RoundingMode.UP.ordinal() == RoundingMode.DOWN.ordinal(),
|
||||
!RoundingMode.UP.equals(RoundingMode.DOWN),
|
||||
!Objects.equals(RoundingMode.UP, RoundingMode.DOWN));
|
||||
!Objects.equals(RoundingMode.UP, RoundingMode.DOWN),
|
||||
RoundingMode.UP.ordinal() != RoundingMode.DOWN.ordinal());
|
||||
}
|
||||
|
||||
boolean testEqualsPredicate() {
|
||||
|
||||
@@ -21,6 +21,8 @@ final class EqualityRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(
|
||||
RoundingMode.UP == RoundingMode.DOWN,
|
||||
RoundingMode.UP == RoundingMode.DOWN,
|
||||
RoundingMode.UP == RoundingMode.DOWN,
|
||||
RoundingMode.UP != RoundingMode.DOWN,
|
||||
RoundingMode.UP != RoundingMode.DOWN,
|
||||
RoundingMode.UP != RoundingMode.DOWN);
|
||||
}
|
||||
|
||||
@@ -20,4 +20,12 @@ final class InputStreamRulesTest implements RefasterRuleCollectionTestCase {
|
||||
byte[] testInputStreamReadAllBytes() throws IOException {
|
||||
return ByteStreams.toByteArray(new ByteArrayInputStream(new byte[0]));
|
||||
}
|
||||
|
||||
byte[] testInputStreamReadNBytes() throws IOException {
|
||||
return ByteStreams.limit(new ByteArrayInputStream(new byte[0]), 0).readAllBytes();
|
||||
}
|
||||
|
||||
void testInputStreamSkipNBytes() throws IOException {
|
||||
ByteStreams.skipFully(new ByteArrayInputStream(new byte[0]), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,12 @@ final class InputStreamRulesTest implements RefasterRuleCollectionTestCase {
|
||||
byte[] testInputStreamReadAllBytes() throws IOException {
|
||||
return new ByteArrayInputStream(new byte[0]).readAllBytes();
|
||||
}
|
||||
|
||||
byte[] testInputStreamReadNBytes() throws IOException {
|
||||
return new ByteArrayInputStream(new byte[0]).readNBytes(0);
|
||||
}
|
||||
|
||||
void testInputStreamSkipNBytes() throws IOException {
|
||||
new ByteArrayInputStream(new byte[0]).skipNBytes(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(Streams.class);
|
||||
}
|
||||
|
||||
Optional<String> testOptionalEmpty() {
|
||||
return Optional.ofNullable(null);
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<String>> testOptionalOfNullable() {
|
||||
return ImmutableSet.of(
|
||||
toString() == null ? Optional.empty() : Optional.of(toString()),
|
||||
@@ -41,6 +45,12 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Optional.of("baz").stream().anyMatch("qux"::equals));
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testNotOptionalEqualsOptional() {
|
||||
return ImmutableSet.of(
|
||||
Optional.of("foo").filter("bar"::equals).isEmpty(),
|
||||
Optional.of("baz").stream().noneMatch("qux"::equals));
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<String>> testOptionalFirstIteratorElement() {
|
||||
return ImmutableSet.of(
|
||||
ImmutableSet.of("foo").iterator().hasNext()
|
||||
@@ -120,10 +130,12 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(
|
||||
Optional.of("foo").or(() -> Optional.empty()),
|
||||
Optional.of("bar").or(Optional::empty),
|
||||
Optional.of("baz").stream().findFirst(),
|
||||
Optional.of("qux").stream().findAny(),
|
||||
Optional.of("quux").stream().min(String::compareTo),
|
||||
Optional.of("quuz").stream().max(String::compareTo));
|
||||
Optional.of("baz").map(Optional::of).orElseGet(() -> Optional.empty()),
|
||||
Optional.of("qux").map(Optional::of).orElseGet(Optional::empty),
|
||||
Optional.of("quux").stream().findFirst(),
|
||||
Optional.of("quuz").stream().findAny(),
|
||||
Optional.of("corge").stream().min(String::compareTo),
|
||||
Optional.of("grault").stream().max(String::compareTo));
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<String>> testOptionalFilter() {
|
||||
@@ -136,9 +148,7 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return Optional.of(1).stream().map(String::valueOf).findAny();
|
||||
}
|
||||
|
||||
ImmutableSet<Stream<String>> testOptionalStream() {
|
||||
return ImmutableSet.of(
|
||||
Optional.of("foo").map(Stream::of).orElse(Stream.empty()),
|
||||
Optional.of("bar").map(Stream::of).orElseGet(Stream::empty));
|
||||
Stream<String> testOptionalStream() {
|
||||
return Optional.of("foo").map(Stream::of).orElseGet(Stream::empty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(Streams.class);
|
||||
}
|
||||
|
||||
Optional<String> testOptionalEmpty() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<String>> testOptionalOfNullable() {
|
||||
return ImmutableSet.of(Optional.ofNullable(toString()), Optional.ofNullable(toString()));
|
||||
}
|
||||
@@ -41,6 +45,12 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Optional.of("baz").equals(Optional.of("qux")));
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testNotOptionalEqualsOptional() {
|
||||
return ImmutableSet.of(
|
||||
!Optional.of("foo").equals(Optional.of("bar")),
|
||||
!Optional.of("baz").equals(Optional.of("qux")));
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<String>> testOptionalFirstIteratorElement() {
|
||||
return ImmutableSet.of(
|
||||
stream(ImmutableSet.of("foo").iterator()).findFirst(),
|
||||
@@ -120,7 +130,9 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Optional.of("baz"),
|
||||
Optional.of("qux"),
|
||||
Optional.of("quux"),
|
||||
Optional.of("quuz"));
|
||||
Optional.of("quuz"),
|
||||
Optional.of("corge"),
|
||||
Optional.of("grault"));
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<String>> testOptionalFilter() {
|
||||
@@ -132,7 +144,7 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return Optional.of(1).map(String::valueOf);
|
||||
}
|
||||
|
||||
ImmutableSet<Stream<String>> testOptionalStream() {
|
||||
return ImmutableSet.of(Optional.of("foo").stream(), Optional.of("bar").stream());
|
||||
Stream<String> testOptionalStream() {
|
||||
return Optional.of("foo").stream();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,16 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return Flux.just("foo", "bar").zipWithIterable(ImmutableSet.of(1, 2), String::repeat);
|
||||
}
|
||||
|
||||
ImmutableSet<?> testMonoError() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
return ImmutableSet.of(Mono.error(exception), Mono.error(new IllegalArgumentException()));
|
||||
}
|
||||
|
||||
ImmutableSet<?> testFluxError() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
return ImmutableSet.of(Flux.error(exception), Flux.error(new IllegalArgumentException()));
|
||||
}
|
||||
|
||||
Mono<Void> testMonoDeferredError() {
|
||||
return Mono.defer(() -> Mono.error(new IllegalStateException()));
|
||||
}
|
||||
@@ -142,7 +152,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
}
|
||||
|
||||
Flux<Integer> testFluxTake() {
|
||||
return Flux.just(1, 2, 3).take(1);
|
||||
return Flux.just(1, 2, 3).take(1, true);
|
||||
}
|
||||
|
||||
Mono<String> testMonoDefaultIfEmpty() {
|
||||
@@ -432,6 +442,10 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Flux.just(ImmutableList.of("bar")).concatMap(Flux::fromIterable, 2));
|
||||
}
|
||||
|
||||
Flux<String> testFluxFromIterable() {
|
||||
return Flux.fromStream(ImmutableList.of("foo").stream());
|
||||
}
|
||||
|
||||
ImmutableSet<Mono<Integer>> testFluxCountMapMathToIntExact() {
|
||||
return ImmutableSet.of(
|
||||
Flux.just(1).collect(toImmutableList()).map(Collection::size),
|
||||
@@ -571,6 +585,10 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
MathFlux.min(Flux.just(1), reverseOrder()), MathFlux.max(Flux.just(2), naturalOrder()));
|
||||
}
|
||||
|
||||
Mono<String> testOptionalMapOrElse() {
|
||||
return Mono.justOrEmpty(Optional.of("foo")).flatMap(Mono::just).switchIfEmpty(Mono.just("bar"));
|
||||
}
|
||||
|
||||
ImmutableSet<Context> testContextEmpty() {
|
||||
return ImmutableSet.of(Context.of(ImmutableMap.of()), Context.of(ImmutableMap.of(1, 2)));
|
||||
}
|
||||
|
||||
@@ -123,6 +123,16 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
.map(function(String::repeat));
|
||||
}
|
||||
|
||||
ImmutableSet<?> testMonoError() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
return ImmutableSet.of(Mono.error(exception), Mono.error(() -> new IllegalArgumentException()));
|
||||
}
|
||||
|
||||
ImmutableSet<?> testFluxError() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
return ImmutableSet.of(Flux.error(exception), Flux.error(() -> new IllegalArgumentException()));
|
||||
}
|
||||
|
||||
Mono<Void> testMonoDeferredError() {
|
||||
return Mono.error(() -> new IllegalStateException());
|
||||
}
|
||||
@@ -147,7 +157,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
}
|
||||
|
||||
Flux<Integer> testFluxTake() {
|
||||
return Flux.just(1, 2, 3).take(1, true);
|
||||
return Flux.just(1, 2, 3).take(1);
|
||||
}
|
||||
|
||||
Mono<String> testMonoDefaultIfEmpty() {
|
||||
@@ -427,6 +437,10 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Flux.just(ImmutableList.of("bar")).concatMapIterable(identity(), 2));
|
||||
}
|
||||
|
||||
Flux<String> testFluxFromIterable() {
|
||||
return Flux.fromIterable(ImmutableList.of("foo"));
|
||||
}
|
||||
|
||||
ImmutableSet<Mono<Integer>> testFluxCountMapMathToIntExact() {
|
||||
return ImmutableSet.of(
|
||||
Flux.just(1).count().map(Math::toIntExact),
|
||||
@@ -560,6 +574,10 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return ImmutableSet.of(MathFlux.max(Flux.just(1)), MathFlux.max(Flux.just(2)));
|
||||
}
|
||||
|
||||
Mono<String> testOptionalMapOrElse() {
|
||||
return Optional.of("foo").map(Mono::just).orElse(Mono.just("bar"));
|
||||
}
|
||||
|
||||
ImmutableSet<Context> testContextEmpty() {
|
||||
return ImmutableSet.of(Context.empty(), Context.of(ImmutableMap.of(1, 2)));
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static java.util.Comparator.comparingInt;
|
||||
import static java.util.Comparator.reverseOrder;
|
||||
import static java.util.function.Function.identity;
|
||||
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;
|
||||
@@ -21,11 +24,14 @@ import static java.util.stream.Collectors.summingInt;
|
||||
import static java.util.stream.Collectors.summingLong;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Streams;
|
||||
import java.util.DoubleSummaryStatistics;
|
||||
import java.util.IntSummaryStatistics;
|
||||
import java.util.List;
|
||||
import java.util.LongSummaryStatistics;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
@@ -38,8 +44,12 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
|
||||
public ImmutableSet<Object> elidedTypesAndStaticImports() {
|
||||
return ImmutableSet.of(
|
||||
ImmutableList.class,
|
||||
ImmutableMap.class,
|
||||
List.class,
|
||||
Map.class,
|
||||
Objects.class,
|
||||
Streams.class,
|
||||
collectingAndThen(null, null),
|
||||
counting(),
|
||||
filtering(null, null),
|
||||
flatMapping(null, null),
|
||||
@@ -54,7 +64,9 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
|
||||
summarizingLong(null),
|
||||
summingDouble(null),
|
||||
summingInt(null),
|
||||
summingLong(null));
|
||||
summingLong(null),
|
||||
toImmutableList(),
|
||||
toImmutableMap(null, null));
|
||||
}
|
||||
|
||||
String testJoining() {
|
||||
@@ -108,21 +120,25 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Stream.of("bar").map(String::length).findFirst());
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testStreamIsEmpty() {
|
||||
ImmutableSet<Boolean> testStreamFindAnyIsEmpty() {
|
||||
return ImmutableSet.of(
|
||||
Stream.of(1).count() == 0,
|
||||
Stream.of(2).count() <= 0,
|
||||
Stream.of(3).count() < 1,
|
||||
Stream.of(4).findFirst().isEmpty(),
|
||||
Stream.of(5).collect(toImmutableSet()).isEmpty());
|
||||
Stream.of(5).collect(toImmutableSet()).isEmpty(),
|
||||
Stream.of(6).collect(collectingAndThen(toImmutableList(), List::isEmpty)),
|
||||
Stream.of(7).collect(collectingAndThen(toImmutableList(), ImmutableList::isEmpty)),
|
||||
Stream.of(8).collect(collectingAndThen(toImmutableMap(k -> k, v -> v), Map::isEmpty)),
|
||||
Stream.of(9)
|
||||
.collect(collectingAndThen(toImmutableMap(k -> k, v -> v), ImmutableMap::isEmpty)),
|
||||
Stream.of(10).count() != 0,
|
||||
Stream.of(11).count() > 0,
|
||||
Stream.of(12).count() >= 1);
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testStreamIsNotEmpty() {
|
||||
return ImmutableSet.of(
|
||||
Stream.of(1).count() != 0,
|
||||
Stream.of(2).count() > 0,
|
||||
Stream.of(3).count() >= 1,
|
||||
Stream.of(4).findFirst().isPresent());
|
||||
boolean testStreamFindAnyIsPresent() {
|
||||
return Stream.of(1).findFirst().isPresent();
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<String>> testStreamMin() {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static java.util.Comparator.comparingInt;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.Comparator.reverseOrder;
|
||||
import static java.util.function.Function.identity;
|
||||
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;
|
||||
@@ -22,12 +25,15 @@ import static java.util.stream.Collectors.summingInt;
|
||||
import static java.util.stream.Collectors.summingLong;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Streams;
|
||||
import java.util.Arrays;
|
||||
import java.util.DoubleSummaryStatistics;
|
||||
import java.util.IntSummaryStatistics;
|
||||
import java.util.List;
|
||||
import java.util.LongSummaryStatistics;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
@@ -40,8 +46,12 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
|
||||
public ImmutableSet<Object> elidedTypesAndStaticImports() {
|
||||
return ImmutableSet.of(
|
||||
ImmutableList.class,
|
||||
ImmutableMap.class,
|
||||
List.class,
|
||||
Map.class,
|
||||
Objects.class,
|
||||
Streams.class,
|
||||
collectingAndThen(null, null),
|
||||
counting(),
|
||||
filtering(null, null),
|
||||
flatMapping(null, null),
|
||||
@@ -56,7 +66,9 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
|
||||
summarizingLong(null),
|
||||
summingDouble(null),
|
||||
summingInt(null),
|
||||
summingLong(null));
|
||||
summingLong(null),
|
||||
toImmutableList(),
|
||||
toImmutableMap(null, null));
|
||||
}
|
||||
|
||||
String testJoining() {
|
||||
@@ -109,21 +121,24 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Stream.of("bar").findFirst().map(String::length));
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testStreamIsEmpty() {
|
||||
ImmutableSet<Boolean> testStreamFindAnyIsEmpty() {
|
||||
return ImmutableSet.of(
|
||||
Stream.of(1).findAny().isEmpty(),
|
||||
Stream.of(2).findAny().isEmpty(),
|
||||
Stream.of(3).findAny().isEmpty(),
|
||||
Stream.of(4).findAny().isEmpty(),
|
||||
Stream.of(5).findAny().isEmpty());
|
||||
Stream.of(5).findAny().isEmpty(),
|
||||
Stream.of(6).findAny().isEmpty(),
|
||||
Stream.of(7).findAny().isEmpty(),
|
||||
Stream.of(8).findAny().isEmpty(),
|
||||
Stream.of(9).findAny().isEmpty(),
|
||||
!Stream.of(10).findAny().isEmpty(),
|
||||
!Stream.of(11).findAny().isEmpty(),
|
||||
!Stream.of(12).findAny().isEmpty());
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testStreamIsNotEmpty() {
|
||||
return ImmutableSet.of(
|
||||
Stream.of(1).findAny().isPresent(),
|
||||
Stream.of(2).findAny().isPresent(),
|
||||
Stream.of(3).findAny().isPresent(),
|
||||
Stream.of(4).findAny().isPresent());
|
||||
boolean testStreamFindAnyIsPresent() {
|
||||
return Stream.of(1).findAny().isPresent();
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<String>> testStreamMin() {
|
||||
|
||||
@@ -73,6 +73,18 @@ final class StringRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return Objects.toString("foo");
|
||||
}
|
||||
|
||||
ImmutableSet<String> testNewStringFromCharArraySubSequence() {
|
||||
return ImmutableSet.of(
|
||||
String.valueOf(new char[] {'f', 'o', 'o'}, 0, 1),
|
||||
String.copyValueOf(new char[] {'b', 'a', 'r'}, 2, 3));
|
||||
}
|
||||
|
||||
ImmutableSet<String> testNewStringFromCharArray() {
|
||||
return ImmutableSet.of(
|
||||
String.valueOf(new char[] {'f', 'o', 'o'}),
|
||||
new String(new char[] {'b', 'a', 'r'}, 0, new char[] {'b', 'a', 'r'}.length));
|
||||
}
|
||||
|
||||
Function<Object, String> testStringValueOfMethodReference() {
|
||||
return Objects::toString;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,16 @@ final class StringRulesTest implements RefasterRuleCollectionTestCase {
|
||||
return String.valueOf("foo");
|
||||
}
|
||||
|
||||
ImmutableSet<String> testNewStringFromCharArraySubSequence() {
|
||||
return ImmutableSet.of(
|
||||
new String(new char[] {'f', 'o', 'o'}, 0, 1), new String(new char[] {'b', 'a', 'r'}, 2, 3));
|
||||
}
|
||||
|
||||
ImmutableSet<String> testNewStringFromCharArray() {
|
||||
return ImmutableSet.of(
|
||||
new String(new char[] {'f', 'o', 'o'}), new String(new char[] {'b', 'a', 'r'}));
|
||||
}
|
||||
|
||||
Function<Object, String> testStringValueOfMethodReference() {
|
||||
return String::valueOf;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ import java.time.temporal.ChronoUnit;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
final class TimeRulesTest implements RefasterRuleCollectionTestCase {
|
||||
private static final ZonedDateTime ZONED_DATE_TIME = Instant.EPOCH.atZone(ZoneOffset.UTC);
|
||||
|
||||
@Override
|
||||
public ImmutableSet<Object> elidedTypesAndStaticImports() {
|
||||
return ImmutableSet.of(ChronoUnit.class);
|
||||
@@ -220,4 +222,385 @@ final class TimeRulesTest implements RefasterRuleCollectionTestCase {
|
||||
Period.ofYears(0),
|
||||
Period.of(0, 0, 0));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDatePlusDays() {
|
||||
return ImmutableSet.of(
|
||||
LocalDate.EPOCH.plus(1L, ChronoUnit.DAYS), LocalDate.EPOCH.plus(Period.ofDays(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDatePlusWeeks() {
|
||||
return ImmutableSet.of(
|
||||
LocalDate.EPOCH.plus(1L, ChronoUnit.WEEKS), LocalDate.EPOCH.plus(Period.ofWeeks(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDatePlusMonths() {
|
||||
return ImmutableSet.of(
|
||||
LocalDate.EPOCH.plus(1L, ChronoUnit.MONTHS), LocalDate.EPOCH.plus(Period.ofMonths(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDatePlusYears() {
|
||||
return ImmutableSet.of(
|
||||
LocalDate.EPOCH.plus(1L, ChronoUnit.YEARS), LocalDate.EPOCH.plus(Period.ofYears(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDateMinusDays() {
|
||||
return ImmutableSet.of(
|
||||
LocalDate.EPOCH.minus(1L, ChronoUnit.DAYS), LocalDate.EPOCH.minus(Period.ofDays(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDateMinusWeeks() {
|
||||
return ImmutableSet.of(
|
||||
LocalDate.EPOCH.minus(1L, ChronoUnit.WEEKS), LocalDate.EPOCH.minus(Period.ofWeeks(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDateMinusMonths() {
|
||||
return ImmutableSet.of(
|
||||
LocalDate.EPOCH.minus(1L, ChronoUnit.MONTHS), LocalDate.EPOCH.minus(Period.ofMonths(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDateMinusYears() {
|
||||
return ImmutableSet.of(
|
||||
LocalDate.EPOCH.minus(1L, ChronoUnit.YEARS), LocalDate.EPOCH.minus(Period.ofYears(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimePlusNanos() {
|
||||
return ImmutableSet.of(
|
||||
LocalTime.NOON.plus(1L, ChronoUnit.NANOS), LocalTime.NOON.plus(Duration.ofNanos(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimePlusSeconds() {
|
||||
return ImmutableSet.of(
|
||||
LocalTime.NOON.plus(1L, ChronoUnit.SECONDS), LocalTime.NOON.plus(Duration.ofSeconds(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimePlusMinutes() {
|
||||
return ImmutableSet.of(
|
||||
LocalTime.NOON.plus(1L, ChronoUnit.MINUTES), LocalTime.NOON.plus(Duration.ofMinutes(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimePlusHours() {
|
||||
return ImmutableSet.of(
|
||||
LocalTime.NOON.plus(1L, ChronoUnit.HOURS), LocalTime.NOON.plus(Duration.ofHours(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimeMinusNanos() {
|
||||
return ImmutableSet.of(
|
||||
LocalTime.NOON.minus(1L, ChronoUnit.NANOS), LocalTime.NOON.minus(Duration.ofNanos(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimeMinusSeconds() {
|
||||
return ImmutableSet.of(
|
||||
LocalTime.NOON.minus(1L, ChronoUnit.SECONDS), LocalTime.NOON.minus(Duration.ofSeconds(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimeMinusMinutes() {
|
||||
return ImmutableSet.of(
|
||||
LocalTime.NOON.minus(1L, ChronoUnit.MINUTES), LocalTime.NOON.minus(Duration.ofMinutes(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimeMinusHours() {
|
||||
return ImmutableSet.of(
|
||||
LocalTime.NOON.minus(1L, ChronoUnit.HOURS), LocalTime.NOON.minus(Duration.ofHours(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimePlusNanos() {
|
||||
return ImmutableSet.of(
|
||||
OffsetTime.MIN.plus(1L, ChronoUnit.NANOS), OffsetTime.MIN.plus(Duration.ofNanos(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimePlusSeconds() {
|
||||
return ImmutableSet.of(
|
||||
OffsetTime.MIN.plus(1L, ChronoUnit.SECONDS), OffsetTime.MIN.plus(Duration.ofSeconds(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimePlusMinutes() {
|
||||
return ImmutableSet.of(
|
||||
OffsetTime.MIN.plus(1L, ChronoUnit.MINUTES), OffsetTime.MIN.plus(Duration.ofMinutes(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimePlusHours() {
|
||||
return ImmutableSet.of(
|
||||
OffsetTime.MIN.plus(1L, ChronoUnit.HOURS), OffsetTime.MIN.plus(Duration.ofHours(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimeMinusNanos() {
|
||||
return ImmutableSet.of(
|
||||
OffsetTime.MAX.minus(1L, ChronoUnit.NANOS), OffsetTime.MAX.minus(Duration.ofNanos(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimeMinusSeconds() {
|
||||
return ImmutableSet.of(
|
||||
OffsetTime.MAX.minus(1L, ChronoUnit.SECONDS), OffsetTime.MAX.minus(Duration.ofSeconds(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimeMinusMinutes() {
|
||||
return ImmutableSet.of(
|
||||
OffsetTime.MAX.minus(1L, ChronoUnit.MINUTES), OffsetTime.MAX.minus(Duration.ofMinutes(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimeMinusHours() {
|
||||
return ImmutableSet.of(
|
||||
OffsetTime.MAX.minus(1L, ChronoUnit.HOURS), OffsetTime.MAX.minus(Duration.ofHours(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusNanos() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MIN.plus(1L, ChronoUnit.NANOS), LocalDateTime.MIN.plus(Duration.ofNanos(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusSeconds() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MIN.plus(1L, ChronoUnit.SECONDS),
|
||||
LocalDateTime.MIN.plus(Duration.ofSeconds(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusMinutes() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MIN.plus(1L, ChronoUnit.MINUTES),
|
||||
LocalDateTime.MIN.plus(Duration.ofMinutes(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusHours() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MIN.plus(1L, ChronoUnit.HOURS), LocalDateTime.MIN.plus(Duration.ofHours(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusDays() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MIN.plus(1L, ChronoUnit.DAYS), LocalDateTime.MIN.plus(Period.ofDays(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusWeeks() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MIN.plus(1L, ChronoUnit.WEEKS), LocalDateTime.MIN.plus(Period.ofWeeks(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusMonths() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MIN.plus(1L, ChronoUnit.MONTHS), LocalDateTime.MIN.plus(Period.ofMonths(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusYears() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MIN.plus(1L, ChronoUnit.YEARS), LocalDateTime.MIN.plus(Period.ofYears(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusNanos() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MAX.minus(1L, ChronoUnit.NANOS),
|
||||
LocalDateTime.MAX.minus(Duration.ofNanos(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusSeconds() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MAX.minus(1L, ChronoUnit.SECONDS),
|
||||
LocalDateTime.MAX.minus(Duration.ofSeconds(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusMinutes() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MAX.minus(1L, ChronoUnit.MINUTES),
|
||||
LocalDateTime.MAX.minus(Duration.ofMinutes(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusHours() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MAX.minus(1L, ChronoUnit.HOURS),
|
||||
LocalDateTime.MAX.minus(Duration.ofHours(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusDays() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MAX.minus(1L, ChronoUnit.DAYS), LocalDateTime.MAX.minus(Period.ofDays(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusWeeks() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MAX.minus(1L, ChronoUnit.WEEKS), LocalDateTime.MAX.minus(Period.ofWeeks(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusMonths() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MAX.minus(1L, ChronoUnit.MONTHS),
|
||||
LocalDateTime.MAX.minus(Period.ofMonths(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusYears() {
|
||||
return ImmutableSet.of(
|
||||
LocalDateTime.MAX.minus(1L, ChronoUnit.YEARS), LocalDateTime.MAX.minus(Period.ofYears(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusNanos() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MIN.plus(1L, ChronoUnit.NANOS),
|
||||
OffsetDateTime.MIN.plus(Duration.ofNanos(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusSeconds() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MIN.plus(1L, ChronoUnit.SECONDS),
|
||||
OffsetDateTime.MIN.plus(Duration.ofSeconds(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusMinutes() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MIN.plus(1L, ChronoUnit.MINUTES),
|
||||
OffsetDateTime.MIN.plus(Duration.ofMinutes(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusHours() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MIN.plus(1L, ChronoUnit.HOURS),
|
||||
OffsetDateTime.MIN.plus(Duration.ofHours(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusDays() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MIN.plus(1L, ChronoUnit.DAYS), OffsetDateTime.MIN.plus(Period.ofDays(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusWeeks() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MIN.plus(1L, ChronoUnit.WEEKS), OffsetDateTime.MIN.plus(Period.ofWeeks(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusMonths() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MIN.plus(1L, ChronoUnit.MONTHS),
|
||||
OffsetDateTime.MIN.plus(Period.ofMonths(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusYears() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MIN.plus(1L, ChronoUnit.YEARS), OffsetDateTime.MIN.plus(Period.ofYears(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusNanos() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MAX.minus(1L, ChronoUnit.NANOS),
|
||||
OffsetDateTime.MAX.minus(Duration.ofNanos(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusSeconds() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MAX.minus(1L, ChronoUnit.SECONDS),
|
||||
OffsetDateTime.MAX.minus(Duration.ofSeconds(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusMinutes() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MAX.minus(1L, ChronoUnit.MINUTES),
|
||||
OffsetDateTime.MAX.minus(Duration.ofMinutes(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusHours() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MAX.minus(1L, ChronoUnit.HOURS),
|
||||
OffsetDateTime.MAX.minus(Duration.ofHours(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusDays() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MAX.minus(1L, ChronoUnit.DAYS), OffsetDateTime.MAX.minus(Period.ofDays(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusWeeks() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MAX.minus(1L, ChronoUnit.WEEKS),
|
||||
OffsetDateTime.MAX.minus(Period.ofWeeks(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusMonths() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MAX.minus(1L, ChronoUnit.MONTHS),
|
||||
OffsetDateTime.MAX.minus(Period.ofMonths(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusYears() {
|
||||
return ImmutableSet.of(
|
||||
OffsetDateTime.MAX.minus(1L, ChronoUnit.YEARS),
|
||||
OffsetDateTime.MAX.minus(Period.ofYears(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusNanos() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.plus(1L, ChronoUnit.NANOS), ZONED_DATE_TIME.plus(Duration.ofNanos(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusSeconds() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.plus(1L, ChronoUnit.SECONDS), ZONED_DATE_TIME.plus(Duration.ofSeconds(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusMinutes() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.plus(1L, ChronoUnit.MINUTES), ZONED_DATE_TIME.plus(Duration.ofMinutes(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusHours() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.plus(1L, ChronoUnit.HOURS), ZONED_DATE_TIME.plus(Duration.ofHours(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusDays() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.plus(1L, ChronoUnit.DAYS), ZONED_DATE_TIME.plus(Period.ofDays(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusWeeks() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.plus(1L, ChronoUnit.WEEKS), ZONED_DATE_TIME.plus(Period.ofWeeks(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusMonths() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.plus(1L, ChronoUnit.MONTHS), ZONED_DATE_TIME.plus(Period.ofMonths(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusYears() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.plus(1L, ChronoUnit.YEARS), ZONED_DATE_TIME.plus(Period.ofYears(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusNanos() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.minus(1L, ChronoUnit.NANOS), ZONED_DATE_TIME.minus(Duration.ofNanos(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusSeconds() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.minus(1L, ChronoUnit.SECONDS),
|
||||
ZONED_DATE_TIME.minus(Duration.ofSeconds(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusMinutes() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.minus(1L, ChronoUnit.MINUTES),
|
||||
ZONED_DATE_TIME.minus(Duration.ofMinutes(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusHours() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.minus(1L, ChronoUnit.HOURS), ZONED_DATE_TIME.minus(Duration.ofHours(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusDays() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.minus(1L, ChronoUnit.DAYS), ZONED_DATE_TIME.minus(Period.ofDays(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusWeeks() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.minus(1L, ChronoUnit.WEEKS), ZONED_DATE_TIME.minus(Period.ofWeeks(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusMonths() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.minus(1L, ChronoUnit.MONTHS), ZONED_DATE_TIME.minus(Period.ofMonths(1)));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusYears() {
|
||||
return ImmutableSet.of(
|
||||
ZONED_DATE_TIME.minus(1L, ChronoUnit.YEARS), ZONED_DATE_TIME.minus(Period.ofYears(1)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ import java.time.temporal.ChronoUnit;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
final class TimeRulesTest implements RefasterRuleCollectionTestCase {
|
||||
private static final ZonedDateTime ZONED_DATE_TIME = Instant.EPOCH.atZone(ZoneOffset.UTC);
|
||||
|
||||
@Override
|
||||
public ImmutableSet<Object> elidedTypesAndStaticImports() {
|
||||
return ImmutableSet.of(ChronoUnit.class);
|
||||
@@ -201,4 +203,292 @@ final class TimeRulesTest implements RefasterRuleCollectionTestCase {
|
||||
ImmutableSet<Period> testZeroPeriod() {
|
||||
return ImmutableSet.of(Period.ZERO, Period.ZERO, Period.ZERO, Period.ZERO, Period.ZERO);
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDatePlusDays() {
|
||||
return ImmutableSet.of(LocalDate.EPOCH.plusDays(1L), LocalDate.EPOCH.plusDays(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDatePlusWeeks() {
|
||||
return ImmutableSet.of(LocalDate.EPOCH.plusWeeks(1L), LocalDate.EPOCH.plusWeeks(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDatePlusMonths() {
|
||||
return ImmutableSet.of(LocalDate.EPOCH.plusMonths(1L), LocalDate.EPOCH.plusMonths(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDatePlusYears() {
|
||||
return ImmutableSet.of(LocalDate.EPOCH.plusYears(1L), LocalDate.EPOCH.plusYears(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDateMinusDays() {
|
||||
return ImmutableSet.of(LocalDate.EPOCH.minusDays(1L), LocalDate.EPOCH.minusDays(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDateMinusWeeks() {
|
||||
return ImmutableSet.of(LocalDate.EPOCH.minusWeeks(1L), LocalDate.EPOCH.minusWeeks(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDateMinusMonths() {
|
||||
return ImmutableSet.of(LocalDate.EPOCH.minusMonths(1L), LocalDate.EPOCH.minusMonths(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDate> testLocalDateMinusYears() {
|
||||
return ImmutableSet.of(LocalDate.EPOCH.minusYears(1L), LocalDate.EPOCH.minusYears(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimePlusNanos() {
|
||||
return ImmutableSet.of(LocalTime.NOON.plusNanos(1L), LocalTime.NOON.plusNanos(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimePlusSeconds() {
|
||||
return ImmutableSet.of(LocalTime.NOON.plusSeconds(1L), LocalTime.NOON.plusSeconds(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimePlusMinutes() {
|
||||
return ImmutableSet.of(LocalTime.NOON.plusMinutes(1L), LocalTime.NOON.plusMinutes(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimePlusHours() {
|
||||
return ImmutableSet.of(LocalTime.NOON.plusHours(1L), LocalTime.NOON.plusHours(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimeMinusNanos() {
|
||||
return ImmutableSet.of(LocalTime.NOON.minusNanos(1L), LocalTime.NOON.minusNanos(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimeMinusSeconds() {
|
||||
return ImmutableSet.of(LocalTime.NOON.minusSeconds(1L), LocalTime.NOON.minusSeconds(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimeMinusMinutes() {
|
||||
return ImmutableSet.of(LocalTime.NOON.minusMinutes(1L), LocalTime.NOON.minusMinutes(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalTime> testLocalTimeMinusHours() {
|
||||
return ImmutableSet.of(LocalTime.NOON.minusHours(1L), LocalTime.NOON.minusHours(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimePlusNanos() {
|
||||
return ImmutableSet.of(OffsetTime.MIN.plusNanos(1L), OffsetTime.MIN.plusNanos(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimePlusSeconds() {
|
||||
return ImmutableSet.of(OffsetTime.MIN.plusSeconds(1L), OffsetTime.MIN.plusSeconds(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimePlusMinutes() {
|
||||
return ImmutableSet.of(OffsetTime.MIN.plusMinutes(1L), OffsetTime.MIN.plusMinutes(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimePlusHours() {
|
||||
return ImmutableSet.of(OffsetTime.MIN.plusHours(1L), OffsetTime.MIN.plusHours(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimeMinusNanos() {
|
||||
return ImmutableSet.of(OffsetTime.MAX.minusNanos(1L), OffsetTime.MAX.minusNanos(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimeMinusSeconds() {
|
||||
return ImmutableSet.of(OffsetTime.MAX.minusSeconds(1L), OffsetTime.MAX.minusSeconds(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimeMinusMinutes() {
|
||||
return ImmutableSet.of(OffsetTime.MAX.minusMinutes(1L), OffsetTime.MAX.minusMinutes(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetTime> testOffsetTimeMinusHours() {
|
||||
return ImmutableSet.of(OffsetTime.MAX.minusHours(1L), OffsetTime.MAX.minusHours(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusNanos() {
|
||||
return ImmutableSet.of(LocalDateTime.MIN.plusNanos(1L), LocalDateTime.MIN.plusNanos(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusSeconds() {
|
||||
return ImmutableSet.of(LocalDateTime.MIN.plusSeconds(1L), LocalDateTime.MIN.plusSeconds(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusMinutes() {
|
||||
return ImmutableSet.of(LocalDateTime.MIN.plusMinutes(1L), LocalDateTime.MIN.plusMinutes(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusHours() {
|
||||
return ImmutableSet.of(LocalDateTime.MIN.plusHours(1L), LocalDateTime.MIN.plusHours(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusDays() {
|
||||
return ImmutableSet.of(LocalDateTime.MIN.plusDays(1L), LocalDateTime.MIN.plusDays(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusWeeks() {
|
||||
return ImmutableSet.of(LocalDateTime.MIN.plusWeeks(1L), LocalDateTime.MIN.plusWeeks(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusMonths() {
|
||||
return ImmutableSet.of(LocalDateTime.MIN.plusMonths(1L), LocalDateTime.MIN.plusMonths(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimePlusYears() {
|
||||
return ImmutableSet.of(LocalDateTime.MIN.plusYears(1L), LocalDateTime.MIN.plusYears(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusNanos() {
|
||||
return ImmutableSet.of(LocalDateTime.MAX.minusNanos(1L), LocalDateTime.MAX.minusNanos(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusSeconds() {
|
||||
return ImmutableSet.of(LocalDateTime.MAX.minusSeconds(1L), LocalDateTime.MAX.minusSeconds(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusMinutes() {
|
||||
return ImmutableSet.of(LocalDateTime.MAX.minusMinutes(1L), LocalDateTime.MAX.minusMinutes(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusHours() {
|
||||
return ImmutableSet.of(LocalDateTime.MAX.minusHours(1L), LocalDateTime.MAX.minusHours(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusDays() {
|
||||
return ImmutableSet.of(LocalDateTime.MAX.minusDays(1L), LocalDateTime.MAX.minusDays(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusWeeks() {
|
||||
return ImmutableSet.of(LocalDateTime.MAX.minusWeeks(1L), LocalDateTime.MAX.minusWeeks(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusMonths() {
|
||||
return ImmutableSet.of(LocalDateTime.MAX.minusMonths(1L), LocalDateTime.MAX.minusMonths(1));
|
||||
}
|
||||
|
||||
ImmutableSet<LocalDateTime> testLocalDateTimeMinusYears() {
|
||||
return ImmutableSet.of(LocalDateTime.MAX.minusYears(1L), LocalDateTime.MAX.minusYears(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusNanos() {
|
||||
return ImmutableSet.of(OffsetDateTime.MIN.plusNanos(1L), OffsetDateTime.MIN.plusNanos(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusSeconds() {
|
||||
return ImmutableSet.of(OffsetDateTime.MIN.plusSeconds(1L), OffsetDateTime.MIN.plusSeconds(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusMinutes() {
|
||||
return ImmutableSet.of(OffsetDateTime.MIN.plusMinutes(1L), OffsetDateTime.MIN.plusMinutes(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusHours() {
|
||||
return ImmutableSet.of(OffsetDateTime.MIN.plusHours(1L), OffsetDateTime.MIN.plusHours(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusDays() {
|
||||
return ImmutableSet.of(OffsetDateTime.MIN.plusDays(1L), OffsetDateTime.MIN.plusDays(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusWeeks() {
|
||||
return ImmutableSet.of(OffsetDateTime.MIN.plusWeeks(1L), OffsetDateTime.MIN.plusWeeks(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusMonths() {
|
||||
return ImmutableSet.of(OffsetDateTime.MIN.plusMonths(1L), OffsetDateTime.MIN.plusMonths(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimePlusYears() {
|
||||
return ImmutableSet.of(OffsetDateTime.MIN.plusYears(1L), OffsetDateTime.MIN.plusYears(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusNanos() {
|
||||
return ImmutableSet.of(OffsetDateTime.MAX.minusNanos(1L), OffsetDateTime.MAX.minusNanos(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusSeconds() {
|
||||
return ImmutableSet.of(OffsetDateTime.MAX.minusSeconds(1L), OffsetDateTime.MAX.minusSeconds(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusMinutes() {
|
||||
return ImmutableSet.of(OffsetDateTime.MAX.minusMinutes(1L), OffsetDateTime.MAX.minusMinutes(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusHours() {
|
||||
return ImmutableSet.of(OffsetDateTime.MAX.minusHours(1L), OffsetDateTime.MAX.minusHours(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusDays() {
|
||||
return ImmutableSet.of(OffsetDateTime.MAX.minusDays(1L), OffsetDateTime.MAX.minusDays(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusWeeks() {
|
||||
return ImmutableSet.of(OffsetDateTime.MAX.minusWeeks(1L), OffsetDateTime.MAX.minusWeeks(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusMonths() {
|
||||
return ImmutableSet.of(OffsetDateTime.MAX.minusMonths(1L), OffsetDateTime.MAX.minusMonths(1));
|
||||
}
|
||||
|
||||
ImmutableSet<OffsetDateTime> testOffsetDateTimeMinusYears() {
|
||||
return ImmutableSet.of(OffsetDateTime.MAX.minusYears(1L), OffsetDateTime.MAX.minusYears(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusNanos() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.plusNanos(1L), ZONED_DATE_TIME.plusNanos(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusSeconds() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.plusSeconds(1L), ZONED_DATE_TIME.plusSeconds(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusMinutes() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.plusMinutes(1L), ZONED_DATE_TIME.plusMinutes(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusHours() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.plusHours(1L), ZONED_DATE_TIME.plusHours(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusDays() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.plusDays(1L), ZONED_DATE_TIME.plusDays(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusWeeks() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.plusWeeks(1L), ZONED_DATE_TIME.plusWeeks(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusMonths() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.plusMonths(1L), ZONED_DATE_TIME.plusMonths(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimePlusYears() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.plusYears(1L), ZONED_DATE_TIME.plusYears(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusNanos() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.minusNanos(1L), ZONED_DATE_TIME.minusNanos(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusSeconds() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.minusSeconds(1L), ZONED_DATE_TIME.minusSeconds(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusMinutes() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.minusMinutes(1L), ZONED_DATE_TIME.minusMinutes(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusHours() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.minusHours(1L), ZONED_DATE_TIME.minusHours(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusDays() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.minusDays(1L), ZONED_DATE_TIME.minusDays(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusWeeks() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.minusWeeks(1L), ZONED_DATE_TIME.minusWeeks(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusMonths() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.minusMonths(1L), ZONED_DATE_TIME.minusMonths(1));
|
||||
}
|
||||
|
||||
ImmutableSet<ZonedDateTime> testZonedDateTimeMinusYears() {
|
||||
return ImmutableSet.of(ZONED_DATE_TIME.minusYears(1L), ZONED_DATE_TIME.minusYears(1));
|
||||
}
|
||||
}
|
||||
|
||||
6
error-prone-experimental/README.md
Normal file
6
error-prone-experimental/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Experimental Error Prone checks
|
||||
|
||||
This module contains Error Prone checks that are currently under development or
|
||||
evaluation. These checks may be works-in-progress or have uncertain impact on
|
||||
code. Having this module allows for controlled experimentation and refinement
|
||||
before integration into production environments.
|
||||
71
error-prone-experimental/pom.xml
Normal file
71
error-prone-experimental/pom.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.16.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>error-prone-experimental</artifactId>
|
||||
|
||||
<name>Picnic :: Error Prone Support :: Experimental</name>
|
||||
<description>Experimental Error Prone checks.</description>
|
||||
<url>https://error-prone.picnic.tech</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${groupId.error-prone}</groupId>
|
||||
<artifactId>error_prone_annotation</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${groupId.error-prone}</groupId>
|
||||
<artifactId>error_prone_annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${groupId.error-prone}</groupId>
|
||||
<artifactId>error_prone_check_api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${groupId.error-prone}</groupId>
|
||||
<artifactId>error_prone_test_helpers</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>error-prone-utils</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto.service</groupId>
|
||||
<artifactId>auto-service-annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jspecify</groupId>
|
||||
<artifactId>jspecify</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<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>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,10 +1,10 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.experimental.bugpatterns;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.STYLE;
|
||||
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;
|
||||
@@ -27,7 +27,6 @@ import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.ParenthesizedTree;
|
||||
import com.sun.source.tree.ReturnTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import com.sun.source.tree.VariableTree;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
@@ -37,8 +36,6 @@ import javax.lang.model.element.Name;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags lambda expressions that can be replaced with method references.
|
||||
*
|
||||
* @see IsInstanceLambdaUsage
|
||||
*/
|
||||
// XXX: Other custom expressions we could rewrite:
|
||||
// - `a -> "str" + a` to `"str"::concat`. But only if `str` is provably non-null.
|
||||
@@ -54,7 +51,8 @@ import javax.lang.model.element.Name;
|
||||
// Palantir's `LambdaMethodReference` check seems to suffer a similar issue at this time.
|
||||
// XXX: Expressions of the form `i -> SomeType.class.isInstance(i)` are not replaced; fix that using
|
||||
// a suitable generalization.
|
||||
// XXX: Consider folding the `IsInstanceLambdaUsage` check into this class.
|
||||
// XXX: Consider folding the `IsInstanceLambdaUsage` check of the `error-prone-contrib` module into
|
||||
// this class.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Prefer method references over lambda expressions",
|
||||
@@ -85,22 +83,19 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
|
||||
.orElse(Description.NO_MATCH);
|
||||
}
|
||||
|
||||
// XXX: Use switch pattern matching once the targeted JDK supports this.
|
||||
private static Optional<SuggestedFix.Builder> constructMethodRef(
|
||||
LambdaExpressionTree lambdaExpr, Tree subTree) {
|
||||
switch (subTree.getKind()) {
|
||||
case BLOCK:
|
||||
return constructMethodRef(lambdaExpr, (BlockTree) subTree);
|
||||
case EXPRESSION_STATEMENT:
|
||||
return constructMethodRef(lambdaExpr, ((ExpressionStatementTree) subTree).getExpression());
|
||||
case METHOD_INVOCATION:
|
||||
return constructMethodRef(lambdaExpr, (MethodInvocationTree) subTree);
|
||||
case PARENTHESIZED:
|
||||
return constructMethodRef(lambdaExpr, ((ParenthesizedTree) subTree).getExpression());
|
||||
case RETURN:
|
||||
return constructMethodRef(lambdaExpr, ((ReturnTree) subTree).getExpression());
|
||||
default:
|
||||
return Optional.empty();
|
||||
}
|
||||
return switch (subTree.getKind()) {
|
||||
case BLOCK -> constructMethodRef(lambdaExpr, (BlockTree) subTree);
|
||||
case EXPRESSION_STATEMENT ->
|
||||
constructMethodRef(lambdaExpr, ((ExpressionStatementTree) subTree).getExpression());
|
||||
case METHOD_INVOCATION -> constructMethodRef(lambdaExpr, (MethodInvocationTree) subTree);
|
||||
case PARENTHESIZED ->
|
||||
constructMethodRef(lambdaExpr, ((ParenthesizedTree) subTree).getExpression());
|
||||
case RETURN -> constructMethodRef(lambdaExpr, ((ReturnTree) subTree).getExpression());
|
||||
default -> Optional.empty();
|
||||
};
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix.Builder> constructMethodRef(
|
||||
@@ -118,33 +113,35 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
|
||||
.flatMap(expectedInstance -> constructMethodRef(lambdaExpr, subTree, expectedInstance));
|
||||
}
|
||||
|
||||
@SuppressWarnings(
|
||||
"java:S1151" /* Extracting `IDENTIFIER` case block to separate method does not improve readability. */)
|
||||
// XXX: Review whether to use switch pattern matching once the targeted JDK supports this.
|
||||
private static Optional<SuggestedFix.Builder> constructMethodRef(
|
||||
LambdaExpressionTree lambdaExpr,
|
||||
MethodInvocationTree subTree,
|
||||
Optional<Name> expectedInstance) {
|
||||
ExpressionTree methodSelect = subTree.getMethodSelect();
|
||||
switch (methodSelect.getKind()) {
|
||||
case IDENTIFIER:
|
||||
if (expectedInstance.isPresent()) {
|
||||
/* Direct method call; there is no matching "implicit parameter". */
|
||||
return Optional.empty();
|
||||
}
|
||||
Symbol sym = ASTHelpers.getSymbol(methodSelect);
|
||||
return ASTHelpers.isStatic(sym)
|
||||
? constructFix(lambdaExpr, sym.owner, methodSelect)
|
||||
: constructFix(lambdaExpr, "this", methodSelect);
|
||||
case MEMBER_SELECT:
|
||||
return constructMethodRef(lambdaExpr, (MemberSelectTree) methodSelect, expectedInstance);
|
||||
default:
|
||||
throw new VerifyException("Unexpected type of expression: " + methodSelect.getKind());
|
||||
|
||||
if (methodSelect instanceof IdentifierTree) {
|
||||
if (expectedInstance.isPresent()) {
|
||||
/* Direct method call; there is no matching "implicit parameter". */
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Symbol sym = ASTHelpers.getSymbol(methodSelect);
|
||||
return ASTHelpers.isStatic(sym)
|
||||
? constructFix(lambdaExpr, sym.owner, methodSelect)
|
||||
: constructFix(lambdaExpr, "this", methodSelect);
|
||||
}
|
||||
|
||||
if (methodSelect instanceof MemberSelectTree memberSelect) {
|
||||
return constructMethodRef(lambdaExpr, memberSelect, expectedInstance);
|
||||
}
|
||||
|
||||
throw new VerifyException("Unexpected type of expression: " + methodSelect.getKind());
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix.Builder> constructMethodRef(
|
||||
LambdaExpressionTree lambdaExpr, MemberSelectTree subTree, Optional<Name> expectedInstance) {
|
||||
if (subTree.getExpression().getKind() != Kind.IDENTIFIER) {
|
||||
if (!(subTree.getExpression() instanceof IdentifierTree identifier)) {
|
||||
// XXX: Could be parenthesized. Handle. Also in other classes.
|
||||
/*
|
||||
* Only suggest a replacement if the method select's expression provably doesn't have
|
||||
@@ -153,12 +150,12 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Name lhs = ((IdentifierTree) subTree.getExpression()).getName();
|
||||
Name lhs = identifier.getName();
|
||||
if (expectedInstance.isEmpty()) {
|
||||
return constructFix(lambdaExpr, lhs, subTree.getIdentifier());
|
||||
}
|
||||
|
||||
Type lhsType = ASTHelpers.getType(subTree.getExpression());
|
||||
Type lhsType = ASTHelpers.getType(identifier);
|
||||
if (lhsType == null || !expectedInstance.orElseThrow().equals(lhs)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -183,8 +180,8 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
|
||||
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
ExpressionTree arg = args.get(i);
|
||||
if (arg.getKind() != Kind.IDENTIFIER
|
||||
|| !((IdentifierTree) arg).getName().equals(expectedArguments.get(i + diff))) {
|
||||
if (!(arg instanceof IdentifierTree identifier)
|
||||
|| !identifier.getName().equals(expectedArguments.get(i + diff))) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/** Experimental Error Prone checks. */
|
||||
@com.google.errorprone.annotations.CheckReturnValue
|
||||
@org.jspecify.annotations.NullMarked
|
||||
package tech.picnic.errorprone.experimental.bugpatterns;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user