mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 08:11:25 +00:00
Compare commits
80 Commits
sschroever
...
gdejong/do
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2890bb983f | ||
|
|
f7165a84b6 | ||
|
|
b0ad824db8 | ||
|
|
e2b79dac47 | ||
|
|
dada0f23f1 | ||
|
|
20ed8568c2 | ||
|
|
546d3e3739 | ||
|
|
eca96a76d2 | ||
|
|
530fd8da1f | ||
|
|
c4e476a731 | ||
|
|
5ca95eb36d | ||
|
|
9204ef0e84 | ||
|
|
ed45be4e15 | ||
|
|
0561c371de | ||
|
|
aa5ad4d25b | ||
|
|
8c0041a94e | ||
|
|
397f9c3df7 | ||
|
|
2ba7bf9f46 | ||
|
|
5b079eef84 | ||
|
|
a2ce053daf | ||
|
|
ba02bad9bf | ||
|
|
d97a20247f | ||
|
|
50970eb932 | ||
|
|
7e7318ad80 | ||
|
|
fb6fe5a96e | ||
|
|
e37da2a1ed | ||
|
|
7bef1c8e67 | ||
|
|
0160eafca0 | ||
|
|
891fecd297 | ||
|
|
7f18bd9030 | ||
|
|
1473a70de8 | ||
|
|
c0c3ce2644 | ||
|
|
100d5c86f7 | ||
|
|
e12f99975b | ||
|
|
791113669f | ||
|
|
564bc7e1d1 | ||
|
|
43bcbeaa98 | ||
|
|
d682b7d41f | ||
|
|
72a124a20e | ||
|
|
326f3328a7 | ||
|
|
ae7068f464 | ||
|
|
066931fe53 | ||
|
|
3874ca9be2 | ||
|
|
84ba3946d9 | ||
|
|
b675ff680f | ||
|
|
8e7d04a24c | ||
|
|
bfc951b61f | ||
|
|
b30562bbd8 | ||
|
|
9be85204ae | ||
|
|
6fbf1b0cb2 | ||
|
|
5a428e6e29 | ||
|
|
71163c0061 | ||
|
|
880be0dbdd | ||
|
|
62fe10f2cd | ||
|
|
f7ec28368a | ||
|
|
e34c2baf7c | ||
|
|
184ba8af51 | ||
|
|
63c3aa8259 | ||
|
|
7daabeee48 | ||
|
|
0acfd8a723 | ||
|
|
000fcefe92 | ||
|
|
1e3ad0fe32 | ||
|
|
b88a668819 | ||
|
|
4c8e125dcb | ||
|
|
4ab5dc4f32 | ||
|
|
7c667334cc | ||
|
|
b4b2afd130 | ||
|
|
4445c93f6e | ||
|
|
5a7d7ff89b | ||
|
|
7ef75e8f07 | ||
|
|
a1f2418805 | ||
|
|
39bfcc75ca | ||
|
|
753cdce29e | ||
|
|
36654883e5 | ||
|
|
d5372934ec | ||
|
|
f810530599 | ||
|
|
6928381403 | ||
|
|
5657a48552 | ||
|
|
50aaf77a9e | ||
|
|
b8e22ffef0 |
51
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
51
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
name: 🐛 Bug report
|
||||
about: Create a report to help us improve.
|
||||
title: ""
|
||||
labels: bug
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
### Describe the bug
|
||||
|
||||
<!-- Provide a clear and concise description of what the bug or issue is. -->
|
||||
|
||||
- [ ] I have verified that the issue is reproducible against the latest version
|
||||
of the project.
|
||||
- [ ] I have searched through existing issues to verify that this issue is not
|
||||
already known.
|
||||
|
||||
### Minimal Reproducible Example
|
||||
|
||||
<!-- Provide a clear and concise description of what happened. Please include
|
||||
steps on how to reproduce the issue. -->
|
||||
|
||||
```java
|
||||
If applicable and possible, please replace this section with the code that
|
||||
triggered the isue.
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Logs</summary>
|
||||
|
||||
```sh
|
||||
Please replace this sentence with log output, if applicable.
|
||||
```
|
||||
</details>
|
||||
|
||||
### Expected behavior
|
||||
|
||||
<!-- Provide a clear and concise description of what you expected to happen. -->
|
||||
|
||||
### Setup
|
||||
|
||||
<!-- Please complete the following information: -->
|
||||
|
||||
- Operating system (e.g. MacOS Monterey).
|
||||
- Java version (i.e. `java --version`, e.g. `17.0.3`).
|
||||
- Error Prone version (e.g. `2.15.0`).
|
||||
- Error Prone Support version (e.g. `0.3.0`).
|
||||
|
||||
### Additional context
|
||||
|
||||
<!-- Provide any other context about the problem here. -->
|
||||
57
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
57
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
name: 🚀 Request a new feature
|
||||
about: Suggest a new feature.
|
||||
title: ""
|
||||
labels: new feature
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
### Problem
|
||||
|
||||
<!-- Here, describe the context of the problem that you're facing, and which
|
||||
you'd like to be solved through Error Prone Support. -->
|
||||
|
||||
### Description of the proposed new feature
|
||||
|
||||
<!-- Please indicate the type of improvement. -->
|
||||
|
||||
- [ ] Support a stylistic preference.
|
||||
- [ ] Avoid a common gotcha, or potential problem.
|
||||
|
||||
<!--
|
||||
Here, provide a clear and concise description of the desired change.
|
||||
|
||||
If possible, provide a simple and minimal example using the following format:
|
||||
|
||||
I would like to rewrite the following code:
|
||||
```java
|
||||
// XXX: Write the code to match here.
|
||||
```
|
||||
|
||||
to:
|
||||
```java
|
||||
// XXX: Write the desired code here.
|
||||
```
|
||||
-->
|
||||
|
||||
### Considerations
|
||||
|
||||
<!--
|
||||
Here, mention any other aspects to consider. Relevant questions:
|
||||
|
||||
- If applicable, is the rewrite operation a clear improvement in all cases?
|
||||
- Are there special cases to consider?
|
||||
- Can we further generalize the proposed solution?
|
||||
- Are there alternative solutions we should consider?
|
||||
-->
|
||||
|
||||
### Participation
|
||||
|
||||
<!-- Pull requests are very welcome, and we happily review contributions. Are
|
||||
you up for the challenge? :D -->
|
||||
|
||||
- [ ] I am willing to submit a pull request to implement this improvement.
|
||||
|
||||
### Additional context
|
||||
|
||||
<!-- Provide any other context about the request here. -->
|
||||
29
.github/release.yml
vendored
Normal file
29
.github/release.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
changelog:
|
||||
exclude:
|
||||
labels:
|
||||
- "ignore-changelog"
|
||||
categories:
|
||||
- title: ":rocket: New Error Prone checks and Refaster templates"
|
||||
labels:
|
||||
- "new feature"
|
||||
- title: ":sparkles: Improvements"
|
||||
labels:
|
||||
- "improvement"
|
||||
- title: ":warning: Update considerations and deprecations"
|
||||
labels:
|
||||
- "breaking change"
|
||||
- "deprecation"
|
||||
- title: ":bug: Bug fixes"
|
||||
labels:
|
||||
- "bug"
|
||||
- "bug fix"
|
||||
- title: ":books: Documentation, test and build improvements"
|
||||
labels:
|
||||
- "documentation"
|
||||
- "chore"
|
||||
- title: ":chart_with_upwards_trend: Dependency upgrades"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- title: "Other changes"
|
||||
labels:
|
||||
- "*"
|
||||
11
.github/workflows/build.yaml
vendored
11
.github/workflows/build.yaml
vendored
@@ -2,8 +2,9 @@ name: Build and verify
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
branches: [$default-branch]
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -19,15 +20,15 @@ jobs:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3.0.2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v3.4.1
|
||||
uses: actions/setup-java@v3.5.1
|
||||
with:
|
||||
java-version: ${{ matrix.jdk }}
|
||||
distribution: temurin
|
||||
cache: maven
|
||||
- name: Display build environment details
|
||||
run: mvn --version
|
||||
- name: Build project against vanilla Error Prone
|
||||
run: mvn -T1C install
|
||||
- name: Build project against vanilla Error Prone, compile Javadoc
|
||||
run: mvn -T1C install javadoc:jar
|
||||
- name: Build project with self-check against Error Prone fork
|
||||
run: mvn -T1C clean verify -Perror-prone-fork -Pnon-maven-central -Pself-check -s settings.xml
|
||||
- name: Remove installed project artifacts
|
||||
|
||||
45
.github/workflows/deploy-website.yaml
vendored
Normal file
45
.github/workflows/deploy-website.yaml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: Update `error-prone.picnic.tech` website content
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [$default-branch]
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pages: write
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3.0.2
|
||||
- name: Configure Github Pages
|
||||
uses: actions/configure-pages@v2.1.1
|
||||
- name: Generate documentation
|
||||
run: ./generate-docs.sh
|
||||
- name: Build website with Jekyll
|
||||
uses: actions/jekyll-build-pages@v1.0.5
|
||||
with:
|
||||
source: website/
|
||||
destination: ./_site
|
||||
- name: Validate HTML output
|
||||
uses: anishathalye/proof-html@v1.4.1
|
||||
with:
|
||||
directory: ./_site
|
||||
check_external_hash: false
|
||||
- name: Upload website as artifact
|
||||
uses: actions/upload-pages-artifact@v1.0.4
|
||||
deploy:
|
||||
if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
|
||||
needs: build
|
||||
runs-on: ubuntu-22.04
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1.2.1
|
||||
@@ -2,6 +2,7 @@
|
||||
-XX:SoftRefLRUPolicyMSPerMB=10
|
||||
-XX:+UseParallelGC
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
|
||||
@@ -9,5 +10,4 @@
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
|
||||
--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
|
||||
--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
|
||||
|
||||
71
CONTRIBUTING.md
Normal file
71
CONTRIBUTING.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Contributing
|
||||
|
||||
Thank you for checking this document! This project is free software, and we
|
||||
(the maintainers) encourage and value any contribution.
|
||||
|
||||
Here are some guidelines to help you get started.
|
||||
|
||||
## 🐛 Reporting a bug
|
||||
|
||||
Like any non-trivial piece of software, this library is probably not bug-free.
|
||||
If you found a bug, feel free to [report the issue][error-prone-support-issues]
|
||||
on GitHub.
|
||||
|
||||
Before doing so, please:
|
||||
- Verify that the issue is reproducible against the latest version of the
|
||||
project.
|
||||
- Search through the existing set of issues to see whether the problem is
|
||||
already known. With some luck a solution is already in place, or a workaround
|
||||
may have been provided.
|
||||
|
||||
When filing a bug report, please include the following:
|
||||
- Any relevant information about your environment. This should generally
|
||||
include the output of `java --version`, as well as the version of Error Prone
|
||||
you're using.
|
||||
- A description of what is going on (e.g. logging output, stacktraces).
|
||||
- A minimum reproducible example, so that other developers can try to reproduce
|
||||
(and optionally fix) the bug.
|
||||
- Any additional information that may be relevant.
|
||||
|
||||
## 💡 Reporting an improvement
|
||||
|
||||
If you would like to see an improvement, you can file a [GitHub
|
||||
issue][error-prone-support-issues]. This is also a good idea when you're
|
||||
already working towards opening a pull request, as this allows for discussion
|
||||
around the idea.
|
||||
|
||||
## 🚀 Opening a pull request
|
||||
|
||||
All submissions, including submissions by project members, require approval by
|
||||
at least two reviewers. We use [GitHub pull
|
||||
requests][error-prone-support-pulls] for this purpose.
|
||||
|
||||
Before opening a pull request, please check whether there are any existing
|
||||
(open or closed) issues or pull requests addressing the same problem. This
|
||||
avoids double work or lots of time spent on a solution that may ultimately not
|
||||
be accepted. When in doubt, make sure to first raise an
|
||||
[issue][error-prone-support-issues] to discuss the idea.
|
||||
|
||||
To the extent possible, the pull request process guards our coding guidelines.
|
||||
Some pointers:
|
||||
- Checks should be _topical_: ideally they address a single concern.
|
||||
- Where possible checks should provide _fixes_, and ideally these are
|
||||
completely behavior-preserving. In order for a check to be adopted by users
|
||||
it must not "get in the way". So for a check that addresses a relatively
|
||||
trivial stylistic concern it is doubly important that the violations it
|
||||
detects can be auto-patched.
|
||||
- Make sure you have read Error Prone's [criteria for new
|
||||
checks][error-prone-criteria]. Most guidelines described there apply to this
|
||||
project as well, except that this project _does_ focus quite heavy on style
|
||||
enforcement. But that just makes the previous point doubly important.
|
||||
- Make sure that a check's [(mutation) test
|
||||
coverage][error-prone-support-mutation-tests] is or remains about as high as
|
||||
it can be. Not only does this lead to better tests, it also points out
|
||||
opportunities to simplify the code.
|
||||
- Please restrict the scope of a pull request to a single feature or fix. Don't
|
||||
sneak in unrelated changes; instead just open more than one pull request 😉.
|
||||
|
||||
[error-prone-criteria]: https://errorprone.info/docs/criteria
|
||||
[error-prone-support-issues]: https://github.com/PicnicSupermarket/error-prone-support/issues
|
||||
[error-prone-support-mutation-tests]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/run-mutation-tests.sh
|
||||
[error-prone-support-pulls]: https://github.com/PicnicSupermarket/error-prone-support/pulls
|
||||
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2022 Picnic Technologies BV
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
226
README.md
Normal file
226
README.md
Normal file
@@ -0,0 +1,226 @@
|
||||
<div align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="website/assets/images/logo-dark.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="website/assets/images/logo.svg">
|
||||
<img alt="Error Prone Support logo" src="website/assets/images/logo.svg" width="50%">
|
||||
</picture>
|
||||
</div>
|
||||
|
||||
# Error Prone Support
|
||||
|
||||
Error Prone Support is a [Picnic][picnic-blog]-opinionated extension of
|
||||
Google's [Error Prone][error-prone-orig-repo]. It aims to improve code quality,
|
||||
focussing on maintainability, consistency and avoidance of common pitfalls.
|
||||
|
||||
> Error Prone is a static analysis tool for Java that catches common
|
||||
> programming mistakes at compile-time.
|
||||
|
||||
[![Maven Central][maven-central-badge]][maven-central-search]
|
||||
[![GitHub Actions][github-actions-build-badge]][github-actions-build-master]
|
||||
[![License][license-badge]][license]
|
||||
[![PRs Welcome][pr-badge]][contributing]
|
||||
|
||||
[Getting started](#-getting-started) •
|
||||
[Developing Error Prone Support](#-developing-error-prone-support) •
|
||||
[How it works](#-how-it-works) • [Contributing](#%EF%B8%8F-contributing)
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Getting started
|
||||
|
||||
### Installation
|
||||
|
||||
This library is built on top of [Error Prone][error-prone-orig-repo]. To use
|
||||
it:
|
||||
|
||||
1. First, follow Error Prone's [installation
|
||||
guide][error-prone-installation-guide].
|
||||
2. Next, edit your `pom.xml` file to add one or more Error Prone Support
|
||||
modules to the `annotationProcessorPaths` of the `maven-compiler-plugin`:
|
||||
|
||||
```xml
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<!-- Error Prone itself. -->
|
||||
<path>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>error_prone_core</artifactId>
|
||||
<version>${error-prone.version}</version>
|
||||
</path>
|
||||
<!-- Error Prone Support's additional bug checkers. -->
|
||||
<path>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-contrib</artifactId>
|
||||
<version>${error-prone-support.version}</version>
|
||||
</path>
|
||||
<!-- Error Prone Support's Refaster templates. -->
|
||||
<path>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>refaster-runner</artifactId>
|
||||
<version>${error-prone-support.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs>
|
||||
<arg>
|
||||
-Xplugin:ErrorProne
|
||||
<!-- Add other Error Prone flags here. See
|
||||
https://errorprone.info/docs/flags. -->
|
||||
</arg>
|
||||
<arg>-XDcompilePolicy=simple</arg>
|
||||
</compilerArgs>
|
||||
<!-- Some checks raise warnings rather than errors. -->
|
||||
<showWarnings>true</showWarnings>
|
||||
<!-- Enable this if you'd like to fail your build upon warnings. -->
|
||||
<!-- <failOnWarning>true</failOnWarning> -->
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
```
|
||||
|
||||
<!-- XXX: Reference `oss-parent`'s `pom.xml` once that project also uses Error
|
||||
Prone Support. Alternatively reference this project's `self-check` profile
|
||||
definition. -->
|
||||
|
||||
### Seeing it in action
|
||||
|
||||
Consider the following example code:
|
||||
|
||||
```java
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class Example {
|
||||
static BigDecimal getNumber() {
|
||||
return BigDecimal.valueOf(0);
|
||||
}
|
||||
|
||||
public ImmutableSet<Integer> getSet() {
|
||||
ImmutableSet<Integer> set = ImmutableSet.of(1);
|
||||
return ImmutableSet.copyOf(set);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the [installation](#installation) was successful, then building the above
|
||||
code with Maven should yield two compiler warnings:
|
||||
|
||||
```sh
|
||||
$ mvn clean install
|
||||
...
|
||||
[INFO] -------------------------------------------------------------
|
||||
[WARNING] COMPILATION WARNING :
|
||||
[INFO] -------------------------------------------------------------
|
||||
[WARNING] Example.java:[9,34] [tech.picnic.errorprone.refastertemplates.BigDecimalTemplates.BigDecimalZero]
|
||||
Did you mean 'return BigDecimal.ZERO;'?
|
||||
[WARNING] Example.java:[13,35] [IdentityConversion] This method invocation appears redundant; remove it or suppress this warning and add a comment explaining its purpose
|
||||
(see https://error-prone.picnic.tech/bugpatterns/IdentityConversion)
|
||||
Did you mean 'return set;' or '@SuppressWarnings("IdentityConversion") public ImmutableSet<Integer> getSet() {'?
|
||||
[INFO] 2 warnings
|
||||
[INFO] -------------------------------------------------------------
|
||||
...
|
||||
```
|
||||
|
||||
Two things are kicking in here:
|
||||
|
||||
1. An Error Prone [`BugChecker`][error-prone-bugchecker] that flags unnecessary
|
||||
[identity conversions][bug-checks-identity-conversion].
|
||||
2. A [Refaster][refaster] template capable of
|
||||
[rewriting][refaster-templates-bigdecimal] expressions of the form
|
||||
`BigDecimal.valueOf(0)` and `new BigDecimal(0)` to `BigDecimal.ZERO`.
|
||||
|
||||
Be sure to check out all [bug checks][bug-checks] and [refaster
|
||||
templates][refaster-templates].
|
||||
|
||||
## 👷 Developing Error Prone Support
|
||||
|
||||
This is a [Maven][maven] project, so running `mvn clean install` performs a
|
||||
full clean build and installs the library to your local Maven repository. Some
|
||||
relevant flags:
|
||||
|
||||
- `-Dverification.warn` makes the warnings and errors emitted by various
|
||||
plugins and the Java compiler non-fatal, where possible.
|
||||
- `-Dverification.skip` disables various non-essential plugins and compiles the
|
||||
code with minimal checks (i.e. without linting, Error Prone checks, etc.).
|
||||
- `-Dversion.error-prone=some-version` runs the build using the specified
|
||||
version of Error Prone. This is useful e.g. when testing a locally built
|
||||
Error Prone SNAPSHOT.
|
||||
- `-Perror-prone-fork` runs the build using Picnic's [Error Prone
|
||||
fork][error-prone-fork-repo], hosted on [Jitpack][error-prone-fork-jitpack].
|
||||
This fork generally contains a few changes on top of the latest Error Prone
|
||||
release.
|
||||
- `-Pself-check` runs the checks defined by this project against itself.
|
||||
Pending a release of [google/error-prone#3301][error-prone-pull-3301], this
|
||||
flag must currently be used in combination with `-Perror-prone-fork`.
|
||||
|
||||
Some other commands one may find relevant:
|
||||
|
||||
- `mvn fmt:format` formats the code using
|
||||
[`google-java-format`][google-java-format].
|
||||
- `./run-mutation-tests.sh` runs mutation tests using [PIT][pitest]. The
|
||||
results can be reviewed by opening the respective
|
||||
`target/pit-reports/index.html` files. For more information check the [PIT
|
||||
Maven plugin][pitest-maven].
|
||||
- `./apply-error-prone-suggestions.sh` applies Error Prone and Error Prone
|
||||
Support code suggestions to this project. Before running this command, make
|
||||
sure to have installed the project (`mvn clean install`) and make sure that
|
||||
the current working directory does not contain unstaged or uncommited
|
||||
changes.
|
||||
|
||||
When running the project's tests in IntelliJ IDEA, you might see the following
|
||||
error:
|
||||
|
||||
```
|
||||
java: exporting a package from system module jdk.compiler is not allowed with --release
|
||||
```
|
||||
|
||||
If this happens, go to _Settings -> Build, Execution, Deployment -> Compiler ->
|
||||
Java Compiler_ and deselect the option _Use '--release' option for
|
||||
cross-compilation (Java 9 and later)_. See [IDEA-288052][idea-288052] for
|
||||
details.
|
||||
|
||||
## 💡 How it works
|
||||
|
||||
This project provides additional [`BugChecker`][error-prone-bugchecker]
|
||||
implementations.
|
||||
|
||||
<!-- XXX: Extend this section. -->
|
||||
|
||||
## ✍️ Contributing
|
||||
|
||||
Want to report or fix a bug, suggest or add a new feature, or improve the
|
||||
documentation? That's awesome! Please read our [contribution
|
||||
guidelines][contributing].
|
||||
|
||||
[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
|
||||
[contributing]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/CONTRIBUTING.md
|
||||
[error-prone-bugchecker]: https://github.com/google/error-prone/blob/master/check_api/src/main/java/com/google/errorprone/bugpatterns/BugChecker.java
|
||||
[error-prone-fork-jitpack]: https://jitpack.io/#PicnicSupermarket/error-prone
|
||||
[error-prone-fork-repo]: https://github.com/PicnicSupermarket/error-prone
|
||||
[error-prone-installation-guide]: https://errorprone.info/docs/installation#maven
|
||||
[error-prone-orig-repo]: https://github.com/google/error-prone
|
||||
[error-prone-pull-3301]: https://github.com/google/error-prone/pull/3301
|
||||
[github-actions-build-badge]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/build.yaml/badge.svg
|
||||
[github-actions-build-master]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/build.yaml?query=branch%3Amaster
|
||||
[google-java-format]: https://github.com/google/google-java-format
|
||||
[idea-288052]: https://youtrack.jetbrains.com/issue/IDEA-288052
|
||||
[license-badge]: https://img.shields.io/github/license/PicnicSupermarket/error-prone-support
|
||||
[license]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/LICENSE.md
|
||||
[maven-central-badge]: https://img.shields.io/maven-central/v/tech.picnic.error-prone-support/error-prone-support?color=blue
|
||||
[maven-central-search]: https://search.maven.org/artifact/tech.picnic.error-prone-support/error-prone-support
|
||||
[maven]: https://maven.apache.org
|
||||
[picnic-blog]: https://blog.picnic.nl
|
||||
[pitest]: https://pitest.org
|
||||
[pitest-maven]: https://pitest.org/quickstart/maven
|
||||
[pr-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
|
||||
[refaster]: https://errorprone.info/docs/refaster
|
||||
[refaster-templates-bigdecimal]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/refastertemplates/BigDecimalTemplates.java
|
||||
[refaster-templates]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/refastertemplates/
|
||||
53
docgen/pom.xml
Normal file
53
docgen/pom.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?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.3.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>docgen</artifactId>
|
||||
|
||||
<name>Picnic :: Error Prone Support :: Docgen</name>
|
||||
<description>Docgen.</description>
|
||||
|
||||
<dependencies>
|
||||
<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>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${groupId.error-prone}</groupId>
|
||||
<artifactId>error_prone_core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto.service</groupId>
|
||||
<artifactId>auto-service-annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto.value</groupId>
|
||||
<artifactId>auto-value-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,15 @@
|
||||
package tech.picnic.errorprone.plugin;
|
||||
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.util.TaskEvent;
|
||||
import tech.picnic.errorprone.plugin.objects.BugPatternData;
|
||||
|
||||
public final class BugPatternExtractor implements DocExtractor<BugPatternData> {
|
||||
@Override
|
||||
public BugPatternData extractData(ClassTree tree, TaskEvent taskEvent, VisitorState state) {
|
||||
BugPattern annotation = taskEvent.getTypeElement().getAnnotation(BugPattern.class);
|
||||
return BugPatternData.create(annotation, taskEvent.getTypeElement().getSimpleName().toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package tech.picnic.errorprone.plugin;
|
||||
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
|
||||
import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.methodIsNamed;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.util.TaskEvent;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import tech.picnic.errorprone.plugin.objects.BugPatternTestData;
|
||||
|
||||
public final class BugPatternTestsExtractor implements DocExtractor<BugPatternTestData> {
|
||||
private static final Matcher<MethodTree> BUG_PATTERN_TEST =
|
||||
allOf(
|
||||
hasAnnotation("org.junit.jupiter.api.Test"),
|
||||
anyOf(methodIsNamed("replacement"), methodIsNamed("identification")));
|
||||
private static final Matcher<ExpressionTree> IDENTIFICATION_SOURCE_LINES =
|
||||
instanceMethod()
|
||||
.onDescendantOf("com.google.errorprone.CompilationTestHelper")
|
||||
.named("addSourceLines");
|
||||
private static final Matcher<ExpressionTree> REPLACEMENT_INPUT =
|
||||
instanceMethod()
|
||||
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper")
|
||||
.named("addInputLines");
|
||||
private static final Matcher<ExpressionTree> REPLACEMENT_OUTPUT =
|
||||
instanceMethod()
|
||||
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper.ExpectOutput")
|
||||
.named("addOutputLines");
|
||||
|
||||
@Override
|
||||
public BugPatternTestData extractData(ClassTree tree, TaskEvent taskEvent, VisitorState state) {
|
||||
String name = tree.getSimpleName().toString().replace("Test", "");
|
||||
ScanBugCheckerTestData scanner = new ScanBugCheckerTestData(state);
|
||||
|
||||
tree.getMembers().stream()
|
||||
.filter(MethodTree.class::isInstance)
|
||||
.map(MethodTree.class::cast)
|
||||
.filter(m -> BUG_PATTERN_TEST.matches(m, state))
|
||||
.forEach(m -> scanner.scan(m, null));
|
||||
|
||||
return BugPatternTestData.create(
|
||||
name, scanner.getIdentification(), scanner.getInput(), scanner.getOutput());
|
||||
}
|
||||
|
||||
private static final class ScanBugCheckerTestData extends TreeScanner<Void, Void> {
|
||||
private final VisitorState state;
|
||||
|
||||
private String identification;
|
||||
private String input;
|
||||
private String output;
|
||||
|
||||
ScanBugCheckerTestData(VisitorState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getIdentification() {
|
||||
return identification;
|
||||
}
|
||||
|
||||
public String getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
public String getOutput() {
|
||||
return output;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Void visitMethodInvocation(MethodInvocationTree node, Void unused) {
|
||||
if (IDENTIFICATION_SOURCE_LINES.matches(node, state)) {
|
||||
identification = getSourceLines(node);
|
||||
} else if (REPLACEMENT_INPUT.matches(node, state)) {
|
||||
input = getSourceLines(node);
|
||||
} else if (REPLACEMENT_OUTPUT.matches(node, state)) {
|
||||
output = getSourceLines(node);
|
||||
}
|
||||
return super.visitMethodInvocation(node, unused);
|
||||
}
|
||||
|
||||
private String getSourceLines(MethodInvocationTree tree) {
|
||||
List<? extends ExpressionTree> sourceLines =
|
||||
tree.getArguments().subList(1, tree.getArguments().size());
|
||||
|
||||
return getConstantSourceCode(sourceLines);
|
||||
}
|
||||
|
||||
private String getConstantSourceCode(List<? extends ExpressionTree> sourceLines) {
|
||||
StringBuilder source = new StringBuilder();
|
||||
|
||||
for (ExpressionTree sourceLine : sourceLines) {
|
||||
Object value = ASTHelpers.constValue(sourceLine);
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
source.append(value).append('\n');
|
||||
}
|
||||
|
||||
return source.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package tech.picnic.errorprone.plugin;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.util.TaskEvent;
|
||||
|
||||
public interface DocExtractor<T> {
|
||||
T extractData(ClassTree tree, TaskEvent taskEvent, VisitorState state);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package tech.picnic.errorprone.plugin;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.sun.source.util.JavacTask;
|
||||
import com.sun.source.util.Plugin;
|
||||
import com.sun.tools.javac.api.BasicJavacTask;
|
||||
|
||||
/**
|
||||
* A variant of {@code com.google.errorprone.refaster.RefasterRuleCompiler} that outputs a {@code
|
||||
* fully/qualified/Class.refaster} file for each compiled {@code fully.qualified.Class} that
|
||||
* contains a Refaster template.
|
||||
*/
|
||||
@AutoService(Plugin.class)
|
||||
public final class Docgen implements Plugin {
|
||||
@Override
|
||||
public String getName() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(JavacTask javacTask, String... args) {
|
||||
javacTask.addTaskListener(
|
||||
new DocgenTaskListener(((BasicJavacTask) javacTask).getContext(), args[0]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package tech.picnic.errorprone.plugin;
|
||||
|
||||
public enum DocgenPart {
|
||||
BUGPATTERN("bug-pattern-test-data.jsonl", new BugPatternExtractor()),
|
||||
BUGPATTERN_TEST("bug-pattern-data.jsonl", new BugPatternTestsExtractor()),
|
||||
REFASTER_TEMPLATE_TEST_INPUT("refaster-test-input-data.jsonl", new RefasterTestExtractor()),
|
||||
REFASTER_TEMPLATE_TEST_OUTPUT("refaster-test-output-data.jsonl", new RefasterTestExtractor());
|
||||
|
||||
private final String dataFileName;
|
||||
private final DocExtractor<?> extractor;
|
||||
|
||||
DocgenPart(String dataFileName, DocExtractor<?> extractor) {
|
||||
this.dataFileName = dataFileName;
|
||||
this.extractor = extractor;
|
||||
}
|
||||
|
||||
public DocExtractor<?> getExtractor() {
|
||||
return extractor;
|
||||
}
|
||||
|
||||
public String getDataFileName() {
|
||||
return dataFileName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package tech.picnic.errorprone.plugin;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.VariableTree;
|
||||
import com.sun.source.util.TaskEvent;
|
||||
import com.sun.source.util.TaskListener;
|
||||
import com.sun.tools.javac.api.JavacTrees;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
/** XXX: Write this. */
|
||||
final class DocgenTaskListener implements TaskListener {
|
||||
private final Context context;
|
||||
|
||||
private final String basePath;
|
||||
|
||||
private final VisitorState state;
|
||||
|
||||
private final ObjectMapper mapper =
|
||||
new ObjectMapper()
|
||||
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
|
||||
.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
|
||||
|
||||
DocgenTaskListener(Context context, String path) {
|
||||
this.context = context;
|
||||
this.basePath = path.substring(path.indexOf('=') + 1);
|
||||
this.state = VisitorState.createForUtilityPurposes(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("SystemOut")
|
||||
public void finished(TaskEvent taskEvent) {
|
||||
ClassTree tree = JavacTrees.instance(context).getTree(taskEvent.getTypeElement());
|
||||
if (tree == null || taskEvent.getSourceFile() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
getDocgenPart(tree, taskEvent)
|
||||
.ifPresent(
|
||||
docgenPart ->
|
||||
writeToFile(
|
||||
docgenPart.getExtractor().extractData(tree, taskEvent, state),
|
||||
docgenPart.getDataFileName()));
|
||||
}
|
||||
|
||||
private static Optional<DocgenPart> getDocgenPart(ClassTree tree, TaskEvent taskEvent) {
|
||||
JavaFileObject sourceFile = taskEvent.getSourceFile();
|
||||
|
||||
if (isBugPatternTest(tree)) {
|
||||
return Optional.of(DocgenPart.BUGPATTERN_TEST);
|
||||
} else if (isBugPattern(tree)) {
|
||||
return Optional.of(DocgenPart.BUGPATTERN);
|
||||
} else if (sourceFile.getName().contains("TestInput")) {
|
||||
return Optional.of(DocgenPart.REFASTER_TEMPLATE_TEST_INPUT);
|
||||
} else if (sourceFile.getName().contains("TestOutput")) {
|
||||
return Optional.of(DocgenPart.REFASTER_TEMPLATE_TEST_OUTPUT);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void writeToFile(T data, String fileName) {
|
||||
File file = new File(basePath + "/" + fileName);
|
||||
|
||||
try (FileWriter fileWriter = new FileWriter(file, true)) {
|
||||
mapper.writeValue(fileWriter, data);
|
||||
fileWriter.write("\n");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isBugPattern(ClassTree tree) {
|
||||
return ASTHelpers.hasDirectAnnotationWithSimpleName(tree, BugPattern.class.getSimpleName());
|
||||
}
|
||||
|
||||
private static boolean isBugPatternTest(ClassTree tree) {
|
||||
return tree.getSimpleName().toString().endsWith("Test")
|
||||
&& tree.getMembers().stream()
|
||||
.filter(VariableTree.class::isInstance)
|
||||
.map(VariableTree.class::cast)
|
||||
.anyMatch(
|
||||
member -> member.getType().toString().equals("BugCheckerRefactoringTestHelper"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package tech.picnic.errorprone.plugin;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.util.TaskEvent;
|
||||
import tech.picnic.errorprone.plugin.objects.RefasterTemplateTestData;
|
||||
|
||||
public class RefasterTestExtractor
|
||||
implements DocExtractor<ImmutableList<RefasterTemplateTestData>> {
|
||||
@Override
|
||||
public ImmutableList<RefasterTemplateTestData> extractData(
|
||||
ClassTree tree, TaskEvent taskEvent, VisitorState state) {
|
||||
|
||||
String templateCollectionName = tree.getSimpleName().toString().replace("Test", "");
|
||||
|
||||
return tree.getMembers().stream()
|
||||
.filter(MethodTree.class::isInstance)
|
||||
.map(MethodTree.class::cast)
|
||||
.filter(m -> m.getName().toString().startsWith("test"))
|
||||
.map(
|
||||
m ->
|
||||
RefasterTemplateTestData.create(
|
||||
templateCollectionName,
|
||||
m.getName().toString().replace("test", ""),
|
||||
m.toString()))
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package tech.picnic.errorprone.plugin.objects;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.BugPattern.LinkType;
|
||||
import com.google.errorprone.BugPattern.SeverityLevel;
|
||||
import java.util.Arrays;
|
||||
|
||||
@AutoValue
|
||||
public abstract class BugPatternData {
|
||||
public static BugPatternData create(BugPattern annotation, String name) {
|
||||
return new AutoValue_BugPatternData(
|
||||
name,
|
||||
Arrays.toString(annotation.altNames()),
|
||||
annotation.linkType(),
|
||||
annotation.link(),
|
||||
Arrays.toString(annotation.tags()),
|
||||
annotation.summary(),
|
||||
annotation.explanation(),
|
||||
annotation.severity(),
|
||||
annotation.disableable());
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
abstract String name();
|
||||
|
||||
// Should be String[]
|
||||
@JsonProperty
|
||||
abstract String altNames();
|
||||
|
||||
@JsonProperty
|
||||
abstract LinkType linkType();
|
||||
|
||||
@JsonProperty
|
||||
abstract String link();
|
||||
|
||||
@JsonProperty
|
||||
// Should be String[]
|
||||
abstract String tags();
|
||||
|
||||
@JsonProperty
|
||||
abstract String summary();
|
||||
|
||||
@JsonProperty
|
||||
abstract String explanation();
|
||||
|
||||
@JsonProperty
|
||||
abstract SeverityLevel severityLevel();
|
||||
|
||||
@JsonProperty
|
||||
abstract boolean disableable();
|
||||
|
||||
// SuppressionAnnotations?
|
||||
// DocumentSuppression?
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package tech.picnic.errorprone.plugin.objects;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@AutoValue
|
||||
public abstract class BugPatternTestData {
|
||||
public static BugPatternTestData create(
|
||||
String name, String identificationLines, String inputLines, String outputLines) {
|
||||
return new AutoValue_BugPatternTestData(name, identificationLines, inputLines, outputLines);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
abstract String name();
|
||||
|
||||
@Nullable
|
||||
@JsonProperty
|
||||
abstract String identificationLines();
|
||||
|
||||
@Nullable
|
||||
@JsonProperty
|
||||
abstract String inputLines();
|
||||
|
||||
@Nullable
|
||||
@JsonProperty
|
||||
abstract String outputLines();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package tech.picnic.errorprone.plugin.objects;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.errorprone.BugPattern.SeverityLevel;
|
||||
|
||||
@AutoValue
|
||||
public abstract class RefasterTemplateData {
|
||||
public static RefasterTemplateData create(
|
||||
String name, String description, String link, SeverityLevel severityLevel) {
|
||||
return new AutoValue_RefasterTemplateData(name, description, link, severityLevel);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
abstract String name();
|
||||
|
||||
@JsonProperty
|
||||
abstract String description();
|
||||
|
||||
@JsonProperty
|
||||
abstract String link();
|
||||
|
||||
@JsonProperty
|
||||
abstract SeverityLevel severityLevel();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package tech.picnic.errorprone.plugin.objects;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.auto.value.AutoValue;
|
||||
|
||||
@AutoValue
|
||||
public abstract class RefasterTemplateTestData {
|
||||
public static RefasterTemplateTestData create(
|
||||
String templateCollection, String templateName, String templateTestContent) {
|
||||
return new AutoValue_RefasterTemplateTestData(
|
||||
templateCollection, templateName, templateTestContent);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
abstract String templateCollection();
|
||||
|
||||
@JsonProperty
|
||||
abstract String templateName();
|
||||
|
||||
@JsonProperty
|
||||
abstract String templateTestContent();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/** A Java compiler plugin that XXX: fill in. */
|
||||
@com.google.errorprone.annotations.CheckReturnValue
|
||||
@javax.annotation.ParametersAreNonnullByDefault
|
||||
package tech.picnic.errorprone.plugin;
|
||||
@@ -11,71 +11,22 @@ request.
|
||||
|
||||
### Building
|
||||
|
||||
This is a [Maven][maven] project, so running `mvn clean install` performs a
|
||||
full clean build. Some relevant flags:
|
||||
- `-Dverification.warn` makes the warnings and errors emitted by various
|
||||
plugins and the Java compiler non-fatal, where possible.
|
||||
- `-Dverification.skip` disables various non-essential plugins and compiles the
|
||||
code with minimal checks (i.e. without linting, Error Prone checks, etc.)
|
||||
- `-Dversion.error-prone=some-version` runs the build using the specified
|
||||
version of Error Prone. This is useful e.g. when testing a locally built
|
||||
Error Prone SNAPSHOT.
|
||||
- `-Perror-prone-fork` run the build using Picnic's [Error Prone
|
||||
fork][error-prone-fork-repo], hosted on [Jitpack][error-prone-fork-jitpack].
|
||||
This fork generally contains a few changes on top of the latest Error Prone
|
||||
release.
|
||||
|
||||
Two other goals that one may find relevant:
|
||||
- `mvn fmt:format` formats the code using
|
||||
[`google-java-format`][google-java-format].
|
||||
- `mvn pitest:mutationCoverage` runs mutation tests using [PIT][pitest]. The
|
||||
results can be reviewed by opening the respective
|
||||
`target/pit-reports/index.html` files. For more information check the [PIT
|
||||
Maven plugin][pitest-maven].
|
||||
|
||||
When loading the project in IntelliJ IDEA (and perhaps other IDEs) errors about
|
||||
the inaccessibility of `com.sun.tools.javac.*` classes may be reported. If this
|
||||
happens, configure your IDE to enable the `add-exports` profile.
|
||||
See the main [readme][main-readme].
|
||||
|
||||
### Contribution guidelines
|
||||
|
||||
To the extend possible, the pull request process guards our coding guidelines.
|
||||
Some pointers:
|
||||
- Checks should we _topical_: Ideally they address a single concern.
|
||||
- Where possible checks should provide _fixes_, and ideally these are
|
||||
completely behavior preserving. In order for a check to be adopted by users
|
||||
it must not "get in the way". So for a check which addresses a relatively
|
||||
trivial stylistic concern it is doubly important that the violations it
|
||||
detects can be auto-patched.
|
||||
- Make sure you have read Error Prone's [criteria for new
|
||||
checks][error-prone-criteria]. Most guidelines described there apply to this
|
||||
project as well, except that this project _does_ focus quite heavy on style
|
||||
enforcement. But that just makes the previous point doubly important.
|
||||
- Make sure that a check's (mutation) coverage is or remains about as high as
|
||||
it can be. Not only does this lead to better tests, it also points out
|
||||
opportunities to simplify the code.
|
||||
- Please restrict the scope of a pull request to a single feature or fix. Don't
|
||||
sneak in unrelated changes.
|
||||
- When in doubt about whether a pull request will be accepted, please first
|
||||
file an issue to discuss it.
|
||||
See our [contributing guidelines][main-contributing].
|
||||
|
||||
### Our wishlist
|
||||
|
||||
We expect the following tasks to help improve the quality of this open source
|
||||
project:
|
||||
|
||||
- Publish the artifact to Maven Central, then document the coordinates in this
|
||||
`README.md`.
|
||||
- Document how to enable the checks.
|
||||
- Document how to apply patches.
|
||||
- Document each of the checks.
|
||||
- Add Travis CI, [SonarQube][sonarcloud] and [Codecov][codecov]
|
||||
integrations.
|
||||
- Investigate whether it makes sense to include license headers in each file.
|
||||
If so, set that up and enforce it.
|
||||
- Add [SonarQube][sonarcloud] and [Codecov][codecov] integrations.
|
||||
- Add non-Java file formatting support, like we have internally at Picnic.
|
||||
(I.e., somehow open-source that stuff.)
|
||||
- Add relevant "badges" at the top of this `README.md`.
|
||||
- Auto-generate a website listing each of the checks, just like the Error Prone
|
||||
[bug patterns page][error-prone-bug-patterns]. The [Error Prone
|
||||
repository][error-prone-repo] contains code for this.
|
||||
@@ -93,7 +44,7 @@ project:
|
||||
- Improve an existing check (see `XXX`-marked comments in the code) or write a
|
||||
new one (see the list of suggestions below).
|
||||
|
||||
### Ideas for new checks
|
||||
### BugChecker extension ideas
|
||||
|
||||
The following is a list of checks we'd like to see implemented:
|
||||
|
||||
@@ -103,7 +54,7 @@ The following is a list of checks we'd like to see implemented:
|
||||
signature groups. Using Error Prone's method matchers forbidden method calls
|
||||
can easily be identified. But Error Prone can go one step further by
|
||||
auto-patching violations. For each violation two fixes can be proposed: a
|
||||
purely behavior-preserving fix which makes the platform-dependent behavior
|
||||
purely behavior-preserving fix, which makes the platform-dependent behavior
|
||||
explicit, and another which replaces the platform-dependent behavior with the
|
||||
preferred alternative. (Such as using `UTF-8` instead of the system default
|
||||
charset.)
|
||||
@@ -113,128 +64,128 @@ The following is a list of checks we'd like to see implemented:
|
||||
functionality.
|
||||
- A subset of the refactor operations provided by the Eclipse-specific
|
||||
[AutoRefactor][autorefactor] plugin.
|
||||
- A check which replaces fully qualified types with simple types in contexts
|
||||
- A check that replaces fully qualified types with simple types in contexts
|
||||
where this does not introduce ambiguity. Should consider both actual Java
|
||||
code and Javadoc `@link` references.
|
||||
- A check which simplifies array expressions. It would replace empty array
|
||||
- A check that simplifies array expressions. It would replace empty array
|
||||
expressions of the form `new int[] {}` with `new int[0]`. Statements of the
|
||||
form `byte[] arr = new byte[] {'c'};` would be shortened to `byte[] arr =
|
||||
{'c'};`.
|
||||
- A check which replaces expressions of the form `String.format("some prefix
|
||||
- A check that replaces expressions of the form `String.format("some prefix
|
||||
%s", arg)` with `"some prefix " + arg`, and similar for simple suffixes. Can
|
||||
perhaps be generalized further, though it's unclear how far. (Well, a
|
||||
`String.format` call without arguments can certainly be simplified, too.)
|
||||
- A check which replaces single-character strings with `char`s where possible.
|
||||
- A check that replaces single-character strings with `char`s where possible.
|
||||
For example as argument to `StringBuilder.append` and in string
|
||||
concatenations.
|
||||
- A check which adds or removes the first `Locale` argument to `String.format`
|
||||
- A check that adds or removes the first `Locale` argument to `String.format`
|
||||
and similar calls as necessary. (For example, a format string containing only
|
||||
`%s` placeholders is locale-insensitive unless any of the arguments is a
|
||||
`Formattable`, while `%f` placeholders _are_ locale-sensitive.)
|
||||
- A check which replaces `String.replaceAll` with `String.replace` if the first
|
||||
- A check that replaces `String.replaceAll` with `String.replace` if the first
|
||||
argument is certainly not a regular expression. And if both arguments are
|
||||
single-character strings then the `(char, char)` overload can be invoked
|
||||
instead.
|
||||
- A check which flags (and ideally, replaces) `try-finally` constructs with
|
||||
- A check that flags (and ideally, replaces) `try-finally` constructs with
|
||||
equivalent `try-with-resources` constructs.
|
||||
- A check which drops exceptions declared in `throws` clauses if they are (a)
|
||||
- A check that drops exceptions declared in `throws` clauses if they are (a)
|
||||
not actually thrown and (b) the associated method cannot be overridden.
|
||||
- A check which tries to statically import certain methods whenever used, if
|
||||
- A check that tries to statically import certain methods whenever used, if
|
||||
possible. The set of targeted methods should be configurable, but may default
|
||||
to e.g. `java.util.Function.identity()`, the static methods exposed by
|
||||
`java.util.stream.Collectors` and the various Guava collector factory
|
||||
methods.
|
||||
- A check which replaces `new Random().someMethod()` calls with
|
||||
`ThreadLocalRandom.current().someMethod()` calls, so as to avoid unnecessary
|
||||
- A check that replaces `new Random().someMethod()` calls with
|
||||
`ThreadLocalRandom.current().someMethod()` calls, to avoid unnecessary
|
||||
synchronization.
|
||||
- A check which drops `this.` from `this.someMethod()` calls and which
|
||||
- A check that drops `this.` from `this.someMethod()` calls and which
|
||||
optionally does the same for fields, if no ambiguity arises.
|
||||
- A check which replaces `Integer.valueOf` calls with `Integer.parseInt` or
|
||||
vice versa in order to prevent auto (un)boxing, and likewise for other number
|
||||
- A check that replaces `Integer.valueOf` calls with `Integer.parseInt` or vice
|
||||
versa in order to prevent auto (un)boxing, and likewise for other number
|
||||
types.
|
||||
- A check which flags nullable collections.
|
||||
- A check which flags `AutoCloseable` resources not managed by a
|
||||
- A check that flags nullable collections.
|
||||
- A check that flags `AutoCloseable` resources not managed by a
|
||||
`try-with-resources` construct. Certain subtypes, such as jOOQ's `DSLContext`
|
||||
should be excluded.
|
||||
- A check which flags `java.time` methods which implicitly consult the system
|
||||
- A check that flags `java.time` methods which implicitly consult the system
|
||||
clock, suggesting that a passed-in `Clock` is used instead.
|
||||
- A check which flags public methods on public classes which reference
|
||||
- A check that flags public methods on public classes which reference
|
||||
non-public types. This can cause `IllegalAccessError`s and
|
||||
`BootstrapMethodError`s at runtime.
|
||||
- A check which swaps the LHS and RHS in expressions of the form
|
||||
- A check that swaps the LHS and RHS in expressions of the form
|
||||
`nonConstant.equals(someNonNullConstant)`.
|
||||
- A check which annotates methods which only throw an exception with
|
||||
- A check that annotates methods which only throw an exception with
|
||||
`@Deprecated` or ` @DoNotCall`.
|
||||
- A check which flags imports from other test classes.
|
||||
- A Guava-specific check which replaces `Joiner.join` calls with `String.join`
|
||||
- A check that flags imports from other test classes.
|
||||
- A Guava-specific check that replaces `Joiner.join` calls with `String.join`
|
||||
calls in those cases where the latter is a proper substitute for the former.
|
||||
- A Guava-specific check which flags `{Immutable,}Multimap` type usages
|
||||
where `{Immutable,}{List,Set}Multimap` would be more appropriate.
|
||||
- A Guava-specific check which rewrites `if (conditional) { throw new
|
||||
- A Guava-specific check that flags `{Immutable,}Multimap` type usages where
|
||||
`{Immutable,}{List,Set}Multimap` would be more appropriate.
|
||||
- A Guava-specific check that rewrites `if (conditional) { throw new
|
||||
IllegalArgumentException(); }` and variants to an equivalent `checkArgument`
|
||||
statement. Idem for other exception types.
|
||||
- A Guava-specific check which replaces simple anonymous `CacheLoader` subclass
|
||||
- A Guava-specific check that replaces simple anonymous `CacheLoader` subclass
|
||||
declarations with `CacheLoader.from(someLambda)`.
|
||||
- A Spring-specific check which enforces that methods with the `@Scheduled`
|
||||
- A Spring-specific check that enforces that methods with the `@Scheduled`
|
||||
annotation are also annotated with New Relic's `@Trace` annotation. Such
|
||||
methods should ideally not also represent Spring MVC endpoints.
|
||||
- A Spring-specific check which enforces that `@RequestMapping` annotations,
|
||||
- A Spring-specific check that enforces that `@RequestMapping` annotations,
|
||||
when applied to a method, explicitly specify one or more target HTTP methods.
|
||||
- A Spring-specific check which looks for classes in which all
|
||||
`@RequestMapping` annotations (and the various aliases) specify the same
|
||||
`path`/`value` property and then moves that path to the class level.
|
||||
- A Spring-specific check which flags `@Value("some.property")` annotations, as
|
||||
- A Spring-specific check that looks for classes in which all `@RequestMapping`
|
||||
annotations (and the various aliases) specify the same `path`/`value`
|
||||
property and then moves that path to the class level.
|
||||
- A Spring-specific check that flags `@Value("some.property")` annotations, as
|
||||
these almost certainly should be `@Value("${some.property}")`.
|
||||
- A Spring-specific check which drops the `required` attribute from
|
||||
- A Spring-specific check that drops the `required` attribute from
|
||||
`@RequestParam` annotations when the `defaultValue` attribute is also
|
||||
specified.
|
||||
- A Spring-specific check which rewrites a class which uses field injection to
|
||||
- A Spring-specific check that rewrites a class which uses field injection to
|
||||
one which uses constructor injection. This check wouldn't be strictly
|
||||
behavior preserving, but could be used for a one-off code base migration.
|
||||
- A Spring-specific check which disallows field injection, except in
|
||||
- A Spring-specific check that disallows field injection, except in
|
||||
`AbstractTestNGSpringContextTests` subclasses. (One known edge case:
|
||||
self-injections so that a bean can call itself through an implicit proxy.)
|
||||
- A Spring-specific check which verifies that public methods on all classes
|
||||
- A Spring-specific check that verifies that public methods on all classes
|
||||
whose name matches a certain pattern, e.g. `.*Service`, are annotated
|
||||
`@Secured`.
|
||||
- A Spring-specific check which verifies that annotations such as
|
||||
- A Spring-specific check that verifies that annotations such as
|
||||
`@RequestParam` are only present in `@RestController` classes.
|
||||
- A Spring-specific check which disallows `@ResponseStatus` on MVC endpoint
|
||||
- A Spring-specific check that disallows `@ResponseStatus` on MVC endpoint
|
||||
methods, as this prevents communication of error status codes.
|
||||
- A Hibernate Validator-specific check which looks for `@UnwrapValidatedValue`
|
||||
- A Hibernate Validator-specific check that looks for `@UnwrapValidatedValue`
|
||||
usages and migrates the associated constraint annotations to the generic type
|
||||
argument to which they (are presumed to) apply.
|
||||
- A TestNG-specific check which drops method-level `@Test` annotations if a
|
||||
- A TestNG-specific check that drops method-level `@Test` annotations if a
|
||||
matching/more specific annotation is already present at the class level.
|
||||
- A TestNG-specific check which enforces that all tests are in a group.
|
||||
- A TestNG-specific check which flags field assignments in
|
||||
- A TestNG-specific check that enforces that all tests are in a group.
|
||||
- A TestNG-specific check that flags field assignments in
|
||||
`@BeforeMethod`-annotated methods unless the class is annotated
|
||||
`@Test(singleThreaded = true)`.
|
||||
- A TestNG-specific check which flags usages of the `expectedExceptions`
|
||||
- A TestNG-specific check that flags usages of the `expectedExceptions`
|
||||
attribute of the `@Test` annotation, pointing to `assertThrows`.
|
||||
- A Jongo-specific check which disallows the creation of sparse indices, in
|
||||
- A Jongo-specific check that disallows the creation of sparse indices, in
|
||||
favour of partial indices.
|
||||
- An Immutables-specific check which replaces
|
||||
- An Immutables-specific check that replaces
|
||||
`checkState`/`IllegalStateException` usages inside a `@Value.Check`-annotated
|
||||
method with `checkArgument`/`IllegalArgument`, since the method is invoked
|
||||
when a caller attempts to create an immutable instance.
|
||||
- An Immutables-specific check which disallows references to collection types
|
||||
- An Immutables-specific check that disallows references to collection types
|
||||
other than the Guava immutable collections, including inside generic type
|
||||
arguments.
|
||||
- An SLF4J-specific check which drops or adds a trailing dot from log messages,
|
||||
- An SLF4J-specific check that drops or adds a trailing dot from log messages,
|
||||
as applicable.
|
||||
- A Mockito-specific check which identifies sequences of statements which mock
|
||||
a significant number of methods on a single object with "default data"; such
|
||||
- A Mockito-specific check that identifies sequences of statements which mock a
|
||||
significant number of methods on a single object with "default data"; such
|
||||
constructions can often benefit from a different type of default answer, such
|
||||
as `Answers.RETURNS_MOCKS`.
|
||||
- An RxJava-specific check which flags `.toCompletable()` calls on expressions
|
||||
- An RxJava-specific check that flags `.toCompletable()` calls on expressions
|
||||
of type `Single<Completable>` etc., as most likely
|
||||
`.flatMapCompletable(Functions.identity())` was meant instead. Idem for other
|
||||
variations.
|
||||
- An RxJava-specific check which flags `expr.firstOrError()` calls and suggests
|
||||
- An RxJava-specific check that flags `expr.firstOrError()` calls and suggests
|
||||
`expr.switchIfEmpty(Single.error(...))`, so that an application-specific
|
||||
exception is thrown instead of `NoSuchElementException`.
|
||||
- An RxJava-specific check which flags use of `#assertValueSet` without
|
||||
- An RxJava-specific check that flags use of `#assertValueSet` without
|
||||
`#assertValueCount`, as the former method doesn't do what one may intuitively
|
||||
expect it to do. See ReactiveX/RxJava#6151.
|
||||
|
||||
@@ -247,6 +198,7 @@ but on the flip side Refaster is much less expressive. While this gap can never
|
||||
be fully closed, there are some ways in which Refaster's scope of utility could
|
||||
be extended. The following is a non-exhaustive list of ideas on how to extend
|
||||
Refaster's expressiveness:
|
||||
|
||||
- Allow more control over _which_ methods are statically imported by
|
||||
`@UseImportPolicy`. Sometimes the `@AfterTemplate` contains more than one
|
||||
static method invocation, and only a subset should be statically imported.
|
||||
@@ -261,9 +213,9 @@ Refaster's expressiveness:
|
||||
pure expression. Introduce a way to express such a constraint. For example,
|
||||
rewriting `optional1.map(Optional::of).orElse(optional2)` to `optional1.or(()
|
||||
-> optional2)` is not behavior preserving if evaluation of `optional2` has
|
||||
side-effects.
|
||||
side effects.
|
||||
- Similarly, certain refactoring operations are only valid if one of the
|
||||
matches expressions is not `@Nullable`. It'd be nice to be able to express
|
||||
matched expressions is not `@Nullable`. It'd be nice to be able to express
|
||||
this.
|
||||
- Generalize `@Placeholder` support such that rules can reference e.g. "any
|
||||
concrete unary method". This would allow refactorings such as
|
||||
@@ -284,40 +236,36 @@ Refaster's expressiveness:
|
||||
- Provide a way to express transformations of compile-time constants. This
|
||||
would allow one to e.g. rewrite single-character strings to chars or vice
|
||||
versa, thereby accommodating a target API. Another example would be to
|
||||
replace SLF4J's `{}` place holders with `%s` or vice versa. Yet another
|
||||
replace SLF4J's `{}` placeholders with `%s` or vice versa. Yet another
|
||||
example would be to rewrite `BigDecimal.valueOf("<some-long-value>")` to
|
||||
`BigDecimal.valueOf(theParsedLongValue)`.
|
||||
- More generally, investigate ways to plug in in fully dynamic behavior, e.g.
|
||||
by providing hooks using which arbitrary predicates/transformations can be
|
||||
plugged in. The result would be a Refaster/`BugChecker` hybrid. A feature
|
||||
such as this could form the basis for many other features listed here. (As a
|
||||
concrete example, consider the ability to reference
|
||||
- More generally, investigate ways to plug in fully dynamic behavior, e.g. by
|
||||
providing hooks which enable plugging in arbitrary
|
||||
predicates/transformations. The result would be a Refaster/`BugChecker`
|
||||
hybrid. A feature like this could form the basis for many other features
|
||||
listed here. (As a concrete example, consider the ability to reference
|
||||
`com.google.errorprone.matchers.Matcher` implementations.)
|
||||
- Provide a way to match lambda expressions and method references which match a
|
||||
specified functional interface. This would allow rewrites such as
|
||||
`Mono.fromCallable(this::doesNotThrowCheckException)` ->
|
||||
`Mono.fromSupplier(this::doesNotThrowCheckException)`.
|
||||
- Provide an extension API using which methods or expressions can be defined
|
||||
based on functional properties. A motivating example is the Java Collections
|
||||
- Provide an extension API that enables defining methods or expressions based
|
||||
on functional properties. A motivating example is the Java Collections
|
||||
framework, which allows many ways to define (im)mutable (un)ordered
|
||||
collections with(out) duplicates. One could then express things like "match
|
||||
any method call with collects its inputs into an immutable ordered list". An
|
||||
any method call that collects its inputs into an immutable ordered list". An
|
||||
enum analogous to `java.util.stream.Collector.Characteristics` could be used.
|
||||
Out of the box JDK and Guava collection factory methods could be classified,
|
||||
with the user having the option to extend the classification.
|
||||
- Refaster currently unconditionally ignores expressions containing comments.
|
||||
Provide two additional modes: (a) match and drop the comments or (b)
|
||||
transport the comments to before/after the replaced expression.
|
||||
- Extend Refaster to drop imports that come become unnecessary as a result of a
|
||||
refactoring. This e.g. allows one to replace a statically import TestNG
|
||||
- Extend Refaster to drop imports that become unnecessary as a result of a
|
||||
refactoring. This e.g. allows one to replace a statically imported TestNG
|
||||
`fail(...)` invocation with a statically imported equivalent AssertJ
|
||||
`fail(...)` invocation. (Observe that without an impor cleanup this
|
||||
`fail(...)` invocation. (Observe that without an import cleanup this
|
||||
replacement would cause a compilation error.)
|
||||
- Extend the `@Repeated` match semantics such that it also covers non-varargs
|
||||
methods. For a motivating example see google/error-prone#568.
|
||||
- When matching explicit type references, also match super types. For a
|
||||
motivating example, see the two subtly difference loop definitions in
|
||||
`CollectionRemoveAllFromCollectionBlock`.
|
||||
motivating example, see the two subtly different loop definitions in
|
||||
`CollectionRemoveAllFromCollectionExpression`.
|
||||
- Figure out why Refaster sometimes doesn't match the correct generic overload.
|
||||
See the `AssertThatIterableHasOneComparableElementEqualTo` template for an
|
||||
example.
|
||||
@@ -327,16 +275,12 @@ Refaster's expressiveness:
|
||||
[checkstyle-external-project-tests]: https://github.com/checkstyle/checkstyle/blob/master/wercker.yml
|
||||
[codecov]: https://codecov.io
|
||||
[error-prone-bug-patterns]: https://errorprone.info/bugpatterns
|
||||
[error-prone-criteria]: https://errorprone.info/docs/criteria
|
||||
[error-prone-fork-jitpack]: https://jitpack.io/#PicnicSupermarket/error-prone
|
||||
[error-prone-fork-repo]: https://github.com/PicnicSupermarket/error-prone
|
||||
[error-prone]: https://errorprone.info
|
||||
[error-prone-repo]: https://github.com/google/error-prone
|
||||
[forbidden-apis]: https://github.com/policeman-tools/forbidden-apis
|
||||
[fossa]: https://fossa.io
|
||||
[google-java-format]: https://github.com/google/google-java-format
|
||||
[maven]: https://maven.apache.org
|
||||
[main-contributing]: ../CONTRIBUTING.md
|
||||
[main-readme]: ../README.md
|
||||
[modernizer-maven-plugin]: https://github.com/gaul/modernizer-maven-plugin
|
||||
[sonarcloud]: https://sonarcloud.io
|
||||
[pitest]: https://pitest.org
|
||||
[pitest-maven]: https://pitest.org/quickstart/maven
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.1.1-SNAPSHOT</version>
|
||||
<version>0.3.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>error-prone-contrib</artifactId>
|
||||
@@ -69,11 +69,6 @@
|
||||
<artifactId>jsr305</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>javac</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.googlejavaformat</groupId>
|
||||
<artifactId>google-java-format</artifactId>
|
||||
@@ -128,11 +123,6 @@
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
@@ -148,6 +138,11 @@
|
||||
<artifactId>value-annotations</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>
|
||||
@@ -243,4 +238,33 @@
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>docgen</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-test-source</id>
|
||||
<goals>
|
||||
<goal>add-test-source</goal>
|
||||
</goals>
|
||||
<phase>generate-test-sources</phase>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>src/test/resources</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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 com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -22,11 +23,12 @@ import com.sun.tools.javac.code.Symbol;
|
||||
import java.util.Map;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
|
||||
/** A {@link BugChecker} which flags ambiguous {@code @JsonCreator}s in enums. */
|
||||
/** A {@link BugChecker} that flags ambiguous {@code @JsonCreator}s in enums. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "`JsonCreator.Mode` should be set for single-argument creators",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "AmbiguousJsonCreator",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class AmbiguousJsonCreator extends BugChecker implements AnnotationTreeMatcher {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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.allOf;
|
||||
@@ -8,6 +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 com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -21,7 +22,7 @@ import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags AssertJ {@code isEqualTo(null)} checks for simplification.
|
||||
* A {@link BugChecker} that flags AssertJ {@code isEqualTo(null)} checks for simplification.
|
||||
*
|
||||
* <p>This bug checker cannot be replaced with a simple Refaster template, as the Refaster approach
|
||||
* would require that all overloads of {@link org.assertj.core.api.Assert#isEqualTo(Object)} (such
|
||||
@@ -31,7 +32,8 @@ import com.sun.source.tree.MethodInvocationTree;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Prefer `.isNull()` over `.isEqualTo(null)`",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "AssertJIsNull",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class AssertJIsNull extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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.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 com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -24,11 +25,12 @@ import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import java.util.List;
|
||||
|
||||
/** A {@link BugChecker} which flags redundant {@code @Autowired} constructor annotations. */
|
||||
/** A {@link BugChecker} that flags redundant {@code @Autowired} constructor annotations. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Omit `@Autowired` on a class' sole constructor, as it is redundant",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "AutowiredConstructor",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class AutowiredConstructor extends BugChecker implements ClassTreeMatcher {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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 com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -27,11 +28,12 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} which flags annotations that could be written more concisely. */
|
||||
/** A {@link BugChecker} that flags annotations that could be written more concisely. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Omit redundant syntax from annotation declarations",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "CanonicalAnnotationSyntax",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class CanonicalAnnotationSyntax extends BugChecker implements AnnotationTreeMatcher {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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 com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -19,7 +20,7 @@ import com.sun.source.tree.MethodInvocationTree;
|
||||
import java.util.stream.Collector;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags {@link Collector Collectors} that don't clearly express
|
||||
* A {@link BugChecker} that flags {@link Collector Collectors} that don't clearly express
|
||||
* (im)mutability.
|
||||
*
|
||||
* <p>Replacing such collectors with alternatives that produce immutable collections is preferred.
|
||||
@@ -29,7 +30,8 @@ import java.util.stream.Collector;
|
||||
@BugPattern(
|
||||
summary =
|
||||
"Avoid `Collectors.to{List,Map,Set}` in favour of alternatives that emphasize (im)mutability",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "CollectorMutability",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = FRAGILE_CODE)
|
||||
public final class CollectorMutability extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
|
||||
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 com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -22,11 +23,12 @@ import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import java.util.Optional;
|
||||
|
||||
/** A {@link BugChecker} which flags empty methods that seemingly can simply be deleted. */
|
||||
/** A {@link BugChecker} that flags empty methods that seemingly can simply be deleted. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Empty method can likely be deleted",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "EmptyMethod",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class EmptyMethod extends BugChecker implements MethodTreeMatcher {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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.instanceMethod;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.Splitter;
|
||||
@@ -30,7 +31,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags improperly formatted Error Prone test code.
|
||||
* A {@link BugChecker} that flags improperly formatted Error Prone test code.
|
||||
*
|
||||
* <p>All test code should be formatted in accordance with Google Java Format's {@link Formatter}
|
||||
* output, and imports should be ordered according to the {@link Style#GOOGLE Google} style.
|
||||
@@ -50,7 +51,8 @@ import java.util.Optional;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Test code should follow the Google Java style",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "ErrorProneTestHelperSourceFormat",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = STYLE)
|
||||
public final class ErrorProneTestHelperSourceFormat extends BugChecker
|
||||
|
||||
@@ -2,11 +2,12 @@ package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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 java.util.stream.Collectors.collectingAndThen;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -30,13 +31,14 @@ import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags {@link Ordering#explicit(Object, Object[])}} invocations listing
|
||||
* A {@link BugChecker} that flags {@link Ordering#explicit(Object, Object[])}} invocations listing
|
||||
* a subset of an enum type's values.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Make sure `Ordering#explicit` lists all of an enum's values",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "ExplicitEnumOrdering",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = FRAGILE_CODE)
|
||||
public final class ExplicitEnumOrdering extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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 com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -24,7 +25,7 @@ import java.util.function.Supplier;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags usages of {@link Flux#flatMap(Function)} and {@link
|
||||
* A {@link BugChecker} that flags usages of {@link Flux#flatMap(Function)} and {@link
|
||||
* Flux#flatMapSequential(Function)}.
|
||||
*
|
||||
* <p>{@link Flux#flatMap(Function)} and {@link Flux#flatMapSequential(Function)} eagerly perform up
|
||||
@@ -43,7 +44,8 @@ import reactor.core.publisher.Flux;
|
||||
summary =
|
||||
"`Flux#flatMap` and `Flux#flatMapSequential` have subtle semantics; "
|
||||
+ "please use `Flux#concatMap` or explicitly specify the desired amount of concurrency",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "FluxFlatMapUsage",
|
||||
linkType = CUSTOM,
|
||||
severity = ERROR,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class FluxFlatMapUsage extends BugChecker
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
@@ -10,6 +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 com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -35,8 +36,8 @@ import javax.annotation.Nullable;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags string concatenations that produce a format string; in such
|
||||
* cases the string concatenation should instead be deferred to the invoked method.
|
||||
* A {@link BugChecker} that flags string concatenations that produce a format string; in such cases
|
||||
* the string concatenation should instead be deferred to the invoked method.
|
||||
*
|
||||
* @implNote This checker is based on the implementation of {@link
|
||||
* com.google.errorprone.bugpatterns.flogger.FloggerStringConcatenation}.
|
||||
@@ -46,12 +47,13 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
// should introduce special handling of `Formattable` arguments, as this check would replace a
|
||||
// `Formattable#toString` invocation with a `Formattable#formatTo` invocation. But likely that
|
||||
// should be considered a bug fix, too.
|
||||
// XXX: Introduce a separate check which adds/removes the `Locale` parameter to `String.format`
|
||||
// XXX: Introduce a separate check that adds/removes the `Locale` parameter to `String.format`
|
||||
// invocations, as necessary.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Defer string concatenation to the invoked method",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "FormatStringConcatenation",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class FormatStringConcatenation extends BugChecker
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
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.staticMethod;
|
||||
import static com.google.errorprone.suppliers.Suppliers.OBJECT_TYPE;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.primitives.Primitives;
|
||||
@@ -36,7 +37,8 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Avoid or clarify identity conversions",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "IdentityConversion",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class IdentityConversion extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.LIKELY_ERROR;
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
@@ -11,6 +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 com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -26,7 +27,7 @@ import java.util.SortedSet;
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags {@link SortedSet} property declarations inside
|
||||
* A {@link BugChecker} that flags {@link SortedSet} property declarations inside
|
||||
* {@code @Value.Immutable}- and {@code @Value.Modifiable}-annotated types that lack a
|
||||
* {@code @Value.NaturalOrder} or {@code @Value.ReverseOrder} annotation.
|
||||
*
|
||||
@@ -44,7 +45,8 @@ import javax.lang.model.element.Modifier;
|
||||
summary =
|
||||
"`SortedSet` properties of a `@Value.Immutable` or `@Value.Modifiable` type must be "
|
||||
+ "annotated with `@Value.NaturalOrder` or `@Value.ReverseOrder`",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "ImmutablesSortedSetComparator",
|
||||
linkType = CUSTOM,
|
||||
severity = ERROR,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class ImmutablesSortedSetComparator extends BugChecker implements MethodTreeMatcher {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
|
||||
@@ -11,6 +11,7 @@ import static com.google.errorprone.matchers.Matchers.enclosingClass;
|
||||
import static com.google.errorprone.matchers.Matchers.hasModifier;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
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.JavaKeywords.isReservedKeyword;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
@@ -38,8 +39,8 @@ import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.Name;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} which flags non-canonical JUnit method declarations. */
|
||||
// XXX: Consider introducing a class-level check which enforces that test classes:
|
||||
/** A {@link BugChecker} that flags non-canonical JUnit method declarations. */
|
||||
// XXX: Consider introducing a class-level check that enforces that test classes:
|
||||
// 1. Are named `*Test` or `Abstract*TestCase`.
|
||||
// 2. If not `abstract`, are package-private and don't have public methods and subclasses.
|
||||
// 3. Only have private fields.
|
||||
@@ -47,7 +48,8 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "JUnit method declaration can likely be improved",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "JUnitMethodDeclaration",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class JUnitMethodDeclaration extends BugChecker implements MethodTreeMatcher {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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 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 com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.Comparators;
|
||||
@@ -42,7 +43,7 @@ import tech.picnic.errorprone.bugpatterns.util.AnnotationAttributeMatcher;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags annotation array listings which aren't sorted lexicographically.
|
||||
* A {@link BugChecker} that flags annotation array listings which aren't sorted lexicographically.
|
||||
*
|
||||
* <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.
|
||||
@@ -50,7 +51,8 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Where possible, sort annotation array attributes lexicographically",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "LexicographicalAnnotationAttributeListing",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = STYLE)
|
||||
public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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 java.util.Comparator.comparing;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -31,7 +32,8 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Sort annotations lexicographically where possible",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "LexicographicalAnnotationListing",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = STYLE)
|
||||
public final class LexicographicalAnnotationListing extends BugChecker
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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 com.google.auto.service.AutoService;
|
||||
import com.google.common.base.VerifyException;
|
||||
@@ -35,7 +36,7 @@ import java.util.Optional;
|
||||
import javax.lang.model.element.Name;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags lambda expressions that can be replaced with method references.
|
||||
* A {@link BugChecker} that flags lambda expressions that can be replaced with method references.
|
||||
*/
|
||||
// XXX: Other custom expressions we could rewrite:
|
||||
// - `a -> "str" + a` to `"str"::concat`. But only if `str` is provably non-null.
|
||||
@@ -52,7 +53,8 @@ import javax.lang.model.element.Name;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Prefer method references over lambda expressions",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "MethodReferenceUsage",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = STYLE)
|
||||
public final class MethodReferenceUsage extends BugChecker implements LambdaExpressionTreeMatcher {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -25,7 +26,8 @@ import com.sun.source.tree.Tree;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "The Refaster template contains a method without any Refaster annotations",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "MissingRefasterAnnotation",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class MissingRefasterAnnotation extends BugChecker implements ClassTreeMatcher {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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 com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -20,13 +21,14 @@ import java.util.List;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags method invocations for which all arguments are wrapped using
|
||||
* A {@link BugChecker} that flags method invocations for which all arguments are wrapped using
|
||||
* {@link org.mockito.Mockito#eq}; this is redundant.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Don't unnecessarily use Mockito's `eq(...)`",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "MockitoStubbing",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class MockitoStubbing extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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 com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -20,12 +21,13 @@ import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/** A {@link BugChecker} which flags nesting of {@link Optional Optionals}. */
|
||||
/** A {@link BugChecker} that flags nesting of {@link Optional Optionals}. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"Avoid nesting `Optional`s inside `Optional`s; the resultant code is hard to reason about",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "NestedOptionals",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = FRAGILE_CODE)
|
||||
public final class NestedOptionals extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
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.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 com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
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.MethodInvocationTree;
|
||||
import java.util.function.BiFunction;
|
||||
import reactor.core.publisher.Mono;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags {@link Mono} operations that are known to be vacuous, given that
|
||||
* they are invoked on a {@link Mono} that is known not to complete empty.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Avoid vacuous operations on known non-empty `Mono`s",
|
||||
link = BUG_PATTERNS_BASE_URL + "NonEmptyMono",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = SIMPLIFICATION)
|
||||
// XXX: This check does not simplify `someFlux.defaultIfEmpty(T).{defaultIfEmpty(T),hasElements()}`,
|
||||
// as `someFlux.defaultIfEmpty(T)` yields a `Flux` rather than a `Mono`. Consider adding support for
|
||||
// these cases.
|
||||
// XXX: Given more advanced analysis many more expressions could be flagged. Consider
|
||||
// `Mono.just(someValue)`, `Flux.just(someNonEmptySequence)`,
|
||||
// `someMono.switchIfEmpty(someProvablyNonEmptyMono)` and many other variants.
|
||||
// XXX: Consider implementing a similar check for `Publisher`s that are known to complete without
|
||||
// emitting a value (e.g. `Mono.empty()`, `someFlux.then()`, ...), or known not to complete normally
|
||||
// (`Mono.never()`, `someFlux.repeat()`, `Mono.error(...)`, ...). The latter category could
|
||||
// potentially be split out further.
|
||||
public final class NonEmptyMono extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> MONO_SIZE_CHECK =
|
||||
instanceMethod()
|
||||
.onDescendantOf("reactor.core.publisher.Mono")
|
||||
.namedAnyOf("defaultIfEmpty", "single", "switchIfEmpty");
|
||||
private static final Matcher<ExpressionTree> NON_EMPTY_MONO =
|
||||
anyOf(
|
||||
instanceMethod()
|
||||
.onDescendantOf("reactor.core.publisher.Flux")
|
||||
.namedAnyOf(
|
||||
"all",
|
||||
"any",
|
||||
"collect",
|
||||
"collectList",
|
||||
"collectMap",
|
||||
"collectMultimap",
|
||||
"collectSortedList",
|
||||
"count",
|
||||
"elementAt",
|
||||
"hasElement",
|
||||
"hasElements",
|
||||
"last",
|
||||
"reduceWith",
|
||||
"single"),
|
||||
instanceMethod()
|
||||
.onDescendantOf("reactor.core.publisher.Flux")
|
||||
.named("reduce")
|
||||
.withParameters(Object.class.getName(), BiFunction.class.getName()),
|
||||
instanceMethod()
|
||||
.onDescendantOf("reactor.core.publisher.Mono")
|
||||
.namedAnyOf("defaultIfEmpty", "hasElement", "single"));
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
if (!MONO_SIZE_CHECK.matches(tree, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
|
||||
if (!NON_EMPTY_MONO.matches(receiver, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
return describeMatch(
|
||||
tree, SuggestedFix.replace(tree, SourceCode.treeToString(receiver, state)));
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.PERFORMANCE;
|
||||
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 com.google.auto.service.AutoService;
|
||||
import com.google.common.base.VerifyException;
|
||||
@@ -34,7 +35,7 @@ import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags {@code Comparator#comparing*} invocations that can be replaced
|
||||
* A {@link BugChecker} that flags {@code Comparator#comparing*} invocations that can be replaced
|
||||
* with an equivalent alternative so as to avoid unnecessary (un)boxing.
|
||||
*/
|
||||
// XXX: Add more documentation. Explain how this is useful in the face of refactoring to more
|
||||
@@ -44,7 +45,8 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
summary =
|
||||
"Ensure invocations of `Comparator#comparing{,Double,Int,Long}` match the return type"
|
||||
+ " of the provided function",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "PrimitiveComparison",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = PERFORMANCE)
|
||||
public final class PrimitiveComparison extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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.SIMPLIFICATION;
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
import static com.google.errorprone.matchers.Matchers.anyMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.argumentCount;
|
||||
import static com.google.errorprone.matchers.Matchers.isNonNullUsingDataflow;
|
||||
import static com.google.errorprone.matchers.Matchers.isSameType;
|
||||
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 com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.ErrorProneFlags;
|
||||
import com.google.errorprone.VisitorState;
|
||||
@@ -24,6 +30,7 @@ import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.suppliers.Suppliers;
|
||||
import com.sun.source.tree.BinaryTree;
|
||||
import com.sun.source.tree.CompoundAssignmentTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
@@ -41,14 +48,16 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.bugpatterns.util.MethodMatcherFactory;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} which flags redundant explicit string conversions. */
|
||||
/** A {@link BugChecker} that flags redundant explicit string conversions. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Avoid redundant string conversions when possible",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "RedundantStringConversion",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class RedundantStringConversion extends BugChecker
|
||||
@@ -69,49 +78,30 @@ public final class RedundantStringConversion extends BugChecker
|
||||
allOf(STRING, isNonNullUsingDataflow());
|
||||
private static final Matcher<ExpressionTree> NOT_FORMATTABLE =
|
||||
not(isSubtypeOf(Formattable.class));
|
||||
private static final Matcher<ExpressionTree> WELL_KNOWN_STRING_CONVERSION_METHODS =
|
||||
private static final Matcher<MethodInvocationTree> WELL_KNOWN_STRING_CONVERSION_METHODS =
|
||||
anyOf(
|
||||
instanceMethod().onDescendantOfAny(Object.class.getName()).named("toString"),
|
||||
staticMethod()
|
||||
.onClass(Objects.class.getName())
|
||||
instanceMethod()
|
||||
.onDescendantOfAny(Object.class.getName())
|
||||
.named("toString")
|
||||
.withParameters(Object.class.getName()),
|
||||
staticMethod()
|
||||
.onClass(String.class.getName())
|
||||
.named("valueOf")
|
||||
.withParameters(Object.class.getName()),
|
||||
staticMethod()
|
||||
.onClass(String.class.getName())
|
||||
.named("valueOf")
|
||||
.withParameters(String.class.getName()),
|
||||
staticMethod()
|
||||
.onClass(Byte.class.getName())
|
||||
.named("toString")
|
||||
.withParameters(byte.class.getName()),
|
||||
staticMethod()
|
||||
.onClass(Character.class.getName())
|
||||
.named("toString")
|
||||
.withParameters(char.class.getName()),
|
||||
staticMethod()
|
||||
.onClass(Short.class.getName())
|
||||
.named("toString")
|
||||
.withParameters(short.class.getName()),
|
||||
staticMethod()
|
||||
.onClass(Integer.class.getName())
|
||||
.named("toString")
|
||||
.withParameters(int.class.getName()),
|
||||
staticMethod()
|
||||
.onClass(Long.class.getName())
|
||||
.named("toString")
|
||||
.withParameters(long.class.getName()),
|
||||
staticMethod()
|
||||
.onClass(Float.class.getName())
|
||||
.named("toString")
|
||||
.withParameters(float.class.getName()),
|
||||
staticMethod()
|
||||
.onClass(Double.class.getName())
|
||||
.named("toString")
|
||||
.withParameters(double.class.getName()));
|
||||
.withNoParameters(),
|
||||
allOf(
|
||||
argumentCount(1),
|
||||
anyOf(
|
||||
staticMethod()
|
||||
.onClassAny(
|
||||
Stream.concat(
|
||||
Primitives.allWrapperTypes().stream(), Stream.of(Objects.class))
|
||||
.map(Class::getName)
|
||||
.collect(toImmutableSet()))
|
||||
.named("toString"),
|
||||
allOf(
|
||||
staticMethod().onClass(String.class.getName()).named("valueOf"),
|
||||
not(
|
||||
anyMethod()
|
||||
.anyClass()
|
||||
.withAnyName()
|
||||
.withParametersOfType(
|
||||
ImmutableList.of(Suppliers.arrayOf(Suppliers.CHAR_TYPE))))))));
|
||||
private static final Matcher<ExpressionTree> STRINGBUILDER_APPEND_INVOCATION =
|
||||
instanceMethod()
|
||||
.onDescendantOf(StringBuilder.class.getName())
|
||||
@@ -127,17 +117,10 @@ public final class RedundantStringConversion extends BugChecker
|
||||
staticMethod().onClass(String.class.getName()).named("format"),
|
||||
instanceMethod().onDescendantOf(Formatter.class.getName()).named("format"),
|
||||
instanceMethod()
|
||||
.onDescendantOf(PrintStream.class.getName())
|
||||
.onDescendantOfAny(PrintStream.class.getName(), PrintWriter.class.getName())
|
||||
.namedAnyOf("format", "printf"),
|
||||
instanceMethod()
|
||||
.onDescendantOf(PrintStream.class.getName())
|
||||
.namedAnyOf("print", "println")
|
||||
.withParameters(Object.class.getName()),
|
||||
instanceMethod()
|
||||
.onDescendantOf(PrintWriter.class.getName())
|
||||
.namedAnyOf("format", "printf"),
|
||||
instanceMethod()
|
||||
.onDescendantOf(PrintWriter.class.getName())
|
||||
.onDescendantOfAny(PrintStream.class.getName(), PrintWriter.class.getName())
|
||||
.namedAnyOf("print", "println")
|
||||
.withParameters(Object.class.getName()),
|
||||
staticMethod()
|
||||
@@ -156,7 +139,7 @@ public final class RedundantStringConversion extends BugChecker
|
||||
.onDescendantOf("org.slf4j.Logger")
|
||||
.namedAnyOf("trace", "debug", "info", "warn", "error");
|
||||
|
||||
private final Matcher<ExpressionTree> conversionMethodMatcher;
|
||||
private final Matcher<MethodInvocationTree> conversionMethodMatcher;
|
||||
|
||||
/** Instantiates the default {@link RedundantStringConversion}. */
|
||||
public RedundantStringConversion() {
|
||||
@@ -186,7 +169,7 @@ public final class RedundantStringConversion extends BugChecker
|
||||
|
||||
List<SuggestedFix.Builder> fixes = new ArrayList<>();
|
||||
|
||||
// XXX: Not so nice: we try to simplify the RHS twice.
|
||||
// XXX: Avoid trying to simplify the RHS twice.
|
||||
ExpressionTree preferredRhs = trySimplify(rhs, state).orElse(rhs);
|
||||
if (STRING.matches(preferredRhs, state)) {
|
||||
tryFix(lhs, state, ANY_EXPR).ifPresent(fixes::add);
|
||||
@@ -243,7 +226,7 @@ public final class RedundantStringConversion extends BugChecker
|
||||
.flatMap(args -> tryFix(args.get(index), state, ANY_EXPR));
|
||||
}
|
||||
|
||||
// XXX: Write another check which checks that Formatter patterns don't use `{}` and have a
|
||||
// XXX: Write another check that checks that Formatter patterns don't use `{}` and have a
|
||||
// matching number of arguments of the appropriate type. Also flag explicit conversions from
|
||||
// `Formattable` to string.
|
||||
private Optional<SuggestedFix.Builder> tryFixFormatter(
|
||||
@@ -274,17 +257,17 @@ public final class RedundantStringConversion extends BugChecker
|
||||
return tryFixFormatterArguments(arguments, state, ANY_EXPR, ANY_EXPR);
|
||||
}
|
||||
|
||||
// XXX: Write another check which checks that SLF4J patterns don't use `%s` and have a matching
|
||||
// XXX: Write another check that checks that SLF4J patterns don't use `%s` and have a matching
|
||||
// number of arguments of the appropriate type. Also flag explicit conversions from `Throwable` to
|
||||
// string as the last logger argument. Suggests either dropping the converison or going with
|
||||
// string as the last logger argument. Suggests either dropping the conversion or going with
|
||||
// `Throwable#getMessage()` instead.
|
||||
private Optional<SuggestedFix.Builder> tryFixSlf4jLogger(
|
||||
List<? extends ExpressionTree> arguments, VisitorState state) {
|
||||
/*
|
||||
* SLF4J treats the final argument to a log statement specially if it is a `Throwabe`: it
|
||||
* SLF4J treats the final argument to a log statement specially if it is a `Throwable`: it
|
||||
* will always choose to render the associated stacktrace, even if the argument has a
|
||||
* matching `{}` placeholder. (In this case the `{}` will simply be logged verbatim.) So if
|
||||
* a log statement's final argument is the string representation of a `Throwble`, then we
|
||||
* a log statement's final argument is the string representation of a `Throwable`, then we
|
||||
* must not strip this explicit string conversion, as that would change the statement's
|
||||
* semantics.
|
||||
*/
|
||||
@@ -338,11 +321,15 @@ public final class RedundantStringConversion extends BugChecker
|
||||
}
|
||||
|
||||
private Optional<ExpressionTree> trySimplify(ExpressionTree tree, VisitorState state) {
|
||||
if (tree.getKind() != Kind.METHOD_INVOCATION || !conversionMethodMatcher.matches(tree, state)) {
|
||||
if (tree.getKind() != Kind.METHOD_INVOCATION) {
|
||||
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);
|
||||
@@ -383,7 +370,8 @@ public final class RedundantStringConversion extends BugChecker
|
||||
.orElse(Description.NO_MATCH);
|
||||
}
|
||||
|
||||
private static Matcher<ExpressionTree> createConversionMethodMatcher(ErrorProneFlags flags) {
|
||||
private static Matcher<MethodInvocationTree> createConversionMethodMatcher(
|
||||
ErrorProneFlags flags) {
|
||||
// XXX: ErrorProneFlags#getList splits by comma, but method signatures may also contain commas.
|
||||
// For this class methods accepting more than one argument are not valid, but still: not nice.
|
||||
return flags
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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 com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -19,7 +20,7 @@ import com.sun.source.tree.MethodInvocationTree;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags unnecessary {@link Refaster#anyOf(Object[])} usages.
|
||||
* A {@link BugChecker} that flags unnecessary {@link Refaster#anyOf(Object[])} usages.
|
||||
*
|
||||
* <p>Note that this logic can't be implemented as a Refaster template, as the {@link Refaster}
|
||||
* class is treated specially.
|
||||
@@ -27,7 +28,8 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "`Refaster#anyOf` should be passed at least two parameters",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "RefasterAnyOfUsage",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class RefasterAnyOfUsage extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
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.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 com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.Placeholder;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which suggests a canonical set of modifiers for Refaster class and method
|
||||
* definitions.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Refaster class and method definitions should specify a canonical set of modifiers",
|
||||
link = BUG_PATTERNS_BASE_URL + "RefasterTemplateModifiers",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = STYLE)
|
||||
public final class RefasterTemplateModifiers extends BugChecker
|
||||
implements ClassTreeMatcher, MethodTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<Tree> BEFORE_TEMPLATE_METHOD = hasAnnotation(BeforeTemplate.class);
|
||||
private static final Matcher<Tree> AFTER_TEMPLATE_METHOD = hasAnnotation(AfterTemplate.class);
|
||||
private static final Matcher<Tree> PLACEHOLDER_METHOD = hasAnnotation(Placeholder.class);
|
||||
private static final Matcher<Tree> REFASTER_METHOD =
|
||||
anyOf(BEFORE_TEMPLATE_METHOD, AFTER_TEMPLATE_METHOD, PLACEHOLDER_METHOD);
|
||||
|
||||
@Override
|
||||
public Description matchClass(ClassTree tree, VisitorState state) {
|
||||
if (!hasMatchingMember(tree, BEFORE_TEMPLATE_METHOD, state)) {
|
||||
/* This class does not contain a Refaster template. */
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
SuggestedFix fix = suggestCanonicalModifiers(tree, state);
|
||||
return fix.isEmpty() ? Description.NO_MATCH : describeMatch(tree, fix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description matchMethod(MethodTree tree, VisitorState state) {
|
||||
if (!REFASTER_METHOD.matches(tree, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
return SuggestedFixes.removeModifiers(
|
||||
tree,
|
||||
state,
|
||||
Modifier.FINAL,
|
||||
Modifier.PRIVATE,
|
||||
Modifier.PROTECTED,
|
||||
Modifier.PUBLIC,
|
||||
Modifier.STATIC,
|
||||
Modifier.SYNCHRONIZED)
|
||||
.map(fix -> describeMatch(tree, fix))
|
||||
.orElse(Description.NO_MATCH);
|
||||
}
|
||||
|
||||
private static SuggestedFix suggestCanonicalModifiers(ClassTree tree, VisitorState state) {
|
||||
Set<Modifier> modifiersToAdd = EnumSet.noneOf(Modifier.class);
|
||||
Set<Modifier> modifiersToRemove =
|
||||
EnumSet.of(Modifier.PRIVATE, Modifier.PROTECTED, Modifier.PUBLIC, Modifier.SYNCHRONIZED);
|
||||
|
||||
if (!hasMatchingMember(tree, PLACEHOLDER_METHOD, state)) {
|
||||
/*
|
||||
* Templates without a `@Placeholder` method should be `final`. Note that Refaster enforces
|
||||
* that `@Placeholder` methods are `abstract`, so templates _with_ such a method will
|
||||
* naturally be `abstract` and non-`final`.
|
||||
*/
|
||||
modifiersToAdd.add(Modifier.FINAL);
|
||||
modifiersToRemove.add(Modifier.ABSTRACT);
|
||||
}
|
||||
|
||||
if (ASTHelpers.findEnclosingNode(state.getPath(), ClassTree.class) != null) {
|
||||
/* Nested classes should be `static`. */
|
||||
modifiersToAdd.add(Modifier.STATIC);
|
||||
}
|
||||
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder();
|
||||
SuggestedFixes.addModifiers(tree, tree.getModifiers(), state, modifiersToAdd)
|
||||
.ifPresent(fix::merge);
|
||||
SuggestedFixes.removeModifiers(tree.getModifiers(), state, modifiersToRemove)
|
||||
.ifPresent(fix::merge);
|
||||
return fix.build();
|
||||
}
|
||||
|
||||
private static boolean hasMatchingMember(
|
||||
ClassTree tree, Matcher<Tree> matcher, VisitorState state) {
|
||||
return tree.getMembers().stream().anyMatch(member -> matcher.matches(member, state));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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.ALL;
|
||||
@@ -11,6 +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 com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -23,7 +24,7 @@ import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags {@code @RequestMapping} methods that have one or more parameters
|
||||
* A {@link BugChecker} that flags {@code @RequestMapping} methods that have one or more parameters
|
||||
* that appear to lack a relevant annotation.
|
||||
*
|
||||
* <p>Matched mappings are {@code @{Delete,Get,Patch,Post,Put,Request}Mapping}.
|
||||
@@ -31,7 +32,8 @@ import com.sun.source.tree.Tree;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Make sure all `@RequestMapping` method parameters are annotated",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "RequestMappingAnnotation",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class RequestMappingAnnotation extends BugChecker implements MethodTreeMatcher {
|
||||
@@ -66,7 +68,8 @@ public final class RequestMappingAnnotation extends BugChecker implements Method
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestAttribute"),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestBody"),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestHeader"),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestParam"))),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestParam"),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestPart"))),
|
||||
isSameType("java.io.InputStream"),
|
||||
isSameType("java.time.ZoneId"),
|
||||
isSameType("java.util.Locale"),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
|
||||
@@ -9,6 +9,7 @@ import static com.google.errorprone.matchers.Matchers.annotations;
|
||||
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 tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
@@ -21,11 +22,12 @@ import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.VariableTree;
|
||||
|
||||
/** A {@link BugChecker} which flags {@code @RequestParam} parameters with an unsupported type. */
|
||||
/** A {@link BugChecker} that flags {@code @RequestParam} parameters with an unsupported type. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "`@RequestParam` does not support `ImmutableCollection` and `ImmutableMap` subtypes",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "RequestParamType",
|
||||
linkType = CUSTOM,
|
||||
severity = ERROR,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class RequestParamType extends BugChecker implements VariableTreeMatcher {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
|
||||
import static com.google.errorprone.matchers.Matchers.annotations;
|
||||
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.common.AnnotationMirrors;
|
||||
import com.google.auto.service.AutoService;
|
||||
@@ -27,13 +28,14 @@ import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags methods with Spring's {@code @Scheduled} annotation that lack
|
||||
* New Relic Agent's {@code @Trace(dispatcher = true)}.
|
||||
* A {@link BugChecker} that flags methods with Spring's {@code @Scheduled} annotation that lack New
|
||||
* Relic Agent's {@code @Trace(dispatcher = true)}.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Scheduled operation must start a new New Relic transaction",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "ScheduledTransactionTrace",
|
||||
linkType = CUSTOM,
|
||||
severity = ERROR,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class ScheduledTransactionTrace extends BugChecker implements MethodTreeMatcher {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.common.base.Verify.verify;
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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.isSubtypeOf;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.Splitter;
|
||||
@@ -24,7 +25,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} which flags SLF4J usages that are likely to be in error. */
|
||||
/** 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
|
||||
// https://www.slf4j.org/faq.html#paramException. That should be documented.
|
||||
// XXX: Also simplify `LOG.error(String.format("Something %s", arg), throwable)`.
|
||||
@@ -33,7 +34,8 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Make sure SLF4J log statements contain proper placeholders with matching arguments",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "Slf4jLogStatement",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class Slf4jLogStatement extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.common.base.Verify.verify;
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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 com.google.auto.service.AutoService;
|
||||
import com.google.common.base.VerifyException;
|
||||
@@ -29,14 +30,15 @@ import tech.picnic.errorprone.bugpatterns.util.AnnotationAttributeMatcher;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags {@code @RequestMapping} annotations that can be written more
|
||||
* A {@link BugChecker} that flags {@code @RequestMapping} annotations that can be written more
|
||||
* concisely.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"Prefer the conciseness of `@{Get,Put,Post,Delete,Patch}Mapping` over `@RequestMapping`",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "SpringMvcAnnotation",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class SpringMvcAnnotation extends BugChecker implements AnnotationTreeMatcher {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
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.Objects.requireNonNull;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@@ -27,15 +28,14 @@ import com.sun.tools.javac.code.Type;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags methods and constants that can and should be statically
|
||||
* imported.
|
||||
* A {@link BugChecker} that flags methods and constants that can and should be statically imported.
|
||||
*/
|
||||
// XXX: Tricky cases:
|
||||
// - `org.springframework.http.HttpStatus` (not always an improvement, and `valueOf` must
|
||||
// certainly be excluded)
|
||||
// - `com.google.common.collect.Tables`
|
||||
// - `ch.qos.logback.classic.Level.{DEBUG, ERROR, INFO, TRACE, WARN"}`
|
||||
// XXX: Also introduce a check which disallows static imports of certain methods. Candidates:
|
||||
// XXX: Also introduce a check that disallows static imports of certain methods. Candidates:
|
||||
// - `com.google.common.base.Strings`
|
||||
// - `java.util.Optional.empty`
|
||||
// - `java.util.Locale.ROOT`
|
||||
@@ -46,7 +46,8 @@ import java.util.Optional;
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Identifier should be statically imported",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "StaticImport",
|
||||
linkType = CUSTOM,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class StaticImport extends BugChecker implements MemberSelectTreeMatcher {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.FRAGILE_CODE;
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
@@ -10,6 +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 com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -26,12 +27,13 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
|
||||
/** A {@link BugChecker} which flags illegal time-zone related operations. */
|
||||
/** A {@link BugChecker} that flags illegal time-zone related operations. */
|
||||
@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",
|
||||
linkType = NONE,
|
||||
link = BUG_PATTERNS_BASE_URL + "TimeZoneUsage",
|
||||
linkType = CUSTOM,
|
||||
severity = WARNING,
|
||||
tags = FRAGILE_CODE)
|
||||
public final class TimeZoneUsage extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package tech.picnic.errorprone.bugpatterns.util;
|
||||
|
||||
/** Utility class providing documentation-related code. */
|
||||
public final class Documentation {
|
||||
/** The base URL at which Error Prone Support bug patterns are hosted. */
|
||||
public static final String BUG_PATTERNS_BASE_URL = "https://error-prone.picnic.tech/bugpatterns/";
|
||||
|
||||
private Documentation() {}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import java.math.BigInteger;
|
||||
import org.assertj.core.api.AbstractBigIntegerAssert;
|
||||
|
||||
// XXX: If we add a rule which drops unnecessary `L` suffixes from literal longs, then the `0L`/`1L`
|
||||
// XXX: If we add a rule that drops unnecessary `L` suffixes from literal longs, then the `0L`/`1L`
|
||||
// cases below can go.
|
||||
final class AssertJBigIntegerTemplates {
|
||||
private AssertJBigIntegerTemplates() {}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package tech.picnic.errorprone.refastertemplates;
|
||||
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import org.assertj.core.api.AbstractComparableAssert;
|
||||
import org.assertj.core.api.AbstractIntegerAssert;
|
||||
|
||||
final class AssertJComparableTemplates {
|
||||
private AssertJComparableTemplates() {}
|
||||
|
||||
static final class AssertThatIsEqualByComparingTo<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
AbstractIntegerAssert<?> before(T actual, T expected) {
|
||||
return assertThat(actual.compareTo(expected)).isEqualTo(0);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractComparableAssert<?, ?> after(T actual, T expected) {
|
||||
return assertThat(actual).isEqualByComparingTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatIsNotEqualByComparingTo<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
AbstractIntegerAssert<?> before(T actual, T expected) {
|
||||
return assertThat(actual.compareTo(expected)).isNotEqualTo(0);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractComparableAssert<?, ?> after(T actual, T expected) {
|
||||
return assertThat(actual).isNotEqualByComparingTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatIsLessThan<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
AbstractIntegerAssert<?> before(T actual, T expected) {
|
||||
return assertThat(actual.compareTo(expected)).isNegative();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractComparableAssert<?, ?> after(T actual, T expected) {
|
||||
return assertThat(actual).isLessThan(expected);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatIsLessThanOrEqualTo<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
AbstractIntegerAssert<?> before(T actual, T expected) {
|
||||
return assertThat(actual.compareTo(expected)).isNotPositive();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractComparableAssert<?, ?> after(T actual, T expected) {
|
||||
return assertThat(actual).isLessThanOrEqualTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatIsGreaterThan<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
AbstractIntegerAssert<?> before(T actual, T expected) {
|
||||
return assertThat(actual.compareTo(expected)).isPositive();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractComparableAssert<?, ?> after(T actual, T expected) {
|
||||
return assertThat(actual).isGreaterThan(expected);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatIsGreaterThanOrEqualTo<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
AbstractIntegerAssert<?> before(T actual, T expected) {
|
||||
return assertThat(actual.compareTo(expected)).isNotNegative();
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractComparableAssert<?, ?> after(T actual, T expected) {
|
||||
return assertThat(actual).isGreaterThanOrEqualTo(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.NotMatches;
|
||||
import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
@@ -18,6 +19,7 @@ import org.assertj.core.api.AbstractIntegerAssert;
|
||||
import org.assertj.core.api.AbstractLongAssert;
|
||||
import org.assertj.core.api.AbstractShortAssert;
|
||||
import org.assertj.core.api.NumberAssert;
|
||||
import tech.picnic.errorprone.refaster.matchers.IsCharacter;
|
||||
|
||||
final class AssertJNumberTemplates {
|
||||
private AssertJNumberTemplates() {}
|
||||
@@ -226,9 +228,16 @@ final class AssertJNumberTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link AbstractLongAssert#isOdd()} (and similar methods for other {@link NumberAssert}
|
||||
* subtypes) over alternatives with less informative error messages.
|
||||
*
|
||||
* <p>Note that {@link org.assertj.core.api.AbstractCharacterAssert} does not implement {@link
|
||||
* NumberAssert} and does not provide an {@code isOdd} test.
|
||||
*/
|
||||
static final class AssertThatIsOdd {
|
||||
@BeforeTemplate
|
||||
AbstractIntegerAssert<?> before(int number) {
|
||||
AbstractIntegerAssert<?> before(@NotMatches(IsCharacter.class) int number) {
|
||||
return assertThat(number % 2).isEqualTo(1);
|
||||
}
|
||||
|
||||
@@ -244,9 +253,16 @@ final class AssertJNumberTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link AbstractLongAssert#isEven()} (and similar methods for other {@link NumberAssert}
|
||||
* subtypes) over alternatives with less informative error messages.
|
||||
*
|
||||
* <p>Note that {@link org.assertj.core.api.AbstractCharacterAssert} does not implement {@link
|
||||
* NumberAssert} and does not provide an {@code isEven} test.
|
||||
*/
|
||||
static final class AssertThatIsEven {
|
||||
@BeforeTemplate
|
||||
AbstractIntegerAssert<?> before(int number) {
|
||||
AbstractIntegerAssert<?> before(@NotMatches(IsCharacter.class) int number) {
|
||||
return assertThat(number % 2).isEqualTo(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
package tech.picnic.errorprone.refastertemplates;
|
||||
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import org.assertj.core.api.AbstractBooleanAssert;
|
||||
import org.assertj.core.api.AbstractDoubleAssert;
|
||||
|
||||
final class AssertJPrimitiveTemplates {
|
||||
private AssertJPrimitiveTemplates() {}
|
||||
|
||||
static final class AssertThatIsEqualTo {
|
||||
@BeforeTemplate
|
||||
AbstractBooleanAssert<?> before(boolean actual, boolean expected) {
|
||||
return Refaster.anyOf(
|
||||
assertThat(actual == expected).isTrue(), assertThat(actual != expected).isFalse());
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
AbstractBooleanAssert<?> before(double actual, double expected) {
|
||||
return Refaster.anyOf(
|
||||
assertThat(actual == expected).isTrue(), assertThat(actual != expected).isFalse());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractBooleanAssert<?> after(boolean actual, boolean expected) {
|
||||
return assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatIsNotEqualTo {
|
||||
@BeforeTemplate
|
||||
AbstractBooleanAssert<?> before(boolean actual, boolean expected) {
|
||||
return Refaster.anyOf(
|
||||
assertThat(actual != expected).isTrue(), assertThat(actual == expected).isFalse());
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
AbstractBooleanAssert<?> before(double actual, double expected) {
|
||||
return Refaster.anyOf(
|
||||
assertThat(actual != expected).isTrue(), assertThat(actual == expected).isFalse());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractBooleanAssert<?> after(boolean actual, boolean expected) {
|
||||
return assertThat(actual).isNotEqualTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatIsLessThan {
|
||||
@BeforeTemplate
|
||||
AbstractBooleanAssert<?> before(double actual, double expected) {
|
||||
return Refaster.anyOf(
|
||||
assertThat(actual < expected).isTrue(), assertThat(actual >= expected).isFalse());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractDoubleAssert<?> after(double actual, double expected) {
|
||||
return assertThat(actual).isLessThan(expected);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatIsLessThanOrEqualTo {
|
||||
@BeforeTemplate
|
||||
AbstractBooleanAssert<?> before(double actual, double expected) {
|
||||
return Refaster.anyOf(
|
||||
assertThat(actual <= expected).isTrue(), assertThat(actual > expected).isFalse());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractDoubleAssert<?> after(double actual, double expected) {
|
||||
return assertThat(actual).isLessThanOrEqualTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatIsGreaterThan {
|
||||
@BeforeTemplate
|
||||
AbstractBooleanAssert<?> before(double actual, double expected) {
|
||||
return Refaster.anyOf(
|
||||
assertThat(actual > expected).isTrue(), assertThat(actual <= expected).isFalse());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractDoubleAssert<?> after(double actual, double expected) {
|
||||
return assertThat(actual).isGreaterThan(expected);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatIsGreaterThanOrEqualTo {
|
||||
@BeforeTemplate
|
||||
AbstractBooleanAssert<?> before(double actual, double expected) {
|
||||
return Refaster.anyOf(
|
||||
assertThat(actual >= expected).isTrue(), assertThat(actual < expected).isFalse());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractDoubleAssert<?> after(double actual, double expected) {
|
||||
return assertThat(actual).isGreaterThanOrEqualTo(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ import org.assertj.core.api.ObjectEnumerableAssert;
|
||||
import org.assertj.core.api.OptionalDoubleAssert;
|
||||
import org.assertj.core.api.OptionalIntAssert;
|
||||
import org.assertj.core.api.OptionalLongAssert;
|
||||
import tech.picnic.errorprone.refaster.util.IsArray;
|
||||
import tech.picnic.errorprone.refaster.matchers.IsArray;
|
||||
|
||||
/** Refaster templates related to AssertJ expressions and statements. */
|
||||
// XXX: Most `AbstractIntegerAssert` rules can also be applied for other primitive types. Generate
|
||||
@@ -63,7 +63,7 @@ import tech.picnic.errorprone.refaster.util.IsArray;
|
||||
// ^ And variants.
|
||||
// XXX: Consider splitting this class into multiple classes.
|
||||
// XXX: Some of these rules may not apply given the updated TestNG rewrite rules. Review.
|
||||
// XXX: For the templates which "unwrap" explicitly enumerated collections, also introduce variants
|
||||
// XXX: For the templates that "unwrap" explicitly enumerated collections, also introduce variants
|
||||
// with explicitly enumerated sorted collections. (Requires that the type bound is Comparable.)
|
||||
// XXX: Handle `.isEqualTo(explicitlyEnumeratedCollection)`. Can be considered equivalent to
|
||||
// `.containsOnly(elements)`. (This does mean the auto-generated code needs to be more advanced.
|
||||
@@ -106,12 +106,12 @@ import tech.picnic.errorprone.refaster.util.IsArray;
|
||||
// candidates, such as `assertThat(ImmutableSet(foo, bar)).XXX`
|
||||
// XXX: Write generic plugin to replace explicit array parameters with varargs (`new int[] {1, 2}`
|
||||
// -> `1, 2`).
|
||||
// XXX: Write plugin which drops any `.withFailMessage` which doesn't include a compile-time
|
||||
// constant string? Most of these are useless.
|
||||
// XXX: Write plugin which identifies `.get().propertyAccess()` and "pushes" this out. Would only
|
||||
// XXX: Write plugin that drops any `.withFailMessage` that doesn't include a compile-time constant
|
||||
// string? Most of these are useless.
|
||||
// XXX: Write plugin that identifies `.get().propertyAccess()` and "pushes" this out. Would only
|
||||
// nicely work for non-special types, though, cause after `extracting(propertyAccess)` many
|
||||
// operations are not available...
|
||||
// XXX: Write plugin which identifies repeated `assertThat(someProp.xxx)` calls and bundles these
|
||||
// XXX: Write plugin that identifies repeated `assertThat(someProp.xxx)` calls and bundles these
|
||||
// somehow.
|
||||
// XXX: `abstractOptionalAssert.get().satisfies(pred)` ->
|
||||
// `abstractOptionalAssert.hasValueSatisfying(pred)`.
|
||||
|
||||
@@ -429,7 +429,7 @@ final class AssertJThrowingCallableTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Drop this template in favour of a generic Error Prone check which flags
|
||||
// XXX: Drop this template in favour of a generic Error Prone check that flags
|
||||
// `String.format(...)` arguments to a wide range of format methods.
|
||||
static final class AbstractThrowableAssertHasMessage {
|
||||
@BeforeTemplate
|
||||
@@ -449,7 +449,7 @@ final class AssertJThrowingCallableTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Drop this template in favour of a generic Error Prone check which flags
|
||||
// XXX: Drop this template in favour of a generic Error Prone check that flags
|
||||
// `String.format(...)` arguments to a wide range of format methods.
|
||||
static final class AbstractThrowableAssertWithFailMessage {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -82,7 +82,7 @@ final class AssortedTemplates {
|
||||
* ImmutableSet#toImmutableSet()} and produces a more compact object.
|
||||
*
|
||||
* <p><strong>Warning:</strong> this rewrite rule is not completely behavior preserving: while the
|
||||
* original code produces a set which iterates over the elements in encounter order, the
|
||||
* original code produces a set that iterates over the elements in encounter order, the
|
||||
* replacement code iterates over the elements in enum definition order.
|
||||
*/
|
||||
// XXX: ^ Consider emitting a comment warning about this fact?
|
||||
@@ -118,7 +118,7 @@ final class AssortedTemplates {
|
||||
|
||||
/** Don't unnecessarily repeat boolean expressions. */
|
||||
// XXX: This template captures only the simplest case. `@AlsoNegation` doesn't help. Consider
|
||||
// contributing a Refaster patch which handles the negation in the `@BeforeTemplate` more
|
||||
// contributing a Refaster patch, which handles the negation in the `@BeforeTemplate` more
|
||||
// intelligently.
|
||||
static final class LogicalImplication {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.ToDoubleFunction;
|
||||
import java.util.function.ToIntFunction;
|
||||
@@ -259,4 +260,36 @@ final class ComparatorTemplates {
|
||||
return Comparators.max(value1, value2, cmp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer a method reference to {@link Comparators#min(Comparable, Comparable)} over calling
|
||||
* {@link BinaryOperator#minBy(Comparator)} with {@link Comparator#naturalOrder()}.
|
||||
*/
|
||||
static final class ComparatorsMin<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
BinaryOperator<T> before() {
|
||||
return BinaryOperator.minBy(naturalOrder());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
BinaryOperator<T> after() {
|
||||
return Comparators::min;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer a method reference to {@link Comparators#max(Comparable, Comparable)} over calling
|
||||
* {@link BinaryOperator#minBy(Comparator)} with {@link Comparator#naturalOrder()}.
|
||||
*/
|
||||
static final class ComparatorsMax<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
BinaryOperator<T> before() {
|
||||
return BinaryOperator.maxBy(naturalOrder());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
BinaryOperator<T> after() {
|
||||
return Comparators::max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,18 +36,16 @@ final class EqualityTemplates {
|
||||
/** Prefer {@link Object#equals(Object)} over the equivalent lambda function. */
|
||||
// XXX: As it stands, this rule is a special case of what `MethodReferenceUsage` tries to achieve.
|
||||
// If/when `MethodReferenceUsage` becomes production ready, we should simply drop this check.
|
||||
// XXX: Alternatively, the rule should be replaced with a plugin which also identifies cases where
|
||||
// XXX: Alternatively, the rule should be replaced with a plugin that also identifies cases where
|
||||
// the arguments are swapped but simplification is possible anyway, by virtue of `v` being
|
||||
// non-null.
|
||||
static final class EqualsPredicate<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("NoFunctionalReturnType")
|
||||
Predicate<T> before(T v) {
|
||||
return e -> v.equals(e);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("NoFunctionalReturnType")
|
||||
Predicate<T> after(T v) {
|
||||
return v::equals;
|
||||
}
|
||||
@@ -70,17 +68,14 @@ final class EqualityTemplates {
|
||||
* Don't negate an equality test or use the ternary operator to compare two booleans; directly
|
||||
* test for inequality instead.
|
||||
*/
|
||||
// XXX: Replacing `a ? !b : b` with `a != b` changes semantics if both `a` and `b` are boxed
|
||||
// booleans.
|
||||
static final class Negation {
|
||||
@BeforeTemplate
|
||||
boolean before(boolean a, boolean b) {
|
||||
return Refaster.anyOf(!(a == b), a ? !b : b);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
boolean before(long a, long b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
boolean before(double a, double b) {
|
||||
return !(a == b);
|
||||
@@ -101,17 +96,14 @@ final class EqualityTemplates {
|
||||
* Don't negate an inequality test or use the ternary operator to compare two booleans; directly
|
||||
* test for equality instead.
|
||||
*/
|
||||
// XXX: Replacing `a ? b : !b` with `a == b` changes semantics if both `a` and `b` are boxed
|
||||
// booleans.
|
||||
static final class IndirectDoubleNegation {
|
||||
@BeforeTemplate
|
||||
boolean before(boolean a, boolean b) {
|
||||
return Refaster.anyOf(!(a != b), a ? b : !b);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
boolean before(long a, long b) {
|
||||
return !(a != b);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
boolean before(double a, double b) {
|
||||
return !(a != b);
|
||||
|
||||
@@ -33,8 +33,8 @@ final class ImmutableListMultimapTemplates {
|
||||
* Prefer {@link ImmutableListMultimap#builder()} over the associated constructor on constructions
|
||||
* that produce a less-specific type.
|
||||
*/
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. Anything
|
||||
// we can do about that?
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableListMultimapBuilder<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableMultimap.Builder<K, V> before() {
|
||||
@@ -141,8 +141,8 @@ final class ImmutableListMultimapTemplates {
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't map a a stream's elements to map entries, only to subsequently collect them into an
|
||||
* {@link ImmutableListMultimap}. The collection can be performed directly.
|
||||
* Don't map stream's elements to map entries, only to subsequently collect them into an {@link
|
||||
* ImmutableListMultimap}. The collection can be performed directly.
|
||||
*/
|
||||
abstract static class StreamOfMapEntriesToImmutableListMultimap<E, K, V> {
|
||||
@Placeholder(allowsIdentity = true)
|
||||
|
||||
@@ -26,8 +26,8 @@ final class ImmutableListTemplates {
|
||||
private ImmutableListTemplates() {}
|
||||
|
||||
/** Prefer {@link ImmutableList#builder()} over the associated constructor. */
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. Anything
|
||||
// we can do about that?
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableListBuilder<T> {
|
||||
@BeforeTemplate
|
||||
ImmutableList.Builder<T> before() {
|
||||
@@ -194,7 +194,7 @@ final class ImmutableListTemplates {
|
||||
* Prefer {@link ImmutableList#of(Object, Object)} over alternatives that don't communicate the
|
||||
* immutability of the resulting list at the type level.
|
||||
*/
|
||||
// XXX: Consider writing an Error Prone check which also flags straightforward
|
||||
// XXX: Consider writing an Error Prone check that also flags straightforward
|
||||
// `ImmutableList.builder()` usages.
|
||||
static final class ImmutableListOf2<T> {
|
||||
@BeforeTemplate
|
||||
@@ -212,7 +212,7 @@ final class ImmutableListTemplates {
|
||||
* Prefer {@link ImmutableList#of(Object, Object, Object)} over alternatives that don't
|
||||
* communicate the immutability of the resulting list at the type level.
|
||||
*/
|
||||
// XXX: Consider writing an Error Prone check which also flags straightforward
|
||||
// XXX: Consider writing an Error Prone check that also flags straightforward
|
||||
// `ImmutableList.builder()` usages.
|
||||
static final class ImmutableListOf3<T> {
|
||||
@BeforeTemplate
|
||||
@@ -230,7 +230,7 @@ final class ImmutableListTemplates {
|
||||
* Prefer {@link ImmutableList#of(Object, Object, Object, Object)} over alternatives that don't
|
||||
* communicate the immutability of the resulting list at the type level.
|
||||
*/
|
||||
// XXX: Consider writing an Error Prone check which also flags straightforward
|
||||
// XXX: Consider writing an Error Prone check that also flags straightforward
|
||||
// `ImmutableList.builder()` usages.
|
||||
static final class ImmutableListOf4<T> {
|
||||
@BeforeTemplate
|
||||
@@ -248,7 +248,7 @@ final class ImmutableListTemplates {
|
||||
* Prefer {@link ImmutableList#of(Object, Object, Object, Object, Object)} over alternatives that
|
||||
* don't communicate the immutability of the resulting list at the type level.
|
||||
*/
|
||||
// XXX: Consider writing an Error Prone check which also flags straightforward
|
||||
// XXX: Consider writing an Error Prone check that also flags straightforward
|
||||
// `ImmutableList.builder()` usages.
|
||||
static final class ImmutableListOf5<T> {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -27,8 +27,8 @@ final class ImmutableMapTemplates {
|
||||
private ImmutableMapTemplates() {}
|
||||
|
||||
/** Prefer {@link ImmutableMap#builder()} over the associated constructor. */
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. Anything
|
||||
// we can do about that?
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableMapBuilder<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableMap.Builder<K, V> before() {
|
||||
@@ -135,7 +135,7 @@ final class ImmutableMapTemplates {
|
||||
abstract V valueFunction(@MayOptionallyUse E element);
|
||||
|
||||
// XXX: We could add variants in which the entry is created some other way, but we have another
|
||||
// rule which covers canonicalization to `Map.entry`.
|
||||
// rule that covers canonicalization to `Map.entry`.
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(Stream<E> stream) {
|
||||
return stream
|
||||
|
||||
@@ -19,8 +19,8 @@ final class ImmutableMultisetTemplates {
|
||||
private ImmutableMultisetTemplates() {}
|
||||
|
||||
/** Prefer {@link ImmutableMultiset#builder()} over the associated constructor. */
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. Anything
|
||||
// we can do about that?
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableMultisetBuilder<T> {
|
||||
@BeforeTemplate
|
||||
ImmutableMultiset.Builder<T> before() {
|
||||
|
||||
@@ -27,8 +27,8 @@ final class ImmutableSetMultimapTemplates {
|
||||
private ImmutableSetMultimapTemplates() {}
|
||||
|
||||
/** Prefer {@link ImmutableSetMultimap#builder()} over the associated constructor. */
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. Anything
|
||||
// we can do about that?
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableSetMultimapBuilder<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableSetMultimap.Builder<K, V> before() {
|
||||
|
||||
@@ -23,8 +23,8 @@ final class ImmutableSetTemplates {
|
||||
private ImmutableSetTemplates() {}
|
||||
|
||||
/** Prefer {@link ImmutableSet#builder()} over the associated constructor. */
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. Anything
|
||||
// we can do about that?
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableSetBuilder<T> {
|
||||
@BeforeTemplate
|
||||
ImmutableSet.Builder<T> before() {
|
||||
@@ -142,7 +142,7 @@ final class ImmutableSetTemplates {
|
||||
* Prefer {@link ImmutableSet#of(Object, Object)} over alternatives that don't communicate the
|
||||
* immutability of the resulting set at the type level.
|
||||
*/
|
||||
// XXX: Consider writing an Error Prone check which also flags straightforward
|
||||
// XXX: Consider writing an Error Prone check that also flags straightforward
|
||||
// `ImmutableSet.builder()` usages.
|
||||
static final class ImmutableSetOf2<T> {
|
||||
@BeforeTemplate
|
||||
@@ -160,7 +160,7 @@ final class ImmutableSetTemplates {
|
||||
* Prefer {@link ImmutableSet#of(Object, Object, Object)} over alternatives that don't communicate
|
||||
* the immutability of the resulting set at the type level.
|
||||
*/
|
||||
// XXX: Consider writing an Error Prone check which also flags straightforward
|
||||
// XXX: Consider writing an Error Prone check that also flags straightforward
|
||||
// `ImmutableSet.builder()` usages.
|
||||
static final class ImmutableSetOf3<T> {
|
||||
@BeforeTemplate
|
||||
@@ -178,7 +178,7 @@ final class ImmutableSetTemplates {
|
||||
* Prefer {@link ImmutableSet#of(Object, Object, Object, Object)} over alternatives that don't
|
||||
* communicate the immutability of the resulting set at the type level.
|
||||
*/
|
||||
// XXX: Consider writing an Error Prone check which also flags straightforward
|
||||
// XXX: Consider writing an Error Prone check that also flags straightforward
|
||||
// `ImmutableSet.builder()` usages.
|
||||
static final class ImmutableSetOf4<T> {
|
||||
@BeforeTemplate
|
||||
@@ -196,7 +196,7 @@ final class ImmutableSetTemplates {
|
||||
* Prefer {@link ImmutableSet#of(Object, Object, Object, Object, Object)} over alternatives that
|
||||
* don't communicate the immutability of the resulting set at the type level.
|
||||
*/
|
||||
// XXX: Consider writing an Error Prone check which also flags straightforward
|
||||
// XXX: Consider writing an Error Prone check that also flags straightforward
|
||||
// `ImmutableSet.builder()` usages.
|
||||
static final class ImmutableSetOf5<T> {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -35,8 +35,8 @@ final class ImmutableSortedMapTemplates {
|
||||
* Prefer {@link ImmutableSortedMap#naturalOrder()} over the alternative that requires explicitly
|
||||
* providing the {@link Comparator}.
|
||||
*/
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. Anything
|
||||
// we can do about that?
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableSortedMapNaturalOrderBuilder<K extends Comparable<? super K>, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableSortedMap.Builder<K, V> before() {
|
||||
@@ -53,8 +53,8 @@ final class ImmutableSortedMapTemplates {
|
||||
* Prefer {@link ImmutableSortedMap#reverseOrder()} over the alternative that requires explicitly
|
||||
* providing the {@link Comparator}.
|
||||
*/
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. Anything
|
||||
// we can do about that?
|
||||
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
|
||||
// https://github.com/google/error-prone/pull/2706.
|
||||
static final class ImmutableSortedMapReverseOrderBuilder<K extends Comparable<? super K>, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableSortedMap.Builder<K, V> before() {
|
||||
|
||||
@@ -9,11 +9,38 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Refaster templates related to expressions dealing with (possibly) null values. */
|
||||
final class NullTemplates {
|
||||
private NullTemplates() {}
|
||||
|
||||
/** Prefer the {@code ==} operator over {@link Objects#isNull(Object)}. */
|
||||
static final class IsNull {
|
||||
@BeforeTemplate
|
||||
boolean before(@Nullable Object object) {
|
||||
return Objects.isNull(object);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
boolean after(@Nullable Object object) {
|
||||
return object == null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer the {@code !=} operator over {@link Objects#nonNull(Object)}. */
|
||||
static final class IsNotNull {
|
||||
@BeforeTemplate
|
||||
boolean before(@Nullable Object object) {
|
||||
return Objects.nonNull(object);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
boolean after(@Nullable Object object) {
|
||||
return object != null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Objects#requireNonNullElse(Object, Object)} over the Guava alternative. */
|
||||
// XXX: This rule is not valid in case `second` is `@Nullable`: in that case the Guava variant
|
||||
// will return `null`, while the JDK variant will throw an NPE.
|
||||
@@ -33,13 +60,11 @@ final class NullTemplates {
|
||||
/** Prefer {@link Objects#isNull(Object)} over the equivalent lambda function. */
|
||||
static final class IsNullFunction<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("NoFunctionalReturnType")
|
||||
Predicate<T> before() {
|
||||
return o -> o == null;
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("NoFunctionalReturnType")
|
||||
Predicate<T> after() {
|
||||
return Objects::isNull;
|
||||
}
|
||||
@@ -48,13 +73,11 @@ final class NullTemplates {
|
||||
/** Prefer {@link Objects#nonNull(Object)} over the equivalent lambda function. */
|
||||
static final class NonNullFunction<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("NoFunctionalReturnType")
|
||||
Predicate<T> before() {
|
||||
return o -> o != null;
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("NoFunctionalReturnType")
|
||||
Predicate<T> after() {
|
||||
return Objects::nonNull;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
|
||||
import com.google.errorprone.refaster.annotation.Placeholder;
|
||||
import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
@@ -81,13 +82,11 @@ final class OptionalTemplates {
|
||||
// generalization. If/when Refaster is extended to understand this, delete the template above.
|
||||
static final class OptionalOrElseThrowMethodReference<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("NoFunctionalReturnType")
|
||||
Function<Optional<T>, T> before() {
|
||||
return Optional::get;
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@SuppressWarnings("NoFunctionalReturnType")
|
||||
Function<Optional<T>, T> after() {
|
||||
return Optional::orElseThrow;
|
||||
}
|
||||
@@ -154,7 +153,7 @@ final class OptionalTemplates {
|
||||
* Prefer {@link Optional#filter(Predicate)} over {@link Optional#map(Function)} when converting
|
||||
* an {@link Optional} to a boolean.
|
||||
*/
|
||||
abstract static class MapOptionalToBoolean<T> {
|
||||
static final class MapOptionalToBoolean<T> {
|
||||
@BeforeTemplate
|
||||
boolean before(Optional<T> optional, Function<? super T, Boolean> predicate) {
|
||||
return optional.map(predicate).orElse(Refaster.anyOf(false, Boolean.FALSE));
|
||||
@@ -167,7 +166,7 @@ final class OptionalTemplates {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Optional#map} over a {@link Optional#flatMap} which wraps the result of a
|
||||
* Prefer {@link Optional#map} over a {@link Optional#flatMap} that wraps the result of a
|
||||
* transformation in an {@link Optional}; the former operation transforms {@code null} to {@link
|
||||
* Optional#empty()}.
|
||||
*/
|
||||
@@ -316,7 +315,7 @@ final class OptionalTemplates {
|
||||
}
|
||||
|
||||
/** Prefer {@link Optional#or(Supplier)} over more verbose alternatives. */
|
||||
abstract static class OptionalOrOtherOptional<T> {
|
||||
static final class OptionalOrOtherOptional<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("NestedOptionals" /* Auto-fix for the `NestedOptionals` check. */)
|
||||
Optional<T> before(Optional<T> optional1, Optional<T> optional2) {
|
||||
@@ -334,6 +333,26 @@ final class OptionalTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid unnecessary operations on an {@link Optional} that ultimately result in that very same
|
||||
* {@link Optional}.
|
||||
*/
|
||||
static final class OptionalIdentity<T> {
|
||||
@BeforeTemplate
|
||||
Optional<T> before(Optional<T> optional, Comparator<? super T> comparator) {
|
||||
return Refaster.anyOf(
|
||||
optional.stream().findFirst(),
|
||||
optional.stream().findAny(),
|
||||
optional.stream().min(comparator),
|
||||
optional.stream().max(comparator));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Optional<T> after(Optional<T> optional) {
|
||||
return optional;
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Add a rule for:
|
||||
// `optional.flatMap(x -> pred(x) ? Optional.empty() : Optional.of(x))` and variants.
|
||||
// (Maybe canonicalize the inner expression. Maybe we rewrite already.)
|
||||
|
||||
@@ -10,11 +10,6 @@ final class PrimitiveTemplates {
|
||||
|
||||
/** Avoid contrived ways of expressing the "less than" relationship. */
|
||||
static final class LessThan {
|
||||
@BeforeTemplate
|
||||
boolean before(long a, long b) {
|
||||
return !(a >= b);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
boolean before(double a, double b) {
|
||||
return !(a >= b);
|
||||
@@ -28,11 +23,6 @@ final class PrimitiveTemplates {
|
||||
|
||||
/** Avoid contrived ways of expressing the "less than or equal to" relationship. */
|
||||
static final class LessThanOrEqualTo {
|
||||
@BeforeTemplate
|
||||
boolean before(long a, long b) {
|
||||
return !(a > b);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
boolean before(double a, double b) {
|
||||
return !(a > b);
|
||||
@@ -46,11 +36,6 @@ final class PrimitiveTemplates {
|
||||
|
||||
/** Avoid contrived ways of expressing the "greater than" relationship. */
|
||||
static final class GreaterThan {
|
||||
@BeforeTemplate
|
||||
boolean before(long a, long b) {
|
||||
return !(a <= b);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
boolean before(double a, double b) {
|
||||
return !(a <= b);
|
||||
@@ -64,11 +49,6 @@ final class PrimitiveTemplates {
|
||||
|
||||
/** Avoid contrived ways of expressing the "greater than or equal to" relationship. */
|
||||
static final class GreaterThanOrEqualTo {
|
||||
@BeforeTemplate
|
||||
boolean before(long a, long b) {
|
||||
return !(a < b);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
boolean before(double a, double b) {
|
||||
return !(a < b);
|
||||
|
||||
@@ -9,10 +9,12 @@ import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
|
||||
import com.google.errorprone.refaster.annotation.NotMatches;
|
||||
import com.google.errorprone.refaster.annotation.Placeholder;
|
||||
import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
@@ -22,16 +24,35 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import reactor.test.publisher.PublisherProbe;
|
||||
import tech.picnic.errorprone.refaster.matchers.ThrowsCheckedException;
|
||||
|
||||
/** Refaster templates related to Reactor expressions and statements. */
|
||||
final class ReactorTemplates {
|
||||
private ReactorTemplates() {}
|
||||
|
||||
/**
|
||||
* Prefer {@link Mono#fromSupplier(Supplier)} over {@link Mono#fromCallable(Callable)} where
|
||||
* feasible.
|
||||
*/
|
||||
static final class MonoFromSupplier<T> {
|
||||
@BeforeTemplate
|
||||
Mono<T> before(@NotMatches(ThrowsCheckedException.class) Callable<? extends T> supplier) {
|
||||
return Mono.fromCallable(supplier);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<T> after(Supplier<? extends T> supplier) {
|
||||
return Mono.fromSupplier(supplier);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Mono#justOrEmpty(Optional)} over more verbose alternatives. */
|
||||
// XXX: If `optional` is a constant and effectively-final expression then the `Mono.defer` can be
|
||||
// dropped. Should look into Refaster support for identifying this.
|
||||
static final class MonoFromOptional<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"MonoFromSupplier" /* `optional` may match a checked exception-throwing expression. */)
|
||||
Mono<T> before(Optional<T> optional) {
|
||||
return Refaster.anyOf(
|
||||
Mono.fromCallable(() -> optional.orElse(null)),
|
||||
@@ -207,7 +228,7 @@ final class ReactorTemplates {
|
||||
/**
|
||||
* Prefer a collection using {@link MoreCollectors#toOptional()} over more contrived alternatives.
|
||||
*/
|
||||
// XXX: Consider creating a plugin which flags/discourages `Mono<Optional<T>>` method return
|
||||
// XXX: Consider creating a plugin that flags/discourages `Mono<Optional<T>>` method return
|
||||
// types, just as we discourage nullable `Boolean`s and `Optional`s.
|
||||
static final class MonoCollectToOptional<T> {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -9,7 +9,7 @@ import io.reactivex.Flowable;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.jspecify.nullness.Nullable;
|
||||
import reactor.adapter.rxjava.RxJava2Adapter;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -21,14 +21,14 @@ final class RxJava2AdapterTemplates {
|
||||
/** Use the fluent API style when using {@link RxJava2Adapter#completableToMono}. */
|
||||
static final class CompletableToMono {
|
||||
@BeforeTemplate
|
||||
Mono<Void> before(Completable completable) {
|
||||
Mono<@Nullable Void> before(Completable completable) {
|
||||
return Refaster.anyOf(
|
||||
RxJava2Adapter.completableToMono(completable),
|
||||
completable.to(RxJava2Adapter::completableToMono));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<Void> after(Completable completable) {
|
||||
Mono<@Nullable Void> after(Completable completable) {
|
||||
return completable.as(RxJava2Adapter::completableToMono);
|
||||
}
|
||||
}
|
||||
@@ -39,12 +39,12 @@ final class RxJava2AdapterTemplates {
|
||||
*/
|
||||
static final class FlowableToFlux<T> {
|
||||
@BeforeTemplate
|
||||
Publisher<T> before(Flowable<T> flowable) {
|
||||
Flux<T> before(Flowable<T> flowable) {
|
||||
return Refaster.anyOf(
|
||||
flowable.compose(Flux::from),
|
||||
Flux.from(flowable),
|
||||
flowable.to(Flux::from),
|
||||
flowable.as(Flux::from),
|
||||
flowable.compose(RxJava2Adapter::flowableToFlux),
|
||||
RxJava2Adapter.flowableToFlux(flowable),
|
||||
flowable.to(RxJava2Adapter::flowableToFlux));
|
||||
}
|
||||
|
||||
@@ -60,12 +60,11 @@ final class RxJava2AdapterTemplates {
|
||||
*/
|
||||
static final class FluxToFlowable<T> {
|
||||
@BeforeTemplate
|
||||
Publisher<T> before(Flux<T> flux) {
|
||||
Flowable<T> before(Flux<T> flux) {
|
||||
return Refaster.anyOf(
|
||||
Flowable.fromPublisher(flux),
|
||||
flux.transform(Flowable::fromPublisher),
|
||||
flux.as(Flowable::fromPublisher),
|
||||
flux.transform(RxJava2Adapter::fluxToFlowable));
|
||||
RxJava2Adapter.fluxToFlowable(flux));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@@ -132,12 +131,11 @@ final class RxJava2AdapterTemplates {
|
||||
*/
|
||||
static final class MonoToFlowable<T> {
|
||||
@BeforeTemplate
|
||||
Publisher<T> before(Mono<T> mono) {
|
||||
Flowable<T> before(Mono<T> mono) {
|
||||
return Refaster.anyOf(
|
||||
Flowable.fromPublisher(mono),
|
||||
mono.transform(Flowable::fromPublisher),
|
||||
mono.as(Flowable::fromPublisher),
|
||||
mono.transform(RxJava2Adapter::monoToFlowable));
|
||||
RxJava2Adapter.monoToFlowable(mono));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
|
||||
@@ -13,7 +13,9 @@ import com.google.errorprone.refaster.annotation.AlsoNegation;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Refaster templates related to expressions dealing with {@link String}s. */
|
||||
@@ -106,6 +108,40 @@ final class StringTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer direct invocation of {@link String#valueOf(Object)} over the indirection introduced by
|
||||
* {@link Objects#toString(Object)}.
|
||||
*/
|
||||
static final class StringValueOf {
|
||||
@BeforeTemplate
|
||||
String before(Object object) {
|
||||
return Objects.toString(object);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
String after(Object object) {
|
||||
return String.valueOf(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer direct delegation to {@link String#valueOf(Object)} over the indirection introduced by
|
||||
* {@link Objects#toString(Object)}.
|
||||
*/
|
||||
// XXX: This template is analogous to `StringValueOf` above. Arguably this is its generalization.
|
||||
// If/when Refaster is extended to understand this, delete the template above.
|
||||
static final class StringValueOfMethodReference {
|
||||
@BeforeTemplate
|
||||
Function<Object, String> before() {
|
||||
return Objects::toString;
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Function<Object, String> after() {
|
||||
return String::valueOf;
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily use the two-argument {@link String#substring(int, int)}. */
|
||||
static final class SubstringRemainder {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.testng.Assert;
|
||||
import org.testng.Assert.ThrowingRunnable;
|
||||
|
||||
/**
|
||||
* Refaster templates which replace TestNG assertions with equivalent AssertJ assertions.
|
||||
* Refaster templates that replace TestNG assertions with equivalent AssertJ assertions.
|
||||
*
|
||||
* <p>Some of the classes below have TestNG {@code @BeforeTemplate}s that reference wildcard type
|
||||
* bounds ({@code <?>}), while the associated AssertJ {@code @AfterTemplate}s reference stricter
|
||||
@@ -39,9 +39,9 @@ import org.testng.Assert.ThrowingRunnable;
|
||||
* the appropriate (more specific) types _will_ be inferred properly when plugged into AssertJ's
|
||||
* API.
|
||||
*
|
||||
* <p>The following is an example of a TestNG statement which would not be rewritten if it weren't
|
||||
* <p>The following is an example of a TestNG statement, which would not be rewritten if it weren't
|
||||
* for the wildcard matching (note that the type parameters of the map on the right-hand side will
|
||||
* be inferred to be {@code <Object, Object>} rather than {@code <String, Object>}.)
|
||||
* be inferred to be {@code <Object, Object>} rather than {@code <String, Object>}).
|
||||
*
|
||||
* <pre>{@code
|
||||
* List<Map<String, Object>> myMaps = new ArrayList<>();
|
||||
|
||||
@@ -174,7 +174,7 @@ final class WebClientTemplates {
|
||||
}
|
||||
|
||||
/** Don't unnecessarily use {@link RequestHeadersUriSpec#uri(Function)}. */
|
||||
abstract static class RequestHeadersUriSpecUri {
|
||||
static final class RequestHeadersUriSpecUri {
|
||||
@BeforeTemplate
|
||||
RequestHeadersSpec<?> before(
|
||||
RequestHeadersUriSpec<?> requestHeadersUriSpec,
|
||||
|
||||
@@ -120,7 +120,7 @@ final class AmbiguousJsonCreatorTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import com.fasterxml.jackson.annotation.JsonCreator;",
|
||||
"",
|
||||
"enum A {",
|
||||
@@ -132,7 +132,7 @@ final class AmbiguousJsonCreatorTest {
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import com.fasterxml.jackson.annotation.JsonCreator;",
|
||||
"",
|
||||
"enum A {",
|
||||
|
||||
@@ -73,7 +73,7 @@ final class AutowiredConstructorTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/Container.java",
|
||||
"Container.java",
|
||||
"import org.springframework.beans.factory.annotation.Autowired;",
|
||||
"",
|
||||
"interface Container {",
|
||||
@@ -89,7 +89,7 @@ final class AutowiredConstructorTest {
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/Container.java",
|
||||
"Container.java",
|
||||
"import org.springframework.beans.factory.annotation.Autowired;",
|
||||
"",
|
||||
"interface Container {",
|
||||
|
||||
@@ -135,7 +135,7 @@ final class CanonicalAnnotationSyntaxTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/pkg/A.java",
|
||||
"pkg/A.java",
|
||||
"package pkg;",
|
||||
"",
|
||||
"import pkg.A.Foo;",
|
||||
@@ -206,7 +206,7 @@ final class CanonicalAnnotationSyntaxTest {
|
||||
" A trailingComma3();",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/pkg/A.java",
|
||||
"pkg/A.java",
|
||||
"package pkg;",
|
||||
"",
|
||||
"import pkg.A.Foo;",
|
||||
|
||||
@@ -71,13 +71,13 @@ final class EmptyMethodTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"final class A {",
|
||||
" void instanceMethod() {}",
|
||||
"",
|
||||
" static void staticMethod() {}",
|
||||
"}")
|
||||
.addOutputLines("out/A.java", "final class A {}")
|
||||
.addOutputLines("A.java", "final class A {}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,13 +44,13 @@ final class ErrorProneTestHelperSourceFormatTest {
|
||||
"",
|
||||
" refactoringTestHelper",
|
||||
" // BUG: Diagnostic contains: Test code should follow the Google Java style",
|
||||
" .addInputLines(\"in/A.java\", \"class A { }\")",
|
||||
" .addInputLines(\"A.java\", \"class A { }\")",
|
||||
" // BUG: Diagnostic contains: Test code should follow the Google Java style",
|
||||
" .addOutputLines(\"out/A.java\", \"class A { }\")",
|
||||
" .addOutputLines(\"A.java\", \"class A { }\")",
|
||||
" // BUG: Diagnostic contains: Test code should follow the Google Java style",
|
||||
" .addInputLines(\"in/B.java\", \"import java.util.Map;\", \"\", \"class B {}\")",
|
||||
" .addInputLines(\"B.java\", \"import java.util.Map;\", \"\", \"class B {}\")",
|
||||
" // Unused import, but in an output file, so not flagged.",
|
||||
" .addOutputLines(\"out/B.java\", \"import java.util.Map;\", \"\", \"class B {}\")",
|
||||
" .addOutputLines(\"B.java\", \"import java.util.Map;\", \"\", \"class B {}\")",
|
||||
" .doTest(TestMode.TEXT_MATCH);",
|
||||
" }",
|
||||
"}")
|
||||
@@ -65,7 +65,7 @@ final class ErrorProneTestHelperSourceFormatTest {
|
||||
*/
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
@@ -90,14 +90,14 @@ final class ErrorProneTestHelperSourceFormatTest {
|
||||
"",
|
||||
" refactoringTestHelper",
|
||||
" .addInputLines(",
|
||||
" \"in/A.java\",",
|
||||
" \"A.java\",",
|
||||
" \"import java.util.Map;\",",
|
||||
" \"import java.util.Collection;\",",
|
||||
" \"import java.util.List;\",",
|
||||
" \"\",",
|
||||
" \"interface A extends List<A>, Map<A,A> { }\")",
|
||||
" .addOutputLines(",
|
||||
" \"out/A.java\",",
|
||||
" \"A.java\",",
|
||||
" \"import java.util.Map;\",",
|
||||
" \"import java.util.Collection;\",",
|
||||
" \"import java.util.List;\",",
|
||||
@@ -107,7 +107,7 @@ final class ErrorProneTestHelperSourceFormatTest {
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
@@ -131,13 +131,13 @@ final class ErrorProneTestHelperSourceFormatTest {
|
||||
"",
|
||||
" refactoringTestHelper",
|
||||
" .addInputLines(",
|
||||
" \"in/A.java\",",
|
||||
" \"A.java\",",
|
||||
" \"import java.util.List;\",",
|
||||
" \"import java.util.Map;\",",
|
||||
" \"\",",
|
||||
" \"interface A extends List<A>, Map<A, A> {}\")",
|
||||
" .addOutputLines(",
|
||||
" \"out/A.java\",",
|
||||
" \"A.java\",",
|
||||
" \"import java.util.Collection;\",",
|
||||
" \"import java.util.List;\",",
|
||||
" \"import java.util.Map;\",",
|
||||
|
||||
@@ -67,7 +67,7 @@ final class FluxFlatMapUsageTest {
|
||||
refactoringTestHelper
|
||||
.setFixChooser(FixChoosers.FIRST)
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"",
|
||||
"class A {",
|
||||
@@ -77,7 +77,7 @@ final class FluxFlatMapUsageTest {
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"",
|
||||
"class A {",
|
||||
@@ -94,7 +94,7 @@ final class FluxFlatMapUsageTest {
|
||||
refactoringTestHelper
|
||||
.setFixChooser(FixChoosers.SECOND)
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"",
|
||||
"class A {",
|
||||
@@ -106,7 +106,7 @@ final class FluxFlatMapUsageTest {
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"",
|
||||
"class A {",
|
||||
|
||||
@@ -311,7 +311,7 @@ final class FormatStringConcatenationTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import static com.google.common.base.Preconditions.checkArgument;",
|
||||
"import static org.assertj.core.api.Assertions.assertThat;",
|
||||
"",
|
||||
@@ -360,7 +360,7 @@ final class FormatStringConcatenationTest {
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import static com.google.common.base.Preconditions.checkArgument;",
|
||||
"import static org.assertj.core.api.Assertions.assertThat;",
|
||||
"",
|
||||
|
||||
@@ -308,7 +308,7 @@ final class JUnitMethodDeclarationTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import org.junit.jupiter.api.AfterAll;",
|
||||
@@ -363,7 +363,7 @@ final class JUnitMethodDeclarationTest {
|
||||
" private void testClass() {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import org.junit.jupiter.api.AfterAll;",
|
||||
|
||||
@@ -167,7 +167,7 @@ final class LexicographicalAnnotationAttributeListingTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import static java.math.RoundingMode.DOWN;",
|
||||
"import static java.math.RoundingMode.UP;",
|
||||
"",
|
||||
@@ -204,7 +204,7 @@ final class LexicographicalAnnotationAttributeListingTest {
|
||||
" A unsortedInnderAnns();",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import static java.math.RoundingMode.DOWN;",
|
||||
"import static java.math.RoundingMode.UP;",
|
||||
"",
|
||||
|
||||
@@ -114,7 +114,7 @@ final class LexicographicalAnnotationListingTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import java.lang.annotation.Repeatable;",
|
||||
"",
|
||||
"interface A {",
|
||||
@@ -176,7 +176,7 @@ final class LexicographicalAnnotationListingTest {
|
||||
" A unsortedRepeatableAnnotation();",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import java.lang.annotation.Repeatable;",
|
||||
"",
|
||||
"interface A {",
|
||||
|
||||
@@ -325,7 +325,7 @@ final class MethodReferenceUsageTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import static java.util.Collections.emptyList;",
|
||||
"",
|
||||
"import java.util.Collections;",
|
||||
@@ -378,7 +378,7 @@ final class MethodReferenceUsageTest {
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import static java.util.Collections.emptyList;",
|
||||
"",
|
||||
"import java.util.Collections;",
|
||||
|
||||
@@ -57,7 +57,7 @@ final class MockitoStubbingTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import static org.mockito.ArgumentMatchers.eq;",
|
||||
"import static org.mockito.Mockito.doAnswer;",
|
||||
"import static org.mockito.Mockito.mock;",
|
||||
@@ -80,7 +80,7 @@ final class MockitoStubbingTest {
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import static org.mockito.ArgumentMatchers.eq;",
|
||||
"import static org.mockito.Mockito.doAnswer;",
|
||||
"import static org.mockito.Mockito.mock;",
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class NonEmptyMonoTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(NonEmptyMono.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(NonEmptyMono.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static com.google.common.collect.ImmutableList.toImmutableList;",
|
||||
"import static java.util.function.Function.identity;",
|
||||
"",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableMap;",
|
||||
"import java.util.ArrayList;",
|
||||
"import java.util.HashMap;",
|
||||
"import java.util.List;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() {",
|
||||
" Mono.just(1).defaultIfEmpty(2);",
|
||||
" Mono.just(1).single();",
|
||||
" Mono.just(1).switchIfEmpty(Mono.just(2));",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).all(x -> true).defaultIfEmpty(true);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).any(x -> true).single();",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).collect(toImmutableList()).switchIfEmpty(Mono.just(ImmutableList.of()));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).collect(ArrayList::new, List::add).defaultIfEmpty(new ArrayList<>());",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).collectList().single();",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).collectMap(identity()).switchIfEmpty(Mono.just(ImmutableMap.of()));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).collectMap(identity(), identity()).defaultIfEmpty(ImmutableMap.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).collectMap(identity(), identity(), HashMap::new).single();",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).collectMultimap(identity()).switchIfEmpty(Mono.just(ImmutableMap.of()));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).collectMultimap(identity(), identity()).defaultIfEmpty(ImmutableMap.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).collectMultimap(identity(), identity(), HashMap::new).single();",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).collectSortedList().defaultIfEmpty(ImmutableList.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).collectSortedList((o1, o2) -> 0).single();",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).count().switchIfEmpty(Mono.just(2L));",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).elementAt(0).defaultIfEmpty(1);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).elementAt(0, 2).single();",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).hasElement(2).switchIfEmpty(Mono.just(true));",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).hasElements().defaultIfEmpty(true);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).last().single();",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).last(2).switchIfEmpty(Mono.just(3));",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).reduceWith(() -> 0, Integer::sum).defaultIfEmpty(2);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).single().single();",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).single(2).switchIfEmpty(Mono.just(3));",
|
||||
"",
|
||||
" Flux.just(1).reduce(Integer::sum).defaultIfEmpty(2);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux.just(1).reduce(2, Integer::sum).single();",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Mono.just(1).defaultIfEmpty(1).switchIfEmpty(Mono.just(2));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Mono.just(1).hasElement().defaultIfEmpty(true);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Mono.just(1).single().single();",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import static com.google.common.collect.ImmutableList.toImmutableList;",
|
||||
"",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() {",
|
||||
" Flux.just(1).collect(toImmutableList()).single();",
|
||||
" Flux.just(1).collect(toImmutableList()).defaultIfEmpty(ImmutableList.of());",
|
||||
" Flux.just(1).collect(toImmutableList()).switchIfEmpty(Mono.just(ImmutableList.of()));",
|
||||
"",
|
||||
" Mono.just(2).hasElement().single();",
|
||||
" Mono.just(2).hasElement().defaultIfEmpty(true);",
|
||||
" Mono.just(2).hasElement().switchIfEmpty(Mono.just(true));",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import static com.google.common.collect.ImmutableList.toImmutableList;",
|
||||
"",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() {",
|
||||
" Flux.just(1).collect(toImmutableList());",
|
||||
" Flux.just(1).collect(toImmutableList());",
|
||||
" Flux.just(1).collect(toImmutableList());",
|
||||
"",
|
||||
" Mono.just(2).hasElement();",
|
||||
" Mono.just(2).hasElement();",
|
||||
" Mono.just(2).hasElement();",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -499,7 +499,7 @@ final class PrimitiveComparisonTest {
|
||||
void replacementWithPrimitiveVariants() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
@@ -536,7 +536,7 @@ final class PrimitiveComparisonTest {
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
@@ -579,7 +579,7 @@ final class PrimitiveComparisonTest {
|
||||
void replacementWithBoxedVariants() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
@@ -609,7 +609,7 @@ final class PrimitiveComparisonTest {
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
@@ -645,7 +645,7 @@ final class PrimitiveComparisonTest {
|
||||
void replacementWithPrimitiveVariantsUsingStaticImports() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import static java.util.Comparator.comparing;",
|
||||
"",
|
||||
"import java.util.Comparator;",
|
||||
@@ -660,7 +660,7 @@ final class PrimitiveComparisonTest {
|
||||
" Comparator<A> dCmp = comparing(o -> 0.0);",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import static java.util.Comparator.comparing;",
|
||||
"import static java.util.Comparator.comparingDouble;",
|
||||
"import static java.util.Comparator.comparingInt;",
|
||||
@@ -684,7 +684,7 @@ final class PrimitiveComparisonTest {
|
||||
void replacementWithBoxedVariantsUsingStaticImports() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import static java.util.Comparator.comparingDouble;",
|
||||
"import static java.util.Comparator.comparingInt;",
|
||||
"import static java.util.Comparator.comparingLong;",
|
||||
@@ -701,7 +701,7 @@ final class PrimitiveComparisonTest {
|
||||
" Comparator<A> dCmp = comparingDouble(o -> Double.valueOf(0));",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import static java.util.Comparator.comparing;",
|
||||
"import static java.util.Comparator.comparingDouble;",
|
||||
"import static java.util.Comparator.comparingInt;",
|
||||
@@ -725,7 +725,7 @@ final class PrimitiveComparisonTest {
|
||||
void replacementWithPrimitiveVariantsInComplexSyntacticalContext() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
@@ -738,7 +738,7 @@ final class PrimitiveComparisonTest {
|
||||
" Comparator<A> dCmp = Comparator.<A, A>comparing(o -> o).thenComparing(o -> 0.0);",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
@@ -757,7 +757,7 @@ final class PrimitiveComparisonTest {
|
||||
void replacementWithBoxedVariantsInComplexSyntacticalContext() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
@@ -775,7 +775,7 @@ final class PrimitiveComparisonTest {
|
||||
" Comparator.<A, A>comparing(o -> o).thenComparingDouble(o -> Double.valueOf(0));",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
|
||||
@@ -69,9 +69,14 @@ final class RedundantStringConversionTest {
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s += String.valueOf(i);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s += String.valueOf(0);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s += String.valueOf((String) null);",
|
||||
" s += String.valueOf(null);",
|
||||
" s += String.valueOf(new char[0]);",
|
||||
" s += String.valueOf(new char[0], 0, 0);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s += Boolean.toString(false);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s += Byte.toString((byte) 0);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
@@ -121,9 +126,12 @@ final class RedundantStringConversionTest {
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s + String.valueOf(i),",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s + String.valueOf(0),",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s + String.valueOf((String) null),",
|
||||
" s + String.valueOf(null),",
|
||||
" s + String.valueOf(new char[0]),",
|
||||
" s + String.valueOf(new char[0], 0, 0),",
|
||||
" //",
|
||||
" 42 + this.toString(),",
|
||||
" 42 + super.toString(),",
|
||||
@@ -134,6 +142,7 @@ final class RedundantStringConversionTest {
|
||||
" 42 + String.valueOf((String) null),",
|
||||
" 42 + String.valueOf(null),",
|
||||
" 42 + String.valueOf(new char[0]),",
|
||||
" 42 + String.valueOf(new char[0], 0, 0),",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" this.toString() + s,",
|
||||
@@ -144,19 +153,24 @@ final class RedundantStringConversionTest {
|
||||
" // BUG: Diagnostic contains:",
|
||||
" String.valueOf(i) + s,",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" String.valueOf(0) + s,",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" String.valueOf((String) null) + s,",
|
||||
" String.valueOf(null) + s,",
|
||||
" String.valueOf(new char[0]) + s,",
|
||||
" String.valueOf(new char[0], 0, 0) + s,",
|
||||
" //",
|
||||
" this.toString() + 42,",
|
||||
" super.toString() + 42,",
|
||||
" i.toString() + 42,",
|
||||
" i.toString(16) + 42,",
|
||||
" String.valueOf(i) + 42,",
|
||||
" String.valueOf(0) + 42,",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" String.valueOf((String) null) + 42,",
|
||||
" String.valueOf(null) + 42,",
|
||||
" String.valueOf(new char[0]) + 42,",
|
||||
" String.valueOf(new char[0], 0, 0) + 42,",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" this.toString() + this.toString(),",
|
||||
@@ -167,9 +181,12 @@ final class RedundantStringConversionTest {
|
||||
" // BUG: Diagnostic contains:",
|
||||
" String.valueOf(i) + String.valueOf(i),",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" String.valueOf(0) + String.valueOf(0),",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" String.valueOf((String) null) + String.valueOf((String) null),",
|
||||
" String.valueOf(null) + String.valueOf(null),",
|
||||
" String.valueOf(new char[0]) + String.valueOf(new char[0]),",
|
||||
" String.valueOf(new char[0], 0, 0) + String.valueOf(new char[0], 0, 0),",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
@@ -204,9 +221,12 @@ final class RedundantStringConversionTest {
|
||||
" // BUG: Diagnostic contains:",
|
||||
" sb.append(String.valueOf(i));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" sb.append(String.valueOf(0));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" sb.append(String.valueOf((String) null));",
|
||||
" sb.append(String.valueOf(null));",
|
||||
" sb.append(String.valueOf(new char[0]));",
|
||||
" sb.append(String.valueOf(new char[0], 0, 0));",
|
||||
" sb.append(s);",
|
||||
" sb.append(\"constant\");",
|
||||
"",
|
||||
@@ -218,9 +238,12 @@ final class RedundantStringConversionTest {
|
||||
" // BUG: Diagnostic contains:",
|
||||
" sb.insert(0, String.valueOf(i));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" sb.insert(0, String.valueOf(0));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" sb.insert(0, String.valueOf((String) null));",
|
||||
" sb.insert(0, String.valueOf(null));",
|
||||
" sb.insert(0, String.valueOf(new char[0]));",
|
||||
" sb.insert(0, String.valueOf(new char[0], 0, 0));",
|
||||
" sb.insert(0, s);",
|
||||
" sb.insert(0, \"constant\");",
|
||||
"",
|
||||
@@ -434,6 +457,8 @@ final class RedundantStringConversionTest {
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s + String.valueOf(b),",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s + Boolean.toString(false),",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s + Byte.toString((byte) 0),",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s + Character.toString((char) 0),",
|
||||
@@ -486,7 +511,7 @@ final class RedundantStringConversionTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"class A {",
|
||||
" private final Object o = new Object();",
|
||||
" private final String s = o.toString();",
|
||||
@@ -500,7 +525,7 @@ final class RedundantStringConversionTest {
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"class A {",
|
||||
" private final Object o = new Object();",
|
||||
" private final String s = o.toString();",
|
||||
|
||||
@@ -40,7 +40,7 @@ final class RefasterAnyOfUsageTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import com.google.errorprone.refaster.Refaster;",
|
||||
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
|
||||
"",
|
||||
@@ -52,7 +52,7 @@ final class RefasterAnyOfUsageTest {
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import com.google.errorprone.refaster.Refaster;",
|
||||
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
|
||||
"",
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class RefasterTemplateModifiersTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(RefasterTemplateModifiers.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(RefasterTemplateModifiers.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
|
||||
"",
|
||||
"final class A {",
|
||||
" @BeforeTemplate",
|
||||
" String before(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
"",
|
||||
" String nonRefasterMethod(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
"",
|
||||
" static final class Inner {",
|
||||
" @BeforeTemplate",
|
||||
" String before(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
" }",
|
||||
"}")
|
||||
.addSourceLines(
|
||||
"B.java",
|
||||
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
|
||||
"import com.google.errorprone.refaster.annotation.Placeholder;",
|
||||
"",
|
||||
"abstract class B<I, O> {",
|
||||
" @Placeholder",
|
||||
" abstract O someFunction(I input);",
|
||||
"",
|
||||
" @BeforeTemplate",
|
||||
" String before(I input) {",
|
||||
" return String.valueOf(someFunction(input));",
|
||||
" }",
|
||||
"",
|
||||
" abstract static class Inner<I, O> {",
|
||||
" @Placeholder",
|
||||
" abstract O someFunction(I input);",
|
||||
"",
|
||||
" @BeforeTemplate",
|
||||
" String before(I input) {",
|
||||
" return String.valueOf(someFunction(input));",
|
||||
" }",
|
||||
" }",
|
||||
"}")
|
||||
.addSourceLines(
|
||||
"C.java",
|
||||
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
|
||||
"",
|
||||
"// BUG: Diagnostic contains:",
|
||||
"class C {",
|
||||
" @BeforeTemplate",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" final String beforeFinal(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
"",
|
||||
" @BeforeTemplate",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" private String beforePrivate(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
"",
|
||||
" @BeforeTemplate",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public String beforePublic(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
"",
|
||||
" @BeforeTemplate",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" static String beforeStatic(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
"",
|
||||
" @BeforeTemplate",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" synchronized String beforeSynchronized(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" abstract static class AbstractInner {",
|
||||
" @BeforeTemplate",
|
||||
" String before(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" static class NonFinalInner {",
|
||||
" @BeforeTemplate",
|
||||
" String before(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" final class NonStaticInner {",
|
||||
" @BeforeTemplate",
|
||||
" String before(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
" }",
|
||||
"}")
|
||||
.addSourceLines(
|
||||
"D.java",
|
||||
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
|
||||
"",
|
||||
"// BUG: Diagnostic contains:",
|
||||
"abstract class D {",
|
||||
" @BeforeTemplate",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" protected String beforeProtected(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
|
||||
"",
|
||||
"class A {",
|
||||
" @BeforeTemplate",
|
||||
" private static String before(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
|
||||
"",
|
||||
"final class A {",
|
||||
" @BeforeTemplate",
|
||||
" String before(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
"}")
|
||||
.addInputLines(
|
||||
"B.java",
|
||||
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
|
||||
"import com.google.errorprone.refaster.annotation.Placeholder;",
|
||||
"",
|
||||
"final class B {",
|
||||
" abstract class WithoutPlaceholder {",
|
||||
" @BeforeTemplate",
|
||||
" protected synchronized String before(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" abstract class WithPlaceholder<I, O> {",
|
||||
" @Placeholder",
|
||||
" public abstract O someFunction(I input);",
|
||||
"",
|
||||
" @BeforeTemplate",
|
||||
" public final String before(I input) {",
|
||||
" return String.valueOf(someFunction(input));",
|
||||
" }",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"B.java",
|
||||
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
|
||||
"import com.google.errorprone.refaster.annotation.Placeholder;",
|
||||
"",
|
||||
"final class B {",
|
||||
" static final class WithoutPlaceholder {",
|
||||
" @BeforeTemplate",
|
||||
" String before(String str) {",
|
||||
" return str;",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" abstract static class WithPlaceholder<I, O> {",
|
||||
" @Placeholder",
|
||||
" abstract O someFunction(I input);",
|
||||
"",
|
||||
" @BeforeTemplate",
|
||||
" String before(I input) {",
|
||||
" return String.valueOf(someFunction(input));",
|
||||
" }",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ final class RequestMappingAnnotationTest {
|
||||
"import org.springframework.web.bind.annotation.RequestHeader;",
|
||||
"import org.springframework.web.bind.annotation.RequestMapping;",
|
||||
"import org.springframework.web.bind.annotation.RequestParam;",
|
||||
"import org.springframework.web.bind.annotation.RequestPart;",
|
||||
"import org.springframework.web.context.request.NativeWebRequest;",
|
||||
"import org.springframework.web.context.request.WebRequest;",
|
||||
"import org.springframework.web.server.ServerWebExchange;",
|
||||
@@ -60,6 +61,9 @@ final class RequestMappingAnnotationTest {
|
||||
" A properRequestParam(@RequestParam String param);",
|
||||
"",
|
||||
" @RequestMapping",
|
||||
" A properRequestPart(@RequestPart String part);",
|
||||
"",
|
||||
" @RequestMapping",
|
||||
" A properInputStream(InputStream input);",
|
||||
"",
|
||||
" @RequestMapping",
|
||||
|
||||
@@ -47,7 +47,7 @@ final class ScheduledTransactionTraceTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import com.newrelic.api.agent.Trace;",
|
||||
"import org.springframework.scheduling.annotation.Scheduled;",
|
||||
"",
|
||||
@@ -68,7 +68,7 @@ final class ScheduledTransactionTraceTest {
|
||||
" void scheduledButImproperlyTraced3() {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import com.newrelic.api.agent.Trace;",
|
||||
"import org.springframework.scheduling.annotation.Scheduled;",
|
||||
"",
|
||||
|
||||
@@ -98,7 +98,7 @@ final class Slf4JLogStatementTest {
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"A.java",
|
||||
"import org.slf4j.Logger;",
|
||||
"import org.slf4j.LoggerFactory;",
|
||||
"import org.slf4j.Marker;",
|
||||
@@ -121,7 +121,7 @@ final class Slf4JLogStatementTest {
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"A.java",
|
||||
"import org.slf4j.Logger;",
|
||||
"import org.slf4j.LoggerFactory;",
|
||||
"import org.slf4j.Marker;",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user