mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 08:11:25 +00:00
Compare commits
67 Commits
sschroever
...
sschroever
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf80180103 | ||
|
|
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
|
||||
|
||||
|
||||
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@@ -10,16 +10,16 @@ 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 }}
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
# additionally enabling all checks defined in this project and any Error
|
||||
# Prone checks available only from other artifact repositories.
|
||||
- name: Check out code and set up JDK and Maven
|
||||
uses: s4u/setup-maven-action@fa2c7e4517ed008b1f73e7e0195a9eecf5582cd4 # v1.11.0
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
with:
|
||||
java-version: ${{ matrix.jdk }}
|
||||
java-distribution: ${{ matrix.distribution }}
|
||||
|
||||
8
.github/workflows/codeql.yml
vendored
8
.github/workflows/codeql.yml
vendored
@@ -22,19 +22,19 @@ jobs:
|
||||
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
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
with:
|
||||
java-version: 17.0.8
|
||||
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@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3.23.2
|
||||
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@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3.23.2
|
||||
with:
|
||||
category: /language:${{ matrix.language }}
|
||||
|
||||
6
.github/workflows/deploy-website.yml
vendored
6
.github/workflows/deploy-website.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: ruby/setup-ruby@360dc864d5da99d54fcb8e9148c14a84b90d3e88 # v1.165.1
|
||||
- uses: ruby/setup-ruby@bd03e04863f52d169e18a2b190e8fa6b84938215 # v1.170.0
|
||||
with:
|
||||
working-directory: ./website
|
||||
bundler-cache: true
|
||||
@@ -32,7 +32,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:
|
||||
@@ -48,4 +48,4 @@ jobs:
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@87c3283f01cd6fe19a0ab93a23b2f6fcba5a8e42 # v4.0.3
|
||||
uses: actions/deploy-pages@decdde0ac072f6dcbe43649d82d9c635fff5b4e4 # v4.0.4
|
||||
|
||||
2
.github/workflows/openssf-scorecard.yml
vendored
2
.github/workflows/openssf-scorecard.yml
vendored
@@ -31,6 +31,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@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3.23.2
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
6
.github/workflows/pitest-analyze-pr.yml
vendored
6
.github/workflows/pitest-analyze-pr.yml
vendored
@@ -12,10 +12,10 @@ jobs:
|
||||
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
|
||||
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 +28,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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: pitest-reports
|
||||
path: ./target/pit-reports-ci
|
||||
|
||||
6
.github/workflows/pitest-update-pr.yml
vendored
6
.github/workflows/pitest-update-pr.yml
vendored
@@ -20,13 +20,13 @@ jobs:
|
||||
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
|
||||
uses: s4u/setup-maven-action@6d44c18d67d9e1549907b8815efa5e4dada1801b # v1.12.0
|
||||
with:
|
||||
java-version: 17.0.8
|
||||
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@72aaadce3bc708349fc665eee3785cbb1b6e51d0 # v3.1.1
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
name: pitest-reports
|
||||
|
||||
10
.github/workflows/run-integration-tests.yml
vendored
10
.github/workflows/run-integration-tests.yml
vendored
@@ -19,21 +19,21 @@ jobs:
|
||||
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
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: integration-test-checkstyle-10.12.4
|
||||
name: integration-test-checkstyle
|
||||
path: "${{ runner.temp }}/artifacts"
|
||||
- name: Remove installed project artifacts
|
||||
run: mvn build-helper:remove-project-artifact
|
||||
|
||||
6
.github/workflows/sonarcloud.yml
vendored
6
.github/workflows/sonarcloud.yml
vendored
@@ -13,16 +13,16 @@ 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: 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
|
||||
|
||||
@@ -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.15.1-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.15.1-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>
|
||||
@@ -239,6 +239,11 @@
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
@@ -274,7 +279,6 @@
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs combine.children="append">
|
||||
<arg>-Xplugin:RefasterRuleCompiler</arg>
|
||||
<arg>-Xplugin:DocumentationGenerator -XoutputDirectory=${project.build.directory}/docs</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
|
||||
@@ -4,7 +4,7 @@ import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.LIKELY_ERROR;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -18,7 +18,7 @@ import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import java.util.Map;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
@@ -46,7 +46,7 @@ public final class AmbiguousJsonCreator extends BugChecker implements Annotation
|
||||
}
|
||||
|
||||
ClassTree clazz = state.findEnclosing(ClassTree.class);
|
||||
if (clazz == null || clazz.getKind() != Tree.Kind.ENUM) {
|
||||
if (clazz == null || clazz.getKind() != Kind.ENUM) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import static com.google.errorprone.matchers.Matchers.argument;
|
||||
import static com.google.errorprone.matchers.Matchers.argumentCount;
|
||||
import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.nullLiteral;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
|
||||
@@ -6,7 +6,7 @@ import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
|
||||
import static com.google.errorprone.matchers.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
|
||||
import static com.google.errorprone.matchers.Matchers.annotations;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -23,7 +23,7 @@ import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import java.util.List;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} that flags redundant {@code @Autowired} constructor annotations. */
|
||||
@AutoService(BugChecker.class)
|
||||
|
||||
@@ -3,7 +3,7 @@ package tech.picnic.errorprone.bugpatterns;
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -19,14 +19,13 @@ import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.AssignmentTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.NewArrayTree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} that flags annotations that could be written more concisely. */
|
||||
@AutoService(BugChecker.class)
|
||||
@@ -119,7 +118,7 @@ public final class CanonicalAnnotationSyntax extends BugChecker implements Annot
|
||||
* the expression as a whole.
|
||||
*/
|
||||
ExpressionTree value =
|
||||
(arg.getKind() == Kind.ASSIGNMENT) ? ((AssignmentTree) arg).getExpression() : arg;
|
||||
(arg instanceof AssignmentTree assignment) ? assignment.getExpression() : arg;
|
||||
|
||||
/* Store a fix for each expression that was successfully simplified. */
|
||||
simplifyAttributeValue(value, state)
|
||||
@@ -130,13 +129,10 @@ public final class CanonicalAnnotationSyntax extends BugChecker implements Annot
|
||||
}
|
||||
|
||||
private static Optional<String> simplifyAttributeValue(ExpressionTree expr, VisitorState state) {
|
||||
if (expr.getKind() != Kind.NEW_ARRAY) {
|
||||
/* There are no curly braces or commas to be dropped here. */
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
NewArrayTree array = (NewArrayTree) expr;
|
||||
return simplifySingletonArray(array, state).or(() -> dropTrailingComma(array, state));
|
||||
/* Drop curly braces or commas if possible. */
|
||||
return expr instanceof NewArrayTree newArray
|
||||
? simplifySingletonArray(newArray, state).or(() -> dropTrailingComma(newArray, state))
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
/** Returns the expression describing the array's sole element, if any. */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
@@ -122,13 +124,9 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
}
|
||||
|
||||
private static Optional<NewArrayTree> extractArray(ExpressionTree expr) {
|
||||
if (expr.getKind() == Kind.ASSIGNMENT) {
|
||||
return extractArray(((AssignmentTree) expr).getExpression());
|
||||
}
|
||||
|
||||
return Optional.of(expr)
|
||||
.filter(e -> e.getKind() == Kind.NEW_ARRAY)
|
||||
.map(NewArrayTree.class::cast);
|
||||
return expr instanceof AssignmentTree assignment
|
||||
? extractArray(assignment.getExpression())
|
||||
: Optional.of(expr).filter(NewArrayTree.class::isInstance).map(NewArrayTree.class::cast);
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix.Builder> suggestSorting(
|
||||
@@ -200,8 +198,8 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
public @Nullable Void visitLiteral(LiteralTree node, @Nullable Void unused) {
|
||||
Object value = ASTHelpers.constValue(node);
|
||||
nodes.add(
|
||||
value instanceof String
|
||||
? STRING_ARGUMENT_SPLITTER.splitToStream((String) value).collect(toImmutableList())
|
||||
value instanceof String str
|
||||
? STRING_ARGUMENT_SPLITTER.splitToStream(str).collect(toImmutableList())
|
||||
: ImmutableList.of(String.valueOf(value)));
|
||||
|
||||
return super.visitLiteral(node, unused);
|
||||
|
||||
@@ -7,7 +7,7 @@ import static com.google.errorprone.BugPattern.StandardTags.STYLE;
|
||||
import static com.sun.tools.javac.code.TypeAnnotations.AnnotationType.DECLARATION;
|
||||
import static com.sun.tools.javac.code.TypeAnnotations.AnnotationType.TYPE;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.VerifyException;
|
||||
@@ -28,7 +28,7 @@ import com.sun.tools.javac.code.TypeAnnotations.AnnotationType;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags annotations that are not lexicographically sorted.
|
||||
@@ -36,6 +36,10 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
* <p>The idea behind this checker is that maintaining a sorted sequence simplifies conflict
|
||||
* resolution, and can even avoid it if two branches add the same annotation.
|
||||
*/
|
||||
// XXX: Currently this checker only flags method-level annotations. It should likely also flag
|
||||
// type-, field- and parameter-level annotations.
|
||||
// XXX: Duplicate entries are often a mistake. Consider introducing a similar `BugChecker` that
|
||||
// flags duplicates.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Sort annotations lexicographically where possible",
|
||||
|
||||
@@ -9,7 +9,7 @@ import static com.google.errorprone.matchers.Matchers.isSameType;
|
||||
import static com.google.errorprone.matchers.Matchers.isVariable;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
import static com.google.errorprone.matchers.Matchers.staticMethod;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -25,7 +25,7 @@ import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.tree.VariableTree;
|
||||
import java.util.List;
|
||||
import tech.picnic.errorprone.bugpatterns.util.MoreASTHelpers;
|
||||
import tech.picnic.errorprone.utils.MoreASTHelpers;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags the use of {@link org.mockito.Mockito#mock(Class)} and {@link
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
@@ -44,6 +44,7 @@ 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;
|
||||
@@ -132,7 +133,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 +210,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,
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
@@ -35,6 +36,18 @@ import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
final class AssortedRules {
|
||||
private AssortedRules() {}
|
||||
|
||||
final class IllegalStateExceptionSupplier {
|
||||
@BeforeTemplate
|
||||
Supplier<IllegalStateException> before(String message) {
|
||||
return () -> new IllegalStateException(message);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Supplier<IllegalStateException> after(String message) {
|
||||
return () -> new IllegalStateException("XXX");
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Objects#checkIndex(int, int)} over the Guava alternative. */
|
||||
static final class CheckIndex {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -35,13 +35,21 @@ final class CollectionRules {
|
||||
*/
|
||||
static final class CollectionIsEmpty<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("java:S1155" /* This violation will be rewritten. */)
|
||||
@SuppressWarnings({
|
||||
"java:S1155" /* This violation will be rewritten. */,
|
||||
"LexicographicalAnnotationAttributeListing" /* `key-*` entry must remain last. */,
|
||||
"OptionalFirstCollectionElement" /* This is a more specific template. */,
|
||||
"StreamIsEmpty" /* This is a more specific template. */,
|
||||
"key-to-resolve-AnnotationUseStyle-and-TrailingComment-check-conflict"
|
||||
})
|
||||
boolean before(Collection<T> collection) {
|
||||
return Refaster.anyOf(
|
||||
collection.size() == 0,
|
||||
collection.size() <= 0,
|
||||
collection.size() < 1,
|
||||
Iterables.isEmpty(collection));
|
||||
Iterables.isEmpty(collection),
|
||||
collection.stream().findAny().isEmpty(),
|
||||
collection.stream().findFirst().isEmpty());
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
@@ -337,7 +345,9 @@ final class CollectionRules {
|
||||
|
||||
/**
|
||||
* Don't use the ternary operator to extract the first element of a possibly-empty {@link
|
||||
* Collection} as an {@link Optional}.
|
||||
* Collection} as an {@link Optional}, and (when applicable) prefer {@link Stream#findFirst()}
|
||||
* over {@link Stream#findAny()} to communicate that the collection's first element (if any,
|
||||
* according to iteration order) will be returned.
|
||||
*/
|
||||
static final class OptionalFirstCollectionElement<T> {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.Comparator.reverseOrder;
|
||||
import static java.util.function.Predicate.not;
|
||||
import static java.util.stream.Collectors.collectingAndThen;
|
||||
import static java.util.stream.Collectors.counting;
|
||||
import static java.util.stream.Collectors.filtering;
|
||||
import static java.util.stream.Collectors.flatMapping;
|
||||
@@ -37,6 +38,7 @@ import java.util.Comparator;
|
||||
import java.util.DoubleSummaryStatistics;
|
||||
import java.util.IntSummaryStatistics;
|
||||
import java.util.LongSummaryStatistics;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BinaryOperator;
|
||||
@@ -254,15 +256,21 @@ final class StreamRules {
|
||||
// XXX: This rule assumes that any matched `Collector` does not perform any filtering.
|
||||
// (Perhaps we could add a `@Matches` guard that validates that the collector expression does not
|
||||
// contain a `Collectors#filtering` call. That'd still not be 100% accurate, though.)
|
||||
static final class StreamIsEmpty<T> {
|
||||
static final class StreamIsEmpty<T, K, V, C extends Collection<K>, M extends Map<K, V>> {
|
||||
@BeforeTemplate
|
||||
boolean before(Stream<T> stream, Collector<? super T, ?, ? extends Collection<?>> collector) {
|
||||
boolean before(Stream<T> stream, Collector<? super T, ?, ? extends C> collector) {
|
||||
return Refaster.anyOf(
|
||||
stream.count() == 0,
|
||||
stream.count() <= 0,
|
||||
stream.count() < 1,
|
||||
stream.findFirst().isEmpty(),
|
||||
stream.collect(collector).isEmpty());
|
||||
stream.collect(collector).isEmpty(),
|
||||
stream.collect(collectingAndThen(collector, C::isEmpty)));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
boolean before2(Stream<T> stream, Collector<? super T, ?, ? extends M> collector) {
|
||||
return stream.collect(collectingAndThen(collector, M::isEmpty));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
|
||||
@@ -29,7 +29,9 @@ final class StringRules {
|
||||
private StringRules() {}
|
||||
|
||||
/** Prefer {@link String#isEmpty()} over alternatives that consult the string's length. */
|
||||
// XXX: Once we target JDK 15+, generalize this rule to cover all `CharSequence` subtypes.
|
||||
// XXX: Now that we build with JDK 15+, this rule can be generalized to cover all `CharSequence`
|
||||
// subtypes. This does require a mechanism (perhaps an annotation, or a separate Maven module) to
|
||||
// make sure that non-String expressions are rewritten only if client code also targets JDK 15+.
|
||||
static final class StringIsEmpty {
|
||||
@BeforeTemplate
|
||||
boolean before(String str) {
|
||||
@@ -44,7 +46,9 @@ final class StringRules {
|
||||
}
|
||||
|
||||
/** Prefer a method reference to {@link String#isEmpty()} over the equivalent lambda function. */
|
||||
// XXX: Once we target JDK 15+, generalize this rule to cover all `CharSequence` subtypes.
|
||||
// XXX: Now that we build with JDK 15+, this rule can be generalized to cover all `CharSequence`
|
||||
// subtypes. However, `CharSequence::isEmpty` isn't as nice as `String::isEmpty`, so we might want
|
||||
// to introduce a rule that suggests `String::isEmpty` where possible.
|
||||
// XXX: As it stands, this rule is a special case of what `MethodReferenceUsage` tries to achieve.
|
||||
// If/when `MethodReferenceUsage` becomes production ready, we should simply drop this check.
|
||||
static final class StringIsEmptyPredicate {
|
||||
@@ -60,7 +64,9 @@ final class StringRules {
|
||||
}
|
||||
|
||||
/** Prefer a method reference to {@link String#isEmpty()} over the equivalent lambda function. */
|
||||
// XXX: Once we target JDK 15+, generalize this rule to cover all `CharSequence` subtypes.
|
||||
// XXX: Now that we build with JDK 15+, this rule can be generalized to cover all `CharSequence`
|
||||
// subtypes. However, `CharSequence::isEmpty` isn't as nice as `String::isEmpty`, so we might want
|
||||
// to introduce a rule that suggests `String::isEmpty` where possible.
|
||||
static final class StringIsNotEmptyPredicate {
|
||||
@BeforeTemplate
|
||||
Predicate<String> before() {
|
||||
@@ -162,6 +168,39 @@ final class StringRules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer direct invocation of {@link String#String(char[], int, int)} over the indirection
|
||||
* introduced by alternatives.
|
||||
*/
|
||||
static final class NewStringFromCharArraySubSequence {
|
||||
@BeforeTemplate
|
||||
String before(char[] data, int offset, int count) {
|
||||
return Refaster.anyOf(
|
||||
String.valueOf(data, offset, count), String.copyValueOf(data, offset, count));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(char[] data, int offset, int count) {
|
||||
return new String(data, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer direct invocation of {@link String#String(char[])} over the indirection introduced by
|
||||
* alternatives.
|
||||
*/
|
||||
static final class NewStringFromCharArray {
|
||||
@BeforeTemplate
|
||||
String before(char[] data) {
|
||||
return Refaster.anyOf(String.valueOf(data), new String(data, 0, data.length));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(char[] data) {
|
||||
return new String(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer direct delegation to {@link String#valueOf(Object)} over the indirection introduced by
|
||||
* {@link Objects#toString(Object)}.
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
||||
import org.testng.Assert;
|
||||
import org.testng.Assert.ThrowingRunnable;
|
||||
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
import tech.picnic.errorprone.refaster.annotation.TypeMigration;
|
||||
|
||||
/**
|
||||
* Refaster rules that replace TestNG assertions with equivalent AssertJ assertions.
|
||||
@@ -48,32 +49,107 @@ import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
|
||||
* List<Map<String, Object>> myMaps = new ArrayList<>();
|
||||
* assertEquals(myMaps, ImmutableList.of(ImmutableMap.of()));
|
||||
* }</pre>
|
||||
*
|
||||
* <p>A few {@link Assert} methods are not rewritten:
|
||||
*
|
||||
* <ul>
|
||||
* <li>These methods cannot (easily) be expressed using AssertJ because they mix regular equality
|
||||
* and array equality:
|
||||
* <ul>
|
||||
* <li>{@link Assert#assertEqualsDeep(Map, Map)}
|
||||
* <li>{@link Assert#assertEqualsDeep(Map, Map, String)}
|
||||
* <li>{@link Assert#assertEqualsDeep(Set, Set, String)}
|
||||
* <li>{@link Assert#assertNotEqualsDeep(Map, Map)}
|
||||
* <li>{@link Assert#assertNotEqualsDeep(Map, Map, String)}
|
||||
* <li>{@link Assert#assertNotEqualsDeep(Set, Set)}
|
||||
* <li>{@link Assert#assertNotEqualsDeep(Set, Set, String)}
|
||||
* </ul>
|
||||
* <li>This method returns the caught exception; there is no direct counterpart for this in
|
||||
* AssertJ:
|
||||
* <ul>
|
||||
* <li>{@link Assert#expectThrows(Class, ThrowingRunnable)}
|
||||
* </ul>
|
||||
* </ul>
|
||||
*/
|
||||
// XXX: As-is these rules do not result in a complete migration:
|
||||
// - Expressions containing comments are skipped due to a limitation of Refaster.
|
||||
// - Assertions inside lambda expressions are also skipped. Unclear why.
|
||||
// XXX: The `assertEquals` tests for this class generally use the same expression for `expected` and
|
||||
// `actual`, which makes the validation weaker than necessary; fix this. (And investigate whether we
|
||||
// can introduce validation for this.)
|
||||
@OnlineDocumentation
|
||||
@TypeMigration(
|
||||
of = Assert.class,
|
||||
unmigratedMethods = {
|
||||
// XXX: Add migrations for the methods below.
|
||||
"assertEquals(Boolean, Boolean)",
|
||||
"assertEquals(Boolean, boolean)",
|
||||
"assertEquals(boolean, Boolean)",
|
||||
"assertEquals(Boolean, Boolean, String)",
|
||||
"assertEquals(Boolean, boolean, String)",
|
||||
"assertEquals(boolean, Boolean, String)",
|
||||
"assertEquals(Byte, Byte)",
|
||||
"assertEquals(Byte, byte)",
|
||||
"assertEquals(byte, Byte)",
|
||||
"assertEquals(Byte, Byte, String)",
|
||||
"assertEquals(Byte, byte, String)",
|
||||
"assertEquals(byte, Byte, String)",
|
||||
"assertEquals(char, Character)",
|
||||
"assertEquals(char, Character, String)",
|
||||
"assertEquals(Character, char)",
|
||||
"assertEquals(Character, char, String)",
|
||||
"assertEquals(Character, Character)",
|
||||
"assertEquals(Character, Character, String)",
|
||||
"assertEquals(Double, Double)",
|
||||
"assertEquals(Double, double)",
|
||||
"assertEquals(double, Double)",
|
||||
"assertEquals(Double, Double, String)",
|
||||
"assertEquals(Double, double, String)",
|
||||
"assertEquals(double, Double, String)",
|
||||
"assertEquals(double[], double[], double)",
|
||||
"assertEquals(double[], double[], double, String)",
|
||||
"assertEquals(Float, Float)",
|
||||
"assertEquals(Float, float)",
|
||||
"assertEquals(float, Float)",
|
||||
"assertEquals(Float, Float, String)",
|
||||
"assertEquals(Float, float, String)",
|
||||
"assertEquals(float, Float, String)",
|
||||
"assertEquals(float[], float[], float)",
|
||||
"assertEquals(float[], float[], float, String)",
|
||||
"assertEquals(int, Integer)",
|
||||
"assertEquals(int, Integer, String)",
|
||||
"assertEquals(Integer, int)",
|
||||
"assertEquals(Integer, int, String)",
|
||||
"assertEquals(Integer, Integer)",
|
||||
"assertEquals(Integer, Integer, String)",
|
||||
"assertEquals(Long, Long)",
|
||||
"assertEquals(Long, long)",
|
||||
"assertEquals(long, Long)",
|
||||
"assertEquals(Long, Long, String)",
|
||||
"assertEquals(Long, long, String)",
|
||||
"assertEquals(Short, Short)",
|
||||
"assertEquals(Short, short)",
|
||||
"assertEquals(short, Short)",
|
||||
"assertEquals(Short, Short, String)",
|
||||
"assertEquals(Short, short, String)",
|
||||
"assertEquals(short, Short, String)",
|
||||
/*
|
||||
* These `assertEqualsDeep` methods cannot (easily) be expressed using AssertJ because they
|
||||
* mix regular equality and array equality:
|
||||
*/
|
||||
"assertEqualsDeep(Map<?, ?>, Map<?, ?>)",
|
||||
"assertEqualsDeep(Map<?, ?>, Map<?, ?>, String)",
|
||||
"assertEqualsDeep(Set<?>, Set<?>, String)",
|
||||
// XXX: Add migrations for the methods below.
|
||||
"assertEqualsNoOrder(Collection<?>, Collection<?>)",
|
||||
"assertEqualsNoOrder(Collection<?>, Collection<?>, String)",
|
||||
"assertEqualsNoOrder(Iterator<?>, Iterator<?>)",
|
||||
"assertEqualsNoOrder(Iterator<?>, Iterator<?>, String)",
|
||||
"assertListContains(List<T>, Predicate<T>, String)",
|
||||
"assertListContainsObject(List<T>, T, String)",
|
||||
"assertListNotContains(List<T>, Predicate<T>, String)",
|
||||
"assertListNotContainsObject(List<T>, T, String)",
|
||||
"assertNotEquals(Collection<?>, Collection<?>)",
|
||||
"assertNotEquals(Collection<?>, Collection<?>, String)",
|
||||
"assertNotEquals(Iterator<?>, Iterator<?>)",
|
||||
"assertNotEquals(Iterator<?>, Iterator<?>, String)",
|
||||
"assertNotEquals(Object[], Object[], String)",
|
||||
/*
|
||||
* These `assertNotEqualsDeep` methods cannot (easily) be expressed using AssertJ because they
|
||||
* mix regular equality and array equality:
|
||||
*/
|
||||
"assertNotEqualsDeep(Map<?, ?>, Map<?, ?>)",
|
||||
"assertNotEqualsDeep(Map<?, ?>, Map<?, ?>, String)",
|
||||
"assertNotEqualsDeep(Set<?>, Set<?>)",
|
||||
"assertNotEqualsDeep(Set<?>, Set<?>, String)",
|
||||
// XXX: Add a migration for this `assertThrows` method.
|
||||
"assertThrows(String, Class<T>, ThrowingRunnable)",
|
||||
/*
|
||||
* These `expectThrows` methods return the caught exception; there is no direct counterpart
|
||||
* for this in AssertJ.
|
||||
*/
|
||||
"expectThrows(Class<T>, ThrowingRunnable)",
|
||||
"expectThrows(String, Class<T>, ThrowingRunnable)"
|
||||
})
|
||||
final class TestNGToAssertJRules {
|
||||
private TestNGToAssertJRules() {}
|
||||
|
||||
|
||||
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() {",
|
||||
|
||||
@@ -75,6 +75,8 @@ final class ExplicitEnumOrderingTest {
|
||||
" Ordering.explicit(IsoEra.BCE, SOURCE, RetentionPolicy.CLASS);",
|
||||
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE, IsoEra.BCE",
|
||||
" Ordering.explicit(CLASS, RUNTIME, CE);",
|
||||
"",
|
||||
" Ordering.explicit(BCE, null, CE);",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
|
||||
@@ -65,6 +65,8 @@ final class NonStaticImportTest {
|
||||
"// BUG: Diagnostic contains:",
|
||||
"import static java.util.Optional.empty;",
|
||||
"import static pkg.A.WithMethodThatIsSelectivelyFlagged.list;",
|
||||
"// BUG: Diagnostic contains:",
|
||||
"import static reactor.core.publisher.Flux.just;",
|
||||
"",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
@@ -72,7 +74,7 @@ final class NonStaticImportTest {
|
||||
"import java.time.ZoneOffset;",
|
||||
"import java.util.Locale;",
|
||||
"import java.util.Map;",
|
||||
"import pkg.A.Wrapper.ZERO;",
|
||||
"import pkg.A.Wrapper.INSTANCE;",
|
||||
"",
|
||||
"class A {",
|
||||
" private Integer MIN_VALUE = 12;",
|
||||
@@ -90,9 +92,10 @@ final class NonStaticImportTest {
|
||||
" Locale english = ENGLISH;",
|
||||
" Locale root = ROOT;",
|
||||
" empty();",
|
||||
" just();",
|
||||
"",
|
||||
" list();",
|
||||
" new ZERO();",
|
||||
" new INSTANCE();",
|
||||
" }",
|
||||
"",
|
||||
" static final class WithMethodThatIsSelectivelyFlagged {",
|
||||
@@ -102,7 +105,7 @@ final class NonStaticImportTest {
|
||||
" }",
|
||||
"",
|
||||
" static final class Wrapper {",
|
||||
" static final class ZERO {}",
|
||||
" static final class INSTANCE {}",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
|
||||
@@ -16,6 +16,7 @@ final class RequestMappingAnnotationTest {
|
||||
"import java.util.Locale;",
|
||||
"import java.util.TimeZone;",
|
||||
"import org.springframework.http.HttpMethod;",
|
||||
"import org.springframework.security.core.annotation.CurrentSecurityContext;",
|
||||
"import org.springframework.ui.Model;",
|
||||
"import org.springframework.validation.BindingResult;",
|
||||
"import org.springframework.web.bind.annotation.DeleteMapping;",
|
||||
@@ -63,6 +64,10 @@ final class RequestMappingAnnotationTest {
|
||||
" A properRequestPart(@RequestPart String part);",
|
||||
"",
|
||||
" @RequestMapping",
|
||||
" A properCurrentSecurityContext(",
|
||||
" @CurrentSecurityContext(expression = \"authentication.name\") String user);",
|
||||
"",
|
||||
" @RequestMapping",
|
||||
" A properInputStream(InputStream input);",
|
||||
"",
|
||||
" @RequestMapping",
|
||||
|
||||
@@ -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:",
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.google.common.collect.Streams;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.Stream;
|
||||
import reactor.core.publisher.Mono;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
|
||||
|
||||
final class AssortedRulesTest implements RefasterRuleCollectionTestCase {
|
||||
@@ -28,6 +29,10 @@ final class AssortedRulesTest implements RefasterRuleCollectionTestCase {
|
||||
toImmutableSet());
|
||||
}
|
||||
|
||||
Mono<Void> testIllegalStateExceptionSupplier() {
|
||||
return Mono.error(() -> new IllegalStateException("ISE"));
|
||||
}
|
||||
|
||||
int testCheckIndex() {
|
||||
return Preconditions.checkElementIndex(0, 1);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
@@ -114,7 +126,12 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
|
||||
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)));
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testStreamIsNotEmpty() {
|
||||
|
||||
@@ -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() {
|
||||
@@ -115,7 +127,11 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
|
||||
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());
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testStreamIsNotEmpty() {
|
||||
|
||||
@@ -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.15.1-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;
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.experimental.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
5
error-prone-guidelines/README.md
Normal file
5
error-prone-guidelines/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Error Prone Support coding guidelines
|
||||
|
||||
This module provides Error Prone checks that describe and suggest coding
|
||||
guidelines for both Error Prone checks and Refaster rules. These rules are only
|
||||
meant to be applied to Error Prone Support itself.
|
||||
129
error-prone-guidelines/pom.xml
Normal file
129
error-prone-guidelines/pom.xml
Normal file
@@ -0,0 +1,129 @@
|
||||
<?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.15.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>error-prone-guidelines</artifactId>
|
||||
|
||||
<name>Picnic :: Error Prone Support :: Guidelines</name>
|
||||
<description>Coding guidelines for the Error Prone Support project.</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_core</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>documentation-support</artifactId>
|
||||
<!-- This dependency is declared only as a hint to Maven that
|
||||
compilation depends on it; see the `maven-compiler-plugin`'s
|
||||
`annotationProcessorPaths` configuration below. -->
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>error-prone-utils</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>refaster-support</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto</groupId>
|
||||
<artifactId>auto-common</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.googlejavaformat</groupId>
|
||||
<artifactId>google-java-format</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</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>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths combine.children="append">
|
||||
<!-- XXX: Drop the version declarations once
|
||||
properly supported. See
|
||||
https://youtrack.jetbrains.com/issue/IDEA-342187. -->
|
||||
<path>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>documentation-support</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs combine.children="append">
|
||||
<arg>-Xplugin:DocumentationGenerator -XoutputDirectory=${project.build.directory}/docs</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
@@ -7,7 +7,7 @@ import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
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.ImmutableSet;
|
||||
@@ -27,7 +27,7 @@ import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags unnecessarily nested usage of methods that implement an
|
||||
@@ -36,6 +36,8 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
* <p>The arguments to such methods can be flattened without affecting semantics, while making the
|
||||
* code more readable.
|
||||
*/
|
||||
// XXX: Move this check to the `error-prone-contrib` module once it also covers non-Error Prone
|
||||
// methods.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
@@ -0,0 +1,140 @@
|
||||
package tech.picnic.errorprone.guidelines.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.LIKELY_ERROR;
|
||||
import static com.google.errorprone.matchers.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
|
||||
import static com.google.errorprone.matchers.FieldMatchers.staticField;
|
||||
import static com.google.errorprone.matchers.Matchers.annotations;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static com.google.errorprone.matchers.Matchers.packageStartsWith;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.AnnotationMatcherUtils;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.matchers.MultiMatcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.BinaryTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import com.sun.tools.javac.util.Constants;
|
||||
import javax.lang.model.element.Name;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags {@link BugChecker} declarations inside {@code
|
||||
* tech.picnic.errorprone.*} packages that do not reference the Error Prone Support website.
|
||||
*/
|
||||
// XXX: Introduce a similar check to enforce the Refaster `@OnlineDocumentation` annotation. (Or
|
||||
// update the website generation to document Refaster collections by default, and provide an
|
||||
// exclusion annotation instead. This may make more sense.)
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Error Prone Support checks must reference their online documentation",
|
||||
link = BUG_PATTERNS_BASE_URL + "BugPatternLink",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class BugPatternLink extends BugChecker implements ClassTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ClassTree> IS_ERROR_PRONE_SUPPORT_CLASS =
|
||||
packageStartsWith("tech.picnic.errorprone");
|
||||
private static final Matcher<ExpressionTree> IS_LINK_TYPE_NONE =
|
||||
staticField(BugPattern.LinkType.class.getCanonicalName(), "NONE");
|
||||
private static final Matcher<ExpressionTree> IS_BUG_PATTERNS_BASE_URL =
|
||||
staticField("tech.picnic.errorprone.utils.Documentation", "BUG_PATTERNS_BASE_URL");
|
||||
private static final MultiMatcher<ClassTree, AnnotationTree> HAS_BUG_PATTERN_ANNOTATION =
|
||||
annotations(AT_LEAST_ONE, isType(BugPattern.class.getCanonicalName()));
|
||||
|
||||
/** Instantiates a new {@link BugPatternLink} instance. */
|
||||
public BugPatternLink() {}
|
||||
|
||||
@Override
|
||||
public Description matchClass(ClassTree tree, VisitorState state) {
|
||||
if (ASTHelpers.findEnclosingNode(state.getPath(), ClassTree.class) != null) {
|
||||
/*
|
||||
* This is a nested class; even if it's bug checker, then it's likely declared within a test
|
||||
* class.
|
||||
*/
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
if (!IS_ERROR_PRONE_SUPPORT_CLASS.matches(tree, state)) {
|
||||
/*
|
||||
* Bug checkers defined elsewhere are unlikely to be documented on the Error Prone Support
|
||||
* website.
|
||||
*/
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
ImmutableList<AnnotationTree> bugPatternAnnotations =
|
||||
HAS_BUG_PATTERN_ANNOTATION.multiMatchResult(tree, state).matchingNodes();
|
||||
if (bugPatternAnnotations.isEmpty()) {
|
||||
/* This isn't a bug checker. */
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
AnnotationTree annotation = Iterables.getOnlyElement(bugPatternAnnotations);
|
||||
if (isCompliant(annotation, tree.getSimpleName(), state)) {
|
||||
/* The bug checker is correctly configured. */
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
return describeMatch(annotation, suggestFix(tree, state, annotation));
|
||||
}
|
||||
|
||||
private static boolean isCompliant(
|
||||
AnnotationTree annotation, Name className, VisitorState state) {
|
||||
ExpressionTree linkType = AnnotationMatcherUtils.getArgument(annotation, "linkType");
|
||||
if (IS_LINK_TYPE_NONE.matches(linkType, state)) {
|
||||
/* This bug checker explicitly declares that there is no link. */
|
||||
return true;
|
||||
}
|
||||
|
||||
ExpressionTree link = AnnotationMatcherUtils.getArgument(annotation, "link");
|
||||
if (!(link instanceof BinaryTree binary)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
verify(binary.getKind() == Kind.PLUS, "Unexpected binary operator");
|
||||
return IS_BUG_PATTERNS_BASE_URL.matches(binary.getLeftOperand(), state)
|
||||
&& className.contentEquals(ASTHelpers.constValue(binary.getRightOperand(), String.class));
|
||||
}
|
||||
|
||||
private static SuggestedFix suggestFix(
|
||||
ClassTree tree, VisitorState state, AnnotationTree annotation) {
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder();
|
||||
|
||||
String linkPrefix =
|
||||
SuggestedFixes.qualifyStaticImport(
|
||||
"tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL", fix, state);
|
||||
fix.merge(
|
||||
SuggestedFixes.updateAnnotationArgumentValues(
|
||||
annotation,
|
||||
state,
|
||||
"link",
|
||||
ImmutableList.of(
|
||||
linkPrefix + " + " + Constants.format(tree.getSimpleName().toString()))));
|
||||
|
||||
String linkType =
|
||||
SuggestedFixes.qualifyStaticImport(
|
||||
BugPattern.LinkType.class.getCanonicalName() + ".CUSTOM", fix, state);
|
||||
fix.merge(
|
||||
SuggestedFixes.updateAnnotationArgumentValues(
|
||||
annotation, state, "linkType", ImmutableList.of(linkType)));
|
||||
|
||||
return fix.build();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.FRAGILE_CODE;
|
||||
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.common.collect.ImmutableSet;
|
||||
@@ -28,7 +28,7 @@ import com.sun.tools.javac.code.Symbol;
|
||||
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
||||
import com.sun.tools.javac.util.Constants;
|
||||
import java.util.regex.Pattern;
|
||||
import tech.picnic.errorprone.bugpatterns.util.ThirdPartyLibrary;
|
||||
import tech.picnic.errorprone.utils.ThirdPartyLibrary;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags literal strings in Error Prone Support code that represent the
|
||||
@@ -50,8 +50,9 @@ import tech.picnic.errorprone.bugpatterns.util.ThirdPartyLibrary;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"Prefer `Class#getCanonicalName()` over an equivalent string literal if and only if the "
|
||||
+ "type will be on the runtime classpath",
|
||||
"""
|
||||
Prefer `Class#getCanonicalName()` over an equivalent string literal if and only if the \
|
||||
type will be on the runtime classpath""",
|
||||
link = BUG_PATTERNS_BASE_URL + "ErrorProneRuntimeClasspath",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
@@ -7,7 +7,7 @@ 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 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;
|
||||
@@ -0,0 +1,232 @@
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
|
||||
import static com.google.common.base.Verify.verify;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
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.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
|
||||
import static com.google.errorprone.matchers.Matchers.annotations;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.stream.Collectors.toCollection;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.common.AnnotationMirrors;
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.MultiMatcher;
|
||||
import com.google.errorprone.matchers.MultiMatcher.MultiMatchResult;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.google.errorprone.util.Signatures;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.NewClassTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import com.sun.tools.javac.code.Attribute;
|
||||
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
||||
import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
||||
import com.sun.tools.javac.code.Symbol.TypeSymbol;
|
||||
import com.sun.tools.javac.util.Constants;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that validates the claim made by {@link
|
||||
* tech.picnic.errorprone.refaster.annotation.TypeMigration} annotations.
|
||||
*/
|
||||
// XXX: As-is this checker assumes that a method is fully migrated if it is invoked inside at least
|
||||
// one `@BeforeTemplate` method. A stronger check would be to additionally verify that:
|
||||
// 1. Such invocations are not conditionally matched. That is, there should be no constraint on
|
||||
// their context (i.e. any surrounding code), and their parameters must be `@BeforeTemplate`
|
||||
// method parameters with types that are not more restrictive than those of the method itself.
|
||||
// Additionally, the result of non-void methods should be "returned" by the `@BeforeTemplate`
|
||||
// method, so that Refaster will match any expression, rather than just statements. (One caveat
|
||||
// with this "context-independent migrations only" approach is that APIs often expose methods
|
||||
// that are only useful in combination with other methods of the API; insisting that such methods
|
||||
// are migrated in isolation is unreasonable.)
|
||||
// 2. Where relevant, method references should also be migrated. (TBD what "relevant" means in this
|
||||
// case, and whether in fact method reference matchers can be _derived_ from the associated
|
||||
// method invocation matchers.)
|
||||
// XXX: This checker currently does no concern itself with public fields. Consider adding support
|
||||
// for those.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"""
|
||||
The set of unmigrated methods listed by the `@TypeMigration` annotation must be minimal \
|
||||
yet exhaustive""",
|
||||
link = BUG_PATTERNS_BASE_URL + "ExhaustiveRefasterTypeMigration",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class ExhaustiveRefasterTypeMigration extends BugChecker implements ClassTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final MultiMatcher<Tree, AnnotationTree> IS_TYPE_MIGRATION =
|
||||
annotations(AT_LEAST_ONE, isType("tech.picnic.errorprone.refaster.annotation.TypeMigration"));
|
||||
private static final MultiMatcher<Tree, AnnotationTree> HAS_BEFORE_TEMPLATE =
|
||||
annotations(AT_LEAST_ONE, isType(BeforeTemplate.class.getCanonicalName()));
|
||||
private static final String TYPE_MIGRATION_TYPE_ELEMENT = "of";
|
||||
private static final String TYPE_MIGRATION_UNMIGRATED_METHODS_ELEMENT = "unmigratedMethods";
|
||||
|
||||
/** Instantiates a new {@link ExhaustiveRefasterTypeMigration} instance. */
|
||||
public ExhaustiveRefasterTypeMigration() {}
|
||||
|
||||
@Override
|
||||
public Description matchClass(ClassTree tree, VisitorState state) {
|
||||
MultiMatchResult<AnnotationTree> migrationAnnotations =
|
||||
IS_TYPE_MIGRATION.multiMatchResult(tree, state);
|
||||
if (!migrationAnnotations.matches()) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
AnnotationTree migrationAnnotation = migrationAnnotations.onlyMatchingNode();
|
||||
AnnotationMirror annotationMirror = ASTHelpers.getAnnotationMirror(migrationAnnotation);
|
||||
TypeSymbol migratedType = getMigratedType(annotationMirror);
|
||||
if (migratedType.asType().isPrimitive() || !(migratedType instanceof ClassSymbol)) {
|
||||
return buildDescription(migrationAnnotation)
|
||||
.setMessage(String.format("Migration of type '%s' is unsupported", migratedType))
|
||||
.build();
|
||||
}
|
||||
|
||||
ImmutableList<String> methodsClaimedUnmigrated = getMethodsClaimedUnmigrated(annotationMirror);
|
||||
ImmutableList<String> unmigratedMethods =
|
||||
getMethodsDefinitelyUnmigrated(
|
||||
tree, (ClassSymbol) migratedType, signatureOrder(methodsClaimedUnmigrated), state);
|
||||
|
||||
if (unmigratedMethods.equals(methodsClaimedUnmigrated)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
/*
|
||||
* The `@TypeMigration` annotation lists a different set of unmigrated methods than the one
|
||||
* produced by our analysis; suggest a replacement.
|
||||
*/
|
||||
// XXX: `updateAnnotationArgumentValues` will prepend the new attribute argument if it is not
|
||||
// already present. It would be nicer if it _appended_ the new attribute.
|
||||
return describeMatch(
|
||||
migrationAnnotation,
|
||||
SuggestedFixes.updateAnnotationArgumentValues(
|
||||
migrationAnnotation,
|
||||
state,
|
||||
TYPE_MIGRATION_UNMIGRATED_METHODS_ELEMENT,
|
||||
unmigratedMethods.stream().map(Constants::format).collect(toImmutableList()))
|
||||
.build());
|
||||
}
|
||||
|
||||
private static TypeSymbol getMigratedType(AnnotationMirror migrationAnnotation) {
|
||||
AnnotationValue value =
|
||||
AnnotationMirrors.getAnnotationValue(migrationAnnotation, TYPE_MIGRATION_TYPE_ELEMENT);
|
||||
verify(
|
||||
value instanceof Attribute.Class,
|
||||
"Value of annotation element `%s` is '%s' rather than a class",
|
||||
TYPE_MIGRATION_TYPE_ELEMENT,
|
||||
value);
|
||||
return ((Attribute.Class) value).classType.tsym;
|
||||
}
|
||||
|
||||
private static ImmutableList<String> getMethodsClaimedUnmigrated(
|
||||
AnnotationMirror migrationAnnotation) {
|
||||
AnnotationValue value =
|
||||
AnnotationMirrors.getAnnotationValue(
|
||||
migrationAnnotation, TYPE_MIGRATION_UNMIGRATED_METHODS_ELEMENT);
|
||||
verify(
|
||||
value instanceof Attribute.Array,
|
||||
"Value of annotation element `%s` is '%s' rather than an array",
|
||||
TYPE_MIGRATION_UNMIGRATED_METHODS_ELEMENT,
|
||||
value);
|
||||
return ((Attribute.Array) value)
|
||||
.getValue().stream().map(a -> a.getValue().toString()).collect(toImmutableList());
|
||||
}
|
||||
|
||||
// XXX: Once only JDK 14 and above are supported, change the
|
||||
// `m.getModifiers().contains(Modifier.PUBLIC)` check to just `m.isPublic()`.
|
||||
private static ImmutableList<String> getMethodsDefinitelyUnmigrated(
|
||||
ClassTree tree, ClassSymbol migratedType, Comparator<String> comparator, VisitorState state) {
|
||||
Set<MethodSymbol> publicMethods =
|
||||
Streams.stream(
|
||||
ASTHelpers.scope(migratedType.members())
|
||||
.getSymbols(
|
||||
m ->
|
||||
m.getModifiers().contains(Modifier.PUBLIC)
|
||||
&& m instanceof MethodSymbol))
|
||||
.map(MethodSymbol.class::cast)
|
||||
.collect(toCollection(HashSet::new));
|
||||
|
||||
/* Remove methods that *appear* to be migrated. Note that this is an imperfect heuristic. */
|
||||
removeMethodsInvokedInBeforeTemplateMethods(tree, publicMethods, state);
|
||||
|
||||
return publicMethods.stream()
|
||||
.map(m -> Signatures.prettyMethodSignature(migratedType, m))
|
||||
.sorted(comparator)
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Comparator} that orders method signatures to match the given list of
|
||||
* signatures, with any signatures not listed ordered first, lexicographically.
|
||||
*
|
||||
* @implNote This method does not use {@code comparing(list::indexOf)}, as that would make each
|
||||
* comparison a linear, rather than constant-time operation.
|
||||
*/
|
||||
private static Comparator<String> signatureOrder(ImmutableList<String> existingOrder) {
|
||||
Map<String, Integer> knownEntries = new HashMap<>();
|
||||
for (int i = 0; i < existingOrder.size(); i++) {
|
||||
knownEntries.putIfAbsent(existingOrder.get(i), i);
|
||||
}
|
||||
|
||||
// XXX: The lexicographical order applied to unknown entries aims to match the order applied by
|
||||
// the `LexicographicalAnnotationAttributeListing` check; consider deduplicating this logic.
|
||||
return comparing((String v) -> knownEntries.getOrDefault(v, -1))
|
||||
.thenComparing(String.CASE_INSENSITIVE_ORDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes from the given set of {@link MethodSymbol}s the ones that refer to a method that is
|
||||
* invoked inside a {@link com.google.errorprone.refaster.annotation.BeforeTemplate} method inside
|
||||
* the specified {@link ClassTree}.
|
||||
*/
|
||||
private static void removeMethodsInvokedInBeforeTemplateMethods(
|
||||
ClassTree tree, Set<MethodSymbol> candidates, VisitorState state) {
|
||||
new TreeScanner<@Nullable Void, Consumer<MethodSymbol>>() {
|
||||
@Override
|
||||
public @Nullable Void visitMethod(MethodTree tree, Consumer<MethodSymbol> sink) {
|
||||
return HAS_BEFORE_TEMPLATE.matches(tree, state)
|
||||
? super.visitMethod(tree, candidates::remove)
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Void visitNewClass(NewClassTree tree, Consumer<MethodSymbol> sink) {
|
||||
sink.accept(ASTHelpers.getSymbol(tree));
|
||||
return super.visitNewClass(tree, sink);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Void visitMethodInvocation(
|
||||
MethodInvocationTree tree, Consumer<MethodSymbol> sink) {
|
||||
sink.accept(ASTHelpers.getSymbol(tree));
|
||||
return super.visitMethodInvocation(tree, sink);
|
||||
}
|
||||
}.scan(tree, s -> {});
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.guidelines.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 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;
|
||||
@@ -17,7 +17,7 @@ import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags unnecessary {@link Refaster#anyOf(Object[])} usages.
|
||||
@@ -42,21 +42,18 @@ public final class RefasterAnyOfUsage extends BugChecker implements MethodInvoca
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
if (REFASTER_ANY_OF.matches(tree, state)) {
|
||||
switch (tree.getArguments().size()) {
|
||||
case 0:
|
||||
// We can't safely fix this case; dropping the expression may produce non-compilable code.
|
||||
return describeMatch(tree);
|
||||
case 1:
|
||||
return describeMatch(
|
||||
tree,
|
||||
SuggestedFix.replace(
|
||||
tree, SourceCode.treeToString(tree.getArguments().get(0), state)));
|
||||
default:
|
||||
/* Handled below. */
|
||||
}
|
||||
int argumentCount = tree.getArguments().size();
|
||||
if (argumentCount > 1 || !REFASTER_ANY_OF.matches(tree, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
return Description.NO_MATCH;
|
||||
if (argumentCount == 0) {
|
||||
/* We can't safely fix this case; dropping the expression may produce non-compilable code. */
|
||||
return describeMatch(tree);
|
||||
}
|
||||
|
||||
return describeMatch(
|
||||
tree,
|
||||
SuggestedFix.replace(tree, SourceCode.treeToString(tree.getArguments().get(0), state)));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
@@ -8,7 +8,7 @@ import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.stream.Collectors.toCollection;
|
||||
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;
|
||||
@@ -38,7 +38,7 @@ import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import javax.lang.model.element.Name;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags Refaster methods with a non-canonical parameter order.
|
||||
@@ -1,11 +1,11 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
|
||||
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 com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
|
||||
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;
|
||||
@@ -1,10 +1,10 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.FRAGILE_CODE;
|
||||
import static com.google.errorprone.matchers.Matchers.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;
|
||||
@@ -45,16 +45,14 @@ public final class UnqualifiedSuggestedFixImport extends BugChecker
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
switch (ASTHelpers.getSymbol(tree).getSimpleName().toString()) {
|
||||
case "addImport":
|
||||
return createDescription(
|
||||
tree, "SuggestedFix.Builder#addImport", "SuggestedFixes#qualifyType");
|
||||
case "addStaticImport":
|
||||
return createDescription(
|
||||
tree, "SuggestedFix.Builder#addStaticImport", "SuggestedFixes#qualifyStaticImport");
|
||||
default:
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
return switch (ASTHelpers.getSymbol(tree).getSimpleName().toString()) {
|
||||
case "addImport" ->
|
||||
createDescription(tree, "SuggestedFix.Builder#addImport", "SuggestedFixes#qualifyType");
|
||||
case "addStaticImport" ->
|
||||
createDescription(
|
||||
tree, "SuggestedFix.Builder#addStaticImport", "SuggestedFixes#qualifyStaticImport");
|
||||
default -> Description.NO_MATCH;
|
||||
};
|
||||
}
|
||||
|
||||
private Description createDescription(
|
||||
@@ -0,0 +1,4 @@
|
||||
/** Error Prone Support coding guidelines. */
|
||||
@com.google.errorprone.annotations.CheckReturnValue
|
||||
@org.jspecify.annotations.NullMarked
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
@@ -0,0 +1,193 @@
|
||||
package tech.picnic.errorprone.guidelines.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 BugPatternLinkTest {
|
||||
@Test
|
||||
void identification() {
|
||||
CompilationTestHelper.newInstance(BugPatternLink.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"",
|
||||
"@BugPattern(summary = \"Class in default package\", severity = BugPattern.SeverityLevel.ERROR)",
|
||||
"class A {}")
|
||||
.addSourceLines(
|
||||
"com/example/B.java",
|
||||
"package com.example;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"",
|
||||
"@BugPattern(summary = \"Class in custom package\", severity = BugPattern.SeverityLevel.ERROR)",
|
||||
"class B {}")
|
||||
.addSourceLines(
|
||||
"tech/picnic/errorprone/C.java",
|
||||
"package tech.picnic.errorprone;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"",
|
||||
"@BugPattern(",
|
||||
" summary = \"Class explicitly without link\",",
|
||||
" linkType = BugPattern.LinkType.NONE,",
|
||||
" severity = BugPattern.SeverityLevel.ERROR)",
|
||||
"class C {}")
|
||||
.addSourceLines(
|
||||
"tech/picnic/errorprone/subpackage/D.java",
|
||||
"package tech.picnic.errorprone.subpackage;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"import tech.picnic.errorprone.utils.Documentation;",
|
||||
"",
|
||||
"@BugPattern(",
|
||||
" summary = \"Error Prone Support class in subpackage with proper link\",",
|
||||
" link = Documentation.BUG_PATTERNS_BASE_URL + \"D\",",
|
||||
" linkType = BugPattern.LinkType.CUSTOM,",
|
||||
" severity = BugPattern.SeverityLevel.ERROR)",
|
||||
"class D {}")
|
||||
.addSourceLines(
|
||||
"tech/picnic/errorprone/E.java",
|
||||
"package tech.picnic.errorprone;",
|
||||
"",
|
||||
"import static com.google.errorprone.BugPattern.LinkType.CUSTOM;",
|
||||
"import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;",
|
||||
"import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"",
|
||||
"@BugPattern(",
|
||||
" summary = \"Error Prone Support class with proper link and static imports\",",
|
||||
" link = BUG_PATTERNS_BASE_URL + \"E\",",
|
||||
" linkType = CUSTOM,",
|
||||
" severity = ERROR)",
|
||||
"class E {}")
|
||||
.addSourceLines(
|
||||
"tech/picnic/errorprone/F.java",
|
||||
"package tech.picnic.errorprone;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"",
|
||||
"class F {",
|
||||
" @BugPattern(",
|
||||
" summary = \"Nested Error Prone Support class\",",
|
||||
" severity = BugPattern.SeverityLevel.ERROR)",
|
||||
" class Inner {}",
|
||||
"}")
|
||||
.addSourceLines(
|
||||
"tech/picnic/errorprone/G.java",
|
||||
"package tech.picnic.errorprone;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"",
|
||||
"// BUG: Diagnostic contains:",
|
||||
"@BugPattern(",
|
||||
" summary = \"Error Prone Support class lacking link\",",
|
||||
" severity = BugPattern.SeverityLevel.ERROR)",
|
||||
"class G {}")
|
||||
.addSourceLines(
|
||||
"tech/picnic/errorprone/H.java",
|
||||
"package tech.picnic.errorprone;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"import tech.picnic.errorprone.utils.Documentation;",
|
||||
"",
|
||||
"// BUG: Diagnostic contains:",
|
||||
"@BugPattern(",
|
||||
" summary = \"Error Prone Support class with incorrect link\",",
|
||||
" link = Documentation.BUG_PATTERNS_BASE_URL + \"NotH\",",
|
||||
" linkType = BugPattern.LinkType.CUSTOM,",
|
||||
" severity = BugPattern.SeverityLevel.ERROR)",
|
||||
"class H {}")
|
||||
.addSourceLines(
|
||||
"tech/picnic/errorprone/I.java",
|
||||
"package tech.picnic.errorprone;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"",
|
||||
"// BUG: Diagnostic contains:",
|
||||
"@BugPattern(",
|
||||
" summary = \"Error Prone Support class with non-canonical link\",",
|
||||
" link = \"https://error-prone.picnic.tech/bugpatterns/I\",",
|
||||
" linkType = BugPattern.LinkType.CUSTOM,",
|
||||
" severity = BugPattern.SeverityLevel.ERROR)",
|
||||
"class I {}")
|
||||
.addSourceLines(
|
||||
"tech/picnic/errorprone/J.java",
|
||||
"package tech.picnic.errorprone;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"",
|
||||
"// BUG: Diagnostic contains:",
|
||||
"@BugPattern(",
|
||||
" summary = \"Error Prone Support class in with non-canonical link\",",
|
||||
" link = \"https://error-prone.picnic.tech/bugpatterns/\" + \"J\",",
|
||||
" linkType = BugPattern.LinkType.CUSTOM,",
|
||||
" severity = BugPattern.SeverityLevel.ERROR)",
|
||||
"class J {}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
BugCheckerRefactoringTestHelper.newInstance(BugPatternLink.class, getClass())
|
||||
.addInputLines(
|
||||
"tech/picnic/errorprone/A.java",
|
||||
"package tech.picnic.errorprone;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"",
|
||||
"@BugPattern(",
|
||||
" summary = \"Error Prone Support class lacking link\",",
|
||||
" severity = BugPattern.SeverityLevel.ERROR)",
|
||||
"class A {}")
|
||||
.addOutputLines(
|
||||
"tech/picnic/errorprone/A.java",
|
||||
"package tech.picnic.errorprone;",
|
||||
"",
|
||||
"import static com.google.errorprone.BugPattern.LinkType.CUSTOM;",
|
||||
"import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"",
|
||||
"@BugPattern(",
|
||||
" link = BUG_PATTERNS_BASE_URL + \"A\",",
|
||||
" linkType = CUSTOM,",
|
||||
" summary = \"Error Prone Support class lacking link\",",
|
||||
" severity = BugPattern.SeverityLevel.ERROR)",
|
||||
"class A {}")
|
||||
.addInputLines(
|
||||
"tech/picnic/errorprone/B.java",
|
||||
"package tech.picnic.errorprone;",
|
||||
"",
|
||||
"import static com.google.errorprone.BugPattern.LinkType.CUSTOM;",
|
||||
"import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"",
|
||||
"@BugPattern(",
|
||||
" summary = \"Error Prone Support class with incorrect link\",",
|
||||
" link = \"Not the right link\",",
|
||||
" linkType = CUSTOM,",
|
||||
" severity = ERROR)",
|
||||
"class B {}")
|
||||
.addOutputLines(
|
||||
"tech/picnic/errorprone/B.java",
|
||||
"package tech.picnic.errorprone;",
|
||||
"",
|
||||
"import static com.google.errorprone.BugPattern.LinkType.CUSTOM;",
|
||||
"import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;",
|
||||
"import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;",
|
||||
"",
|
||||
"import com.google.errorprone.BugPattern;",
|
||||
"",
|
||||
"@BugPattern(",
|
||||
" summary = \"Error Prone Support class with incorrect link\",",
|
||||
" link = BUG_PATTERNS_BASE_URL + \"B\",",
|
||||
" linkType = CUSTOM,",
|
||||
" severity = ERROR)",
|
||||
"class B {}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
@@ -47,6 +47,7 @@ final class ErrorProneRuntimeClasspathTest {
|
||||
" m(\"com.google.errorprone.NonExistent\");",
|
||||
" m(\"com.google.common.NonExistent.toString\");",
|
||||
" m(\"java.lang.NonExistent\");",
|
||||
" m(\"com.google.common.collect.ImmutableEnumSet\");",
|
||||
" // BUG: Diagnostic matches: USE_CLASS_REFERENCE",
|
||||
" m(\"com.google.errorprone.BugPattern\");",
|
||||
" // BUG: Diagnostic matches: USE_CLASS_REFERENCE",
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
@@ -14,13 +14,13 @@ final class ErrorProneTestHelperSourceFormatTest {
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"import tech.picnic.errorprone.bugpatterns.EmptyMethod;",
|
||||
"import tech.picnic.errorprone.guidelines.bugpatterns.RefasterAnyOfUsage;",
|
||||
"",
|
||||
"class A {",
|
||||
" private final CompilationTestHelper compilationTestHelper =",
|
||||
" CompilationTestHelper.newInstance(EmptyMethod.class, getClass());",
|
||||
" CompilationTestHelper.newInstance(RefasterAnyOfUsage.class, getClass());",
|
||||
" private final BugCheckerRefactoringTestHelper refactoringTestHelper =",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass());",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(RefasterAnyOfUsage.class, getClass());",
|
||||
"",
|
||||
" void m() {",
|
||||
" compilationTestHelper",
|
||||
@@ -63,13 +63,13 @@ final class ErrorProneTestHelperSourceFormatTest {
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"import tech.picnic.errorprone.bugpatterns.EmptyMethod;",
|
||||
"import tech.picnic.errorprone.guidelines.bugpatterns.RefasterAnyOfUsage;",
|
||||
"",
|
||||
"class A {",
|
||||
" private final CompilationTestHelper compilationTestHelper =",
|
||||
" CompilationTestHelper.newInstance(EmptyMethod.class, getClass());",
|
||||
" CompilationTestHelper.newInstance(RefasterAnyOfUsage.class, getClass());",
|
||||
" private final BugCheckerRefactoringTestHelper refactoringTestHelper =",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass());",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(RefasterAnyOfUsage.class, getClass());",
|
||||
"",
|
||||
" void m() {",
|
||||
" compilationTestHelper",
|
||||
@@ -105,13 +105,13 @@ final class ErrorProneTestHelperSourceFormatTest {
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"import tech.picnic.errorprone.bugpatterns.EmptyMethod;",
|
||||
"import tech.picnic.errorprone.guidelines.bugpatterns.RefasterAnyOfUsage;",
|
||||
"",
|
||||
"class A {",
|
||||
" private final CompilationTestHelper compilationTestHelper =",
|
||||
" CompilationTestHelper.newInstance(EmptyMethod.class, getClass());",
|
||||
" CompilationTestHelper.newInstance(RefasterAnyOfUsage.class, getClass());",
|
||||
" private final BugCheckerRefactoringTestHelper refactoringTestHelper =",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass());",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(RefasterAnyOfUsage.class, getClass());",
|
||||
"",
|
||||
" void m() {",
|
||||
" compilationTestHelper",
|
||||
@@ -0,0 +1,263 @@
|
||||
package tech.picnic.errorprone.guidelines.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 ExhaustiveRefasterTypeMigrationTest {
|
||||
@Test
|
||||
void identification() {
|
||||
CompilationTestHelper.newInstance(ExhaustiveRefasterTypeMigration.class, getClass())
|
||||
.addSourceLines(
|
||||
"Util.java",
|
||||
"class Util {",
|
||||
" public static int CONSTANT = 42;",
|
||||
"",
|
||||
" public static void publicStaticVoidMethod() {}",
|
||||
"",
|
||||
" static void packagePrivateStaticVoidMethod() {}",
|
||||
"",
|
||||
" protected static void protectedStaticVoidMethod() {}",
|
||||
"",
|
||||
" private static void privateStaticVoidMethod() {}",
|
||||
"",
|
||||
" public static int publicStaticIntMethod2() {",
|
||||
" return 0;",
|
||||
" }",
|
||||
"",
|
||||
" public String publicStringMethodWithArg(int arg) {",
|
||||
" return String.valueOf(arg);",
|
||||
" }",
|
||||
"}")
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import com.google.errorprone.refaster.annotation.AfterTemplate;",
|
||||
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
|
||||
"import tech.picnic.errorprone.refaster.annotation.TypeMigration;",
|
||||
"",
|
||||
"class A {",
|
||||
" class UnannotatedEmptyClass {}",
|
||||
"",
|
||||
" // BUG: Diagnostic contains: Migration of type 'int' is unsupported",
|
||||
" @TypeMigration(of = int.class)",
|
||||
" class AnnotatedWithPrimitive {}",
|
||||
"",
|
||||
" @TypeMigration(",
|
||||
" of = Util.class,",
|
||||
" unmigratedMethods = {",
|
||||
" \"publicStaticIntMethod2()\",",
|
||||
" \"publicStringMethodWithArg(int)\",",
|
||||
" \"publicStaticVoidMethod()\"",
|
||||
" })",
|
||||
" class AnnotatedEmptyClass {}",
|
||||
"",
|
||||
" @TypeMigration(",
|
||||
" of = Util.class,",
|
||||
" unmigratedMethods = {",
|
||||
" \"publicStaticVoidMethod()\",",
|
||||
" \"publicStringMethodWithArg(int)\",",
|
||||
" \"publicStaticIntMethod2()\"",
|
||||
" })",
|
||||
" class AnnotatedEmptyClassWithUnsortedMethodListing {}",
|
||||
"",
|
||||
" class UnannotatedTemplate {",
|
||||
" @BeforeTemplate",
|
||||
" void before(int value) {",
|
||||
" Util.publicStaticVoidMethod();",
|
||||
" Util.publicStaticIntMethod2();",
|
||||
" new Util().publicStringMethodWithArg(value);",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" @TypeMigration(",
|
||||
" of = Util.class,",
|
||||
" unmigratedMethods = {",
|
||||
" \"publicStaticIntMethod2()\",",
|
||||
" \"publicStringMethodWithArg(int)\",",
|
||||
" \"publicStaticVoidMethod()\"",
|
||||
" })",
|
||||
" class AnnotatedWithoutBeforeTemplate {",
|
||||
" {",
|
||||
" Util.publicStaticIntMethod2();",
|
||||
" }",
|
||||
"",
|
||||
" @AfterTemplate",
|
||||
" void after(int value) {",
|
||||
" Util.publicStaticVoidMethod();",
|
||||
" new Util().publicStringMethodWithArg(value);",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" @TypeMigration(of = Util.class)",
|
||||
" class AnnotatedFullyMigrated {",
|
||||
" @BeforeTemplate",
|
||||
" void before() {",
|
||||
" new Util().publicStringMethodWithArg(Util.publicStaticIntMethod2());",
|
||||
" }",
|
||||
"",
|
||||
" @BeforeTemplate",
|
||||
" void before2() {",
|
||||
" Util.publicStaticVoidMethod();",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" @TypeMigration(of = Util.class, unmigratedMethods = \"publicStringMethodWithArg(int)\")",
|
||||
" class AnnotatedPartiallyMigrated {",
|
||||
" @BeforeTemplate",
|
||||
" void before() {",
|
||||
" Util.publicStaticVoidMethod();",
|
||||
" Util.publicStaticIntMethod2();",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" // BUG: Diagnostic contains: The set of unmigrated methods listed by the `@TypeMigration`",
|
||||
" // annotation must be minimal yet exhaustive",
|
||||
" @TypeMigration(of = Util.class, unmigratedMethods = \"publicStringMethodWithArg(int)\")",
|
||||
" class AnnotatedWithIncompleteMethodListing {",
|
||||
" @BeforeTemplate",
|
||||
" void before() {",
|
||||
" Util.publicStaticIntMethod2();",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" // BUG: Diagnostic contains: The set of unmigrated methods listed by the `@TypeMigration`",
|
||||
" // annotation must be minimal yet exhaustive",
|
||||
" @TypeMigration(",
|
||||
" of = Util.class,",
|
||||
" unmigratedMethods = {\"publicStaticIntMethod2()\", \"publicStringMethodWithArg(int)\"})",
|
||||
" class AnnotatedWithMigratedMethodReference {",
|
||||
" @BeforeTemplate",
|
||||
" void before() {",
|
||||
" Util.publicStaticVoidMethod();",
|
||||
" Util.publicStaticIntMethod2();",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" // BUG: Diagnostic contains: The set of unmigrated methods listed by the `@TypeMigration`",
|
||||
" // annotation must be minimal yet exhaustive",
|
||||
" @TypeMigration(",
|
||||
" of = Util.class,",
|
||||
" unmigratedMethods = {\"extra\", \"publicStringMethodWithArg(int)\"})",
|
||||
" class AnnotatedWithUnknownMethodReference {",
|
||||
" @BeforeTemplate",
|
||||
" void before() {",
|
||||
" Util.publicStaticVoidMethod();",
|
||||
" Util.publicStaticIntMethod2();",
|
||||
" }",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
BugCheckerRefactoringTestHelper.newInstance(ExhaustiveRefasterTypeMigration.class, getClass())
|
||||
.addInputLines(
|
||||
"Util.java",
|
||||
"public final class Util {",
|
||||
" public static void publicStaticVoidMethod() {}",
|
||||
"",
|
||||
" public static int publicStaticIntMethod2() {",
|
||||
" return 0;",
|
||||
" }",
|
||||
"",
|
||||
" public String publicStringMethodWithArg(int arg) {",
|
||||
" return String.valueOf(arg);",
|
||||
" }",
|
||||
"",
|
||||
" public String publicStringMethodWithArg(String arg) {",
|
||||
" return arg;",
|
||||
" }",
|
||||
"}")
|
||||
.expectUnchanged()
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
|
||||
"import tech.picnic.errorprone.refaster.annotation.TypeMigration;",
|
||||
"",
|
||||
"class A {",
|
||||
" @TypeMigration(of = Util.class)",
|
||||
" class AnnotatedWithoutMethodListing {",
|
||||
" {",
|
||||
" new Util().publicStringMethodWithArg(1);",
|
||||
" }",
|
||||
"",
|
||||
" @BeforeTemplate",
|
||||
" void before() {",
|
||||
" Util.publicStaticIntMethod2();",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" @TypeMigration(",
|
||||
" of = Util.class,",
|
||||
" unmigratedMethods = {\"publicStaticIntMethod2()\", \"extra\", \"publicStringMethodWithArg(int)\"})",
|
||||
" class AnnotatedWithIncorrectMethodReference {",
|
||||
" @BeforeTemplate",
|
||||
" void before() {",
|
||||
" new Util().publicStringMethodWithArg(\"1\");",
|
||||
" Util.publicStaticVoidMethod();",
|
||||
" Util.publicStaticIntMethod2();",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" @TypeMigration(",
|
||||
" of = Util.class,",
|
||||
" unmigratedMethods = {\"publicStaticVoidMethod()\", \"publicStaticVoidMethod()\"})",
|
||||
" class AnnotatedWithDuplicateMethodReference {",
|
||||
" @BeforeTemplate",
|
||||
" void before() {",
|
||||
" new Util().publicStringMethodWithArg(1);",
|
||||
" new Util().publicStringMethodWithArg(\"1\");",
|
||||
" Util.publicStaticIntMethod2();",
|
||||
" }",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
|
||||
"import tech.picnic.errorprone.refaster.annotation.TypeMigration;",
|
||||
"",
|
||||
"class A {",
|
||||
" @TypeMigration(",
|
||||
" unmigratedMethods = {",
|
||||
" \"publicStaticVoidMethod()\",",
|
||||
" \"publicStringMethodWithArg(int)\",",
|
||||
" \"publicStringMethodWithArg(String)\",",
|
||||
" \"Util()\"",
|
||||
" },",
|
||||
" of = Util.class)",
|
||||
" class AnnotatedWithoutMethodListing {",
|
||||
" {",
|
||||
" new Util().publicStringMethodWithArg(1);",
|
||||
" }",
|
||||
"",
|
||||
" @BeforeTemplate",
|
||||
" void before() {",
|
||||
" Util.publicStaticIntMethod2();",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" @TypeMigration(of = Util.class, unmigratedMethods = \"publicStringMethodWithArg(int)\")",
|
||||
" class AnnotatedWithIncorrectMethodReference {",
|
||||
" @BeforeTemplate",
|
||||
" void before() {",
|
||||
" new Util().publicStringMethodWithArg(\"1\");",
|
||||
" Util.publicStaticVoidMethod();",
|
||||
" Util.publicStaticIntMethod2();",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" @TypeMigration(of = Util.class, unmigratedMethods = \"publicStaticVoidMethod()\")",
|
||||
" class AnnotatedWithDuplicateMethodReference {",
|
||||
" @BeforeTemplate",
|
||||
" void before() {",
|
||||
" new Util().publicStringMethodWithArg(1);",
|
||||
" new Util().publicStringMethodWithArg(\"1\");",
|
||||
" Util.publicStaticIntMethod2();",
|
||||
" }",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.guidelines.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user