mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 15:49:33 +00:00
Compare commits
41 Commits
sschroever
...
v0.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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:
|
||||
- "*"
|
||||
6
.github/workflows/build.yaml
vendored
6
.github/workflows/build.yaml
vendored
@@ -4,6 +4,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -26,8 +28,8 @@ jobs:
|
||||
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
|
||||
|
||||
@@ -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 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) 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.
|
||||
224
README.md
Normal file
224
README.md
Normal file
@@ -0,0 +1,224 @@
|
||||
<div align="center">
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="logo-dark.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="logo.svg">
|
||||
<img alt="Error Prone Support logo" src="logo.svg" width="50%">
|
||||
</picture>
|
||||
|
||||
# Error Prone Support
|
||||
|
||||
Error Prone Support is a Picnic-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 gotchas.
|
||||
|
||||
> 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) • [Building](#-building) •
|
||||
[How it works](#-how-it-works) • [Contributing](#%EF%B8%8F-contributing)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## ⚡ 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:[14,35] [IdentityConversion] This method invocation appears redundant; remove it or suppress this warning and add a comment explaining its purpose
|
||||
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].
|
||||
|
||||
## 👷 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` 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]: error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/
|
||||
[bug-checks-identity-conversion]: error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IdentityConversion.java
|
||||
[contributing]: 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]: 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
|
||||
[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]: error-prone-contrib/src/main/java/tech/picnic/errorprone/refastertemplates/BigDecimalTemplates.java
|
||||
[refaster-templates]: error-prone-contrib/src/main/java/tech/picnic/errorprone/refastertemplates/
|
||||
@@ -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:
|
||||
|
||||
@@ -118,12 +69,13 @@ The following is a list of checks we'd like to see implemented:
|
||||
code and Javadoc `@link` references.
|
||||
- A check which 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
|
||||
%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.)
|
||||
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 %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.
|
||||
For example as argument to `StringBuilder.append` and in string
|
||||
concatenations.
|
||||
@@ -168,11 +120,11 @@ The following is a list of checks we'd like to see implemented:
|
||||
- A check which flags imports from other test classes.
|
||||
- A Guava-specific check which 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
|
||||
IllegalArgumentException(); }` and variants to an equivalent `checkArgument`
|
||||
statement. Idem for other exception types.
|
||||
- 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 IllegalArgumentException(); }` and variants to
|
||||
an equivalent `checkArgument` statement. Idem for other exception types.
|
||||
- A Guava-specific check which replaces simple anonymous `CacheLoader` subclass
|
||||
declarations with `CacheLoader.from(someLambda)`.
|
||||
- A Spring-specific check which enforces that methods with the `@Scheduled`
|
||||
@@ -247,6 +199,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.
|
||||
@@ -259,16 +212,16 @@ Refaster's expressiveness:
|
||||
- Some Refaster refactorings (e.g. when dealing with lazy evaluation) are valid
|
||||
only when some free parameter is a constant, variable reference or some other
|
||||
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.
|
||||
rewriting `optional1.map(Optional::of).orElse(optional2)` to
|
||||
`optional1.or(() -> optional2)` is not behavior preserving if evaluation of
|
||||
`optional2` has 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
|
||||
this.
|
||||
- Generalize `@Placeholder` support such that rules can reference e.g. "any
|
||||
concrete unary method". This would allow refactorings such as
|
||||
`Mono.just(constant).flatmap(this::someFun)` -> `Mono.defer(() ->
|
||||
someFun(constant))`.
|
||||
`Mono.just(constant).flatmap(this::someFun)` ->
|
||||
`Mono.defer(() -> someFun(constant))`.
|
||||
- Sometimes a Refaster refactoring can cause the resulting code not to compile
|
||||
due to a lack of generic type information. Identify and resolve such
|
||||
occurrences. For example, an `@AfterTemplate` may require the insertion of a
|
||||
@@ -327,16 +280,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.0</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>
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
|
||||
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} which 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",
|
||||
linkType = NONE,
|
||||
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,10 +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.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;
|
||||
@@ -13,7 +16,9 @@ import static com.google.errorprone.matchers.method.MethodMatchers.instanceMetho
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
|
||||
|
||||
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 +29,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,6 +47,7 @@ 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;
|
||||
|
||||
@@ -69,49 +76,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 +115,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 +137,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 +167,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);
|
||||
@@ -276,15 +257,15 @@ public final class RedundantStringConversion extends BugChecker
|
||||
|
||||
// XXX: Write another check which 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 +319,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 +368,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
|
||||
|
||||
@@ -66,7 +66,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"),
|
||||
|
||||
@@ -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.util.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,13 +41,11 @@ final class EqualityTemplates {
|
||||
// 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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.util.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)),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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),",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -19,6 +19,7 @@ final class RefasterTemplatesTest {
|
||||
AssertJBooleanTemplates.class,
|
||||
AssertJByteTemplates.class,
|
||||
AssertJCharSequenceTemplates.class,
|
||||
AssertJComparableTemplates.class,
|
||||
AssertJDoubleTemplates.class,
|
||||
AssertJEnumerableTemplates.class,
|
||||
AssertJFloatTemplates.class,
|
||||
@@ -28,6 +29,7 @@ final class RefasterTemplatesTest {
|
||||
AssertJMapTemplates.class,
|
||||
AssertJObjectTemplates.class,
|
||||
AssertJOptionalTemplates.class,
|
||||
AssertJPrimitiveTemplates.class,
|
||||
AssertJShortTemplates.class,
|
||||
AssertJStringTemplates.class,
|
||||
AssertJThrowingCallableTemplates.class,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package tech.picnic.errorprone.refastertemplates;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import org.assertj.core.api.AbstractComparableAssert;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterTemplateTestCase;
|
||||
|
||||
final class AssertJComparableTemplatesTest implements RefasterTemplateTestCase {
|
||||
AbstractComparableAssert<?, ?> testAssertThatIsEqualByComparingTo() {
|
||||
return assertThat(BigDecimal.ZERO.compareTo(BigDecimal.ONE)).isEqualTo(0);
|
||||
}
|
||||
|
||||
AbstractComparableAssert<?, ?> testAssertThatIsNotEqualByComparingTo() {
|
||||
return assertThat(BigDecimal.ZERO.compareTo(BigDecimal.ONE)).isNotEqualTo(0);
|
||||
}
|
||||
|
||||
AbstractComparableAssert<?, ?> testAssertThatIsLessThan() {
|
||||
return assertThat(BigDecimal.ZERO.compareTo(BigDecimal.ONE)).isNegative();
|
||||
}
|
||||
|
||||
AbstractComparableAssert<?, ?> testAssertThatIsLessThanOrEqualTo() {
|
||||
return assertThat(BigDecimal.ZERO.compareTo(BigDecimal.ONE)).isNotPositive();
|
||||
}
|
||||
|
||||
AbstractComparableAssert<?, ?> testAssertThatIsGreaterThan() {
|
||||
return assertThat(BigDecimal.ZERO.compareTo(BigDecimal.ONE)).isPositive();
|
||||
}
|
||||
|
||||
AbstractComparableAssert<?, ?> testAssertThatIsGreaterThanOrEqualTo() {
|
||||
return assertThat(BigDecimal.ZERO.compareTo(BigDecimal.ONE)).isNotNegative();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package tech.picnic.errorprone.refastertemplates;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import org.assertj.core.api.AbstractComparableAssert;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterTemplateTestCase;
|
||||
|
||||
final class AssertJComparableTemplatesTest implements RefasterTemplateTestCase {
|
||||
AbstractComparableAssert<?, ?> testAssertThatIsEqualByComparingTo() {
|
||||
return assertThat(BigDecimal.ZERO).isEqualByComparingTo(BigDecimal.ONE);
|
||||
}
|
||||
|
||||
AbstractComparableAssert<?, ?> testAssertThatIsNotEqualByComparingTo() {
|
||||
return assertThat(BigDecimal.ZERO).isNotEqualByComparingTo(BigDecimal.ONE);
|
||||
}
|
||||
|
||||
AbstractComparableAssert<?, ?> testAssertThatIsLessThan() {
|
||||
return assertThat(BigDecimal.ZERO).isLessThan(BigDecimal.ONE);
|
||||
}
|
||||
|
||||
AbstractComparableAssert<?, ?> testAssertThatIsLessThanOrEqualTo() {
|
||||
return assertThat(BigDecimal.ZERO).isLessThanOrEqualTo(BigDecimal.ONE);
|
||||
}
|
||||
|
||||
AbstractComparableAssert<?, ?> testAssertThatIsGreaterThan() {
|
||||
return assertThat(BigDecimal.ZERO).isGreaterThan(BigDecimal.ONE);
|
||||
}
|
||||
|
||||
AbstractComparableAssert<?, ?> testAssertThatIsGreaterThanOrEqualTo() {
|
||||
return assertThat(BigDecimal.ZERO).isGreaterThanOrEqualTo(BigDecimal.ONE);
|
||||
}
|
||||
}
|
||||
@@ -81,23 +81,27 @@ final class AssertJNumberTemplatesTest implements RefasterTemplateTestCase {
|
||||
return ImmutableSet.of(
|
||||
assertThat((byte) 1 % 2).isEqualTo(1),
|
||||
assertThat(Byte.valueOf((byte) 1) % 2).isEqualTo(1),
|
||||
assertThat((char) 1 % 2).isEqualTo(1),
|
||||
assertThat(Character.valueOf((char) 1) % 2).isEqualTo(1),
|
||||
assertThat((short) 1 % 2).isEqualTo(1),
|
||||
assertThat(Short.valueOf((short) 1) % 2).isEqualTo(1),
|
||||
assertThat(1 % 2).isEqualTo(1),
|
||||
assertThat(Integer.valueOf(1) % 2).isEqualTo(1),
|
||||
assertThat(1L % 2).isEqualTo(1),
|
||||
assertThat(Long.valueOf(1) % 2).isEqualTo(1),
|
||||
assertThat((short) 1 % 2).isEqualTo(1),
|
||||
assertThat(Short.valueOf((short) 1) % 2).isEqualTo(1));
|
||||
assertThat(Long.valueOf(1) % 2).isEqualTo(1));
|
||||
}
|
||||
|
||||
ImmutableSet<NumberAssert<?, ?>> testAssertThatIsEven() {
|
||||
return ImmutableSet.of(
|
||||
assertThat((byte) 1 % 2).isEqualTo(0),
|
||||
assertThat(Byte.valueOf((byte) 1) % 2).isEqualTo(0),
|
||||
assertThat((char) 1 % 2).isEqualTo(0),
|
||||
assertThat(Character.valueOf((char) 1) % 2).isEqualTo(0),
|
||||
assertThat((short) 1 % 2).isEqualTo(0),
|
||||
assertThat(Short.valueOf((short) 1) % 2).isEqualTo(0),
|
||||
assertThat(1 % 2).isEqualTo(0),
|
||||
assertThat(Integer.valueOf(1) % 2).isEqualTo(0),
|
||||
assertThat(1L % 2).isEqualTo(0),
|
||||
assertThat(Long.valueOf(1) % 2).isEqualTo(0),
|
||||
assertThat((short) 1 % 2).isEqualTo(0),
|
||||
assertThat(Short.valueOf((short) 1) % 2).isEqualTo(0));
|
||||
assertThat(Long.valueOf(1) % 2).isEqualTo(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,23 +81,27 @@ final class AssertJNumberTemplatesTest implements RefasterTemplateTestCase {
|
||||
return ImmutableSet.of(
|
||||
assertThat((byte) 1).isOdd(),
|
||||
assertThat(Byte.valueOf((byte) 1)).isOdd(),
|
||||
assertThat((char) 1 % 2).isEqualTo(1),
|
||||
assertThat(Character.valueOf((char) 1) % 2).isEqualTo(1),
|
||||
assertThat((short) 1).isOdd(),
|
||||
assertThat(Short.valueOf((short) 1)).isOdd(),
|
||||
assertThat(1).isOdd(),
|
||||
assertThat(Integer.valueOf(1)).isOdd(),
|
||||
assertThat(1L).isOdd(),
|
||||
assertThat(Long.valueOf(1)).isOdd(),
|
||||
assertThat((short) 1).isOdd(),
|
||||
assertThat(Short.valueOf((short) 1)).isOdd());
|
||||
assertThat(Long.valueOf(1)).isOdd());
|
||||
}
|
||||
|
||||
ImmutableSet<NumberAssert<?, ?>> testAssertThatIsEven() {
|
||||
return ImmutableSet.of(
|
||||
assertThat((byte) 1).isEven(),
|
||||
assertThat(Byte.valueOf((byte) 1)).isEven(),
|
||||
assertThat((char) 1 % 2).isEqualTo(0),
|
||||
assertThat(Character.valueOf((char) 1) % 2).isEqualTo(0),
|
||||
assertThat((short) 1).isEven(),
|
||||
assertThat(Short.valueOf((short) 1)).isEven(),
|
||||
assertThat(1).isEven(),
|
||||
assertThat(Integer.valueOf(1)).isEven(),
|
||||
assertThat(1L).isEven(),
|
||||
assertThat(Long.valueOf(1)).isEven(),
|
||||
assertThat((short) 1).isEven(),
|
||||
assertThat(Short.valueOf((short) 1)).isEven());
|
||||
assertThat(Long.valueOf(1)).isEven());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package tech.picnic.errorprone.refastertemplates;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterTemplateTestCase;
|
||||
|
||||
final class AssertJPrimitiveTemplatesTest implements RefasterTemplateTestCase {
|
||||
@SuppressWarnings("SimplifyBooleanExpression")
|
||||
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIsEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
assertThat(true == false).isTrue(),
|
||||
assertThat(true != false).isFalse(),
|
||||
assertThat((byte) 1 == (byte) 2).isTrue(),
|
||||
assertThat((byte) 1 != (byte) 2).isFalse(),
|
||||
assertThat((char) 1 == (char) 2).isTrue(),
|
||||
assertThat((char) 1 != (char) 2).isFalse(),
|
||||
assertThat((short) 1 == (short) 2).isTrue(),
|
||||
assertThat((short) 1 != (short) 2).isFalse(),
|
||||
assertThat(1 == 2).isTrue(),
|
||||
assertThat(1 != 2).isFalse(),
|
||||
assertThat(1L == 2L).isTrue(),
|
||||
assertThat(1L != 2L).isFalse(),
|
||||
assertThat(1F == 2F).isTrue(),
|
||||
assertThat(1F != 2F).isFalse(),
|
||||
assertThat(1.0 == 2.0).isTrue(),
|
||||
assertThat(1.0 != 2.0).isFalse());
|
||||
}
|
||||
|
||||
@SuppressWarnings("SimplifyBooleanExpression")
|
||||
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIsNotEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
assertThat(true != false).isTrue(),
|
||||
assertThat(true == false).isFalse(),
|
||||
assertThat((byte) 1 != (byte) 2).isTrue(),
|
||||
assertThat((byte) 1 == (byte) 2).isFalse(),
|
||||
assertThat((char) 1 != (char) 2).isTrue(),
|
||||
assertThat((char) 1 == (char) 2).isFalse(),
|
||||
assertThat((short) 1 != (short) 2).isTrue(),
|
||||
assertThat((short) 1 == (short) 2).isFalse(),
|
||||
assertThat(1 != 2).isTrue(),
|
||||
assertThat(1 == 2).isFalse(),
|
||||
assertThat(1L != 2L).isTrue(),
|
||||
assertThat(1L == 2L).isFalse(),
|
||||
assertThat(1F != 2F).isTrue(),
|
||||
assertThat(1F == 2F).isFalse(),
|
||||
assertThat(1.0 != 2.0).isTrue(),
|
||||
assertThat(1.0 == 2.0).isFalse());
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIsLessThan() {
|
||||
return ImmutableSet.of(
|
||||
assertThat((byte) 1 < (byte) 2).isTrue(),
|
||||
assertThat((byte) 1 >= (byte) 2).isFalse(),
|
||||
assertThat((char) 1 < (char) 2).isTrue(),
|
||||
assertThat((char) 1 >= (char) 2).isFalse(),
|
||||
assertThat((short) 1 < (short) 2).isTrue(),
|
||||
assertThat((short) 1 >= (short) 2).isFalse(),
|
||||
assertThat(1 < 2).isTrue(),
|
||||
assertThat(1 >= 2).isFalse(),
|
||||
assertThat(1L < 2L).isTrue(),
|
||||
assertThat(1L >= 2L).isFalse(),
|
||||
assertThat(1F < 2F).isTrue(),
|
||||
assertThat(1F >= 2F).isFalse(),
|
||||
assertThat(1.0 < 2.0).isTrue(),
|
||||
assertThat(1.0 >= 2.0).isFalse());
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIsLessThanOrEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
assertThat((byte) 1 <= (byte) 2).isTrue(),
|
||||
assertThat((byte) 1 > (byte) 2).isFalse(),
|
||||
assertThat((char) 1 <= (char) 2).isTrue(),
|
||||
assertThat((char) 1 > (char) 2).isFalse(),
|
||||
assertThat((short) 1 <= (short) 2).isTrue(),
|
||||
assertThat((short) 1 > (short) 2).isFalse(),
|
||||
assertThat(1 <= 2).isTrue(),
|
||||
assertThat(1 > 2).isFalse(),
|
||||
assertThat(1L <= 2L).isTrue(),
|
||||
assertThat(1L > 2L).isFalse(),
|
||||
assertThat(1F <= 2F).isTrue(),
|
||||
assertThat(1F > 2F).isFalse(),
|
||||
assertThat(1.0 <= 2.0).isTrue(),
|
||||
assertThat(1.0 > 2.0).isFalse());
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIsGreaterThan() {
|
||||
return ImmutableSet.of(
|
||||
assertThat((byte) 1 > (byte) 2).isTrue(),
|
||||
assertThat((byte) 1 <= (byte) 2).isFalse(),
|
||||
assertThat((char) 1 > (char) 2).isTrue(),
|
||||
assertThat((char) 1 <= (char) 2).isFalse(),
|
||||
assertThat((short) 1 > (short) 2).isTrue(),
|
||||
assertThat((short) 1 <= (short) 2).isFalse(),
|
||||
assertThat(1 > 2).isTrue(),
|
||||
assertThat(1 <= 2).isFalse(),
|
||||
assertThat(1L > 2L).isTrue(),
|
||||
assertThat(1L <= 2L).isFalse(),
|
||||
assertThat(1F > 2F).isTrue(),
|
||||
assertThat(1F <= 2F).isFalse(),
|
||||
assertThat(1.0 > 2.0).isTrue(),
|
||||
assertThat(1.0 <= 2.0).isFalse());
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIsGreaterThanOrEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
assertThat((byte) 1 >= (byte) 2).isTrue(),
|
||||
assertThat((byte) 1 < (byte) 2).isFalse(),
|
||||
assertThat((char) 1 >= (char) 2).isTrue(),
|
||||
assertThat((char) 1 < (char) 2).isFalse(),
|
||||
assertThat((short) 1 >= (short) 2).isTrue(),
|
||||
assertThat((short) 1 < (short) 2).isFalse(),
|
||||
assertThat(1 >= 2).isTrue(),
|
||||
assertThat(1 < 2).isFalse(),
|
||||
assertThat(1L >= 2L).isTrue(),
|
||||
assertThat(1L < 2L).isFalse(),
|
||||
assertThat(1F >= 2F).isTrue(),
|
||||
assertThat(1F < 2F).isFalse(),
|
||||
assertThat(1.0 >= 2.0).isTrue(),
|
||||
assertThat(1.0 < 2.0).isFalse());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package tech.picnic.errorprone.refastertemplates;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterTemplateTestCase;
|
||||
|
||||
final class AssertJPrimitiveTemplatesTest implements RefasterTemplateTestCase {
|
||||
@SuppressWarnings("SimplifyBooleanExpression")
|
||||
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIsEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
assertThat(true).isEqualTo(false),
|
||||
assertThat(true).isEqualTo(false),
|
||||
assertThat((byte) 1).isEqualTo((byte) 2),
|
||||
assertThat((byte) 1).isEqualTo((byte) 2),
|
||||
assertThat((char) 1).isEqualTo((char) 2),
|
||||
assertThat((char) 1).isEqualTo((char) 2),
|
||||
assertThat((short) 1).isEqualTo((short) 2),
|
||||
assertThat((short) 1).isEqualTo((short) 2),
|
||||
assertThat(1).isEqualTo(2),
|
||||
assertThat(1).isEqualTo(2),
|
||||
assertThat(1L).isEqualTo(2L),
|
||||
assertThat(1L).isEqualTo(2L),
|
||||
assertThat(1F).isEqualTo(2F),
|
||||
assertThat(1F).isEqualTo(2F),
|
||||
assertThat(1.0).isEqualTo(2.0),
|
||||
assertThat(1.0).isEqualTo(2.0));
|
||||
}
|
||||
|
||||
@SuppressWarnings("SimplifyBooleanExpression")
|
||||
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIsNotEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
assertThat(true).isNotEqualTo(false),
|
||||
assertThat(true).isNotEqualTo(false),
|
||||
assertThat((byte) 1).isNotEqualTo((byte) 2),
|
||||
assertThat((byte) 1).isNotEqualTo((byte) 2),
|
||||
assertThat((char) 1).isNotEqualTo((char) 2),
|
||||
assertThat((char) 1).isNotEqualTo((char) 2),
|
||||
assertThat((short) 1).isNotEqualTo((short) 2),
|
||||
assertThat((short) 1).isNotEqualTo((short) 2),
|
||||
assertThat(1).isNotEqualTo(2),
|
||||
assertThat(1).isNotEqualTo(2),
|
||||
assertThat(1L).isNotEqualTo(2L),
|
||||
assertThat(1L).isNotEqualTo(2L),
|
||||
assertThat(1F).isNotEqualTo(2F),
|
||||
assertThat(1F).isNotEqualTo(2F),
|
||||
assertThat(1.0).isNotEqualTo(2.0),
|
||||
assertThat(1.0).isNotEqualTo(2.0));
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIsLessThan() {
|
||||
return ImmutableSet.of(
|
||||
assertThat((byte) 1).isLessThan((byte) 2),
|
||||
assertThat((byte) 1).isLessThan((byte) 2),
|
||||
assertThat((char) 1).isLessThan((char) 2),
|
||||
assertThat((char) 1).isLessThan((char) 2),
|
||||
assertThat((short) 1).isLessThan((short) 2),
|
||||
assertThat((short) 1).isLessThan((short) 2),
|
||||
assertThat(1).isLessThan(2),
|
||||
assertThat(1).isLessThan(2),
|
||||
assertThat(1L).isLessThan(2L),
|
||||
assertThat(1L).isLessThan(2L),
|
||||
assertThat(1F).isLessThan(2F),
|
||||
assertThat(1F).isLessThan(2F),
|
||||
assertThat(1.0).isLessThan(2.0),
|
||||
assertThat(1.0).isLessThan(2.0));
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIsLessThanOrEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
assertThat((byte) 1).isLessThanOrEqualTo((byte) 2),
|
||||
assertThat((byte) 1).isLessThanOrEqualTo((byte) 2),
|
||||
assertThat((char) 1).isLessThanOrEqualTo((char) 2),
|
||||
assertThat((char) 1).isLessThanOrEqualTo((char) 2),
|
||||
assertThat((short) 1).isLessThanOrEqualTo((short) 2),
|
||||
assertThat((short) 1).isLessThanOrEqualTo((short) 2),
|
||||
assertThat(1).isLessThanOrEqualTo(2),
|
||||
assertThat(1).isLessThanOrEqualTo(2),
|
||||
assertThat(1L).isLessThanOrEqualTo(2L),
|
||||
assertThat(1L).isLessThanOrEqualTo(2L),
|
||||
assertThat(1F).isLessThanOrEqualTo(2F),
|
||||
assertThat(1F).isLessThanOrEqualTo(2F),
|
||||
assertThat(1.0).isLessThanOrEqualTo(2.0),
|
||||
assertThat(1.0).isLessThanOrEqualTo(2.0));
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIsGreaterThan() {
|
||||
return ImmutableSet.of(
|
||||
assertThat((byte) 1).isGreaterThan((byte) 2),
|
||||
assertThat((byte) 1).isGreaterThan((byte) 2),
|
||||
assertThat((char) 1).isGreaterThan((char) 2),
|
||||
assertThat((char) 1).isGreaterThan((char) 2),
|
||||
assertThat((short) 1).isGreaterThan((short) 2),
|
||||
assertThat((short) 1).isGreaterThan((short) 2),
|
||||
assertThat(1).isGreaterThan(2),
|
||||
assertThat(1).isGreaterThan(2),
|
||||
assertThat(1L).isGreaterThan(2L),
|
||||
assertThat(1L).isGreaterThan(2L),
|
||||
assertThat(1F).isGreaterThan(2F),
|
||||
assertThat(1F).isGreaterThan(2F),
|
||||
assertThat(1.0).isGreaterThan(2.0),
|
||||
assertThat(1.0).isGreaterThan(2.0));
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIsGreaterThanOrEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
assertThat((byte) 1).isGreaterThanOrEqualTo((byte) 2),
|
||||
assertThat((byte) 1).isGreaterThanOrEqualTo((byte) 2),
|
||||
assertThat((char) 1).isGreaterThanOrEqualTo((char) 2),
|
||||
assertThat((char) 1).isGreaterThanOrEqualTo((char) 2),
|
||||
assertThat((short) 1).isGreaterThanOrEqualTo((short) 2),
|
||||
assertThat((short) 1).isGreaterThanOrEqualTo((short) 2),
|
||||
assertThat(1).isGreaterThanOrEqualTo(2),
|
||||
assertThat(1).isGreaterThanOrEqualTo(2),
|
||||
assertThat(1L).isGreaterThanOrEqualTo(2L),
|
||||
assertThat(1L).isGreaterThanOrEqualTo(2L),
|
||||
assertThat(1F).isGreaterThanOrEqualTo(2F),
|
||||
assertThat(1F).isGreaterThanOrEqualTo(2F),
|
||||
assertThat(1.0).isGreaterThanOrEqualTo(2.0),
|
||||
assertThat(1.0).isGreaterThanOrEqualTo(2.0));
|
||||
}
|
||||
}
|
||||
@@ -31,11 +31,13 @@ final class EqualityTemplatesTest implements RefasterTemplateTestCase {
|
||||
return !!Boolean.TRUE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SimplifyBooleanExpression")
|
||||
ImmutableSet<Boolean> testNegation() {
|
||||
return ImmutableSet.of(
|
||||
Boolean.TRUE ? !Boolean.FALSE : Boolean.FALSE,
|
||||
!(Boolean.TRUE == Boolean.FALSE),
|
||||
true ? !false : false,
|
||||
!(true == false),
|
||||
!((byte) 3 == (byte) 4),
|
||||
!((char) 3 == (char) 4),
|
||||
!((short) 3 == (short) 4),
|
||||
!(3 == 4),
|
||||
!(3L == 4L),
|
||||
@@ -44,11 +46,13 @@ final class EqualityTemplatesTest implements RefasterTemplateTestCase {
|
||||
!(BoundType.OPEN == BoundType.CLOSED));
|
||||
}
|
||||
|
||||
@SuppressWarnings("SimplifyBooleanExpression")
|
||||
ImmutableSet<Boolean> testIndirectDoubleNegation() {
|
||||
return ImmutableSet.of(
|
||||
Boolean.TRUE ? Boolean.FALSE : !Boolean.FALSE,
|
||||
!(Boolean.TRUE != Boolean.FALSE),
|
||||
true ? false : !false,
|
||||
!(true != false),
|
||||
!((byte) 3 != (byte) 4),
|
||||
!((char) 3 != (char) 4),
|
||||
!((short) 3 != (short) 4),
|
||||
!(3 != 4),
|
||||
!(3L != 4L),
|
||||
|
||||
@@ -31,11 +31,13 @@ final class EqualityTemplatesTest implements RefasterTemplateTestCase {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SimplifyBooleanExpression")
|
||||
ImmutableSet<Boolean> testNegation() {
|
||||
return ImmutableSet.of(
|
||||
Boolean.TRUE != Boolean.FALSE,
|
||||
Boolean.TRUE != Boolean.FALSE,
|
||||
true != false,
|
||||
true != false,
|
||||
(byte) 3 != (byte) 4,
|
||||
(char) 3 != (char) 4,
|
||||
(short) 3 != (short) 4,
|
||||
3 != 4,
|
||||
3L != 4L,
|
||||
@@ -44,11 +46,13 @@ final class EqualityTemplatesTest implements RefasterTemplateTestCase {
|
||||
BoundType.OPEN != BoundType.CLOSED);
|
||||
}
|
||||
|
||||
@SuppressWarnings("SimplifyBooleanExpression")
|
||||
ImmutableSet<Boolean> testIndirectDoubleNegation() {
|
||||
return ImmutableSet.of(
|
||||
Boolean.TRUE == Boolean.FALSE,
|
||||
Boolean.TRUE == Boolean.FALSE,
|
||||
true == false,
|
||||
true == false,
|
||||
(byte) 3 == (byte) 4,
|
||||
(char) 3 == (char) 4,
|
||||
(short) 3 == (short) 4,
|
||||
3 == 4,
|
||||
3L == 4L,
|
||||
|
||||
@@ -2,6 +2,7 @@ package tech.picnic.errorprone.refastertemplates;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterTemplateTestCase;
|
||||
|
||||
@@ -11,6 +12,14 @@ final class NullTemplatesTest implements RefasterTemplateTestCase {
|
||||
return ImmutableSet.of(MoreObjects.class);
|
||||
}
|
||||
|
||||
boolean testIsNull() {
|
||||
return Objects.isNull("foo");
|
||||
}
|
||||
|
||||
boolean testIsNotNull() {
|
||||
return Objects.nonNull("foo");
|
||||
}
|
||||
|
||||
String testRequireNonNullElse() {
|
||||
return MoreObjects.firstNonNull("foo", "bar");
|
||||
}
|
||||
|
||||
@@ -14,6 +14,14 @@ final class NullTemplatesTest implements RefasterTemplateTestCase {
|
||||
return ImmutableSet.of(MoreObjects.class);
|
||||
}
|
||||
|
||||
boolean testIsNull() {
|
||||
return "foo" == null;
|
||||
}
|
||||
|
||||
boolean testIsNotNull() {
|
||||
return "foo" != null;
|
||||
}
|
||||
|
||||
String testRequireNonNullElse() {
|
||||
return requireNonNullElse("foo", "bar");
|
||||
}
|
||||
|
||||
@@ -101,4 +101,12 @@ final class OptionalTemplatesTest implements RefasterTemplateTestCase {
|
||||
Optional.of("baz").map(Optional::of).orElseGet(() -> Optional.of("qux")),
|
||||
Stream.of(Optional.of("quux"), Optional.of("quuz")).flatMap(Optional::stream).findFirst());
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<String>> testOptionalIdentity() {
|
||||
return ImmutableSet.of(
|
||||
Optional.of("foo").stream().findFirst(),
|
||||
Optional.of("bar").stream().findAny(),
|
||||
Optional.of("baz").stream().min(String::compareTo),
|
||||
Optional.of("qux").stream().max(String::compareTo));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,4 +98,9 @@ final class OptionalTemplatesTest implements RefasterTemplateTestCase {
|
||||
Optional.of("baz").or(() -> Optional.of("qux")),
|
||||
Optional.of("quux").or(() -> Optional.of("quuz")));
|
||||
}
|
||||
|
||||
ImmutableSet<Optional<String>> testOptionalIdentity() {
|
||||
return ImmutableSet.of(
|
||||
Optional.of("foo"), Optional.of("bar"), Optional.of("baz"), Optional.of("qux"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ final class PrimitiveTemplatesTest implements RefasterTemplateTestCase {
|
||||
ImmutableSet<Boolean> testLessThan() {
|
||||
return ImmutableSet.of(
|
||||
!((byte) 3 >= (byte) 4),
|
||||
!((char) 3 >= (char) 4),
|
||||
!((short) 3 >= (short) 4),
|
||||
!(3 >= 4),
|
||||
!(3L >= 4L),
|
||||
@@ -23,6 +24,7 @@ final class PrimitiveTemplatesTest implements RefasterTemplateTestCase {
|
||||
ImmutableSet<Boolean> testLessThanOrEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
!((byte) 3 > (byte) 4),
|
||||
!((char) 3 > (char) 4),
|
||||
!((short) 3 > (short) 4),
|
||||
!(3 > 4),
|
||||
!(3L > 4L),
|
||||
@@ -33,6 +35,7 @@ final class PrimitiveTemplatesTest implements RefasterTemplateTestCase {
|
||||
ImmutableSet<Boolean> testGreaterThan() {
|
||||
return ImmutableSet.of(
|
||||
!((byte) 3 <= (byte) 4),
|
||||
!((char) 3 <= (char) 4),
|
||||
!((short) 3 <= (short) 4),
|
||||
!(3 <= 4),
|
||||
!(3L <= 4L),
|
||||
@@ -43,6 +46,7 @@ final class PrimitiveTemplatesTest implements RefasterTemplateTestCase {
|
||||
ImmutableSet<Boolean> testGreaterThanOrEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
!((byte) 3 < (byte) 4),
|
||||
!((char) 3 < (char) 4),
|
||||
!((short) 3 < (short) 4),
|
||||
!(3 < 4),
|
||||
!(3L < 4L),
|
||||
|
||||
@@ -12,22 +12,46 @@ final class PrimitiveTemplatesTest implements RefasterTemplateTestCase {
|
||||
|
||||
ImmutableSet<Boolean> testLessThan() {
|
||||
return ImmutableSet.of(
|
||||
(byte) 3 < (byte) 4, (short) 3 < (short) 4, 3 < 4, 3L < 4L, 3F < 4F, 3.0 < 4.0);
|
||||
(byte) 3 < (byte) 4,
|
||||
(char) 3 < (char) 4,
|
||||
(short) 3 < (short) 4,
|
||||
3 < 4,
|
||||
3L < 4L,
|
||||
3F < 4F,
|
||||
3.0 < 4.0);
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testLessThanOrEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
(byte) 3 <= (byte) 4, (short) 3 <= (short) 4, 3 <= 4, 3L <= 4L, 3F <= 4F, 3.0 <= 4.0);
|
||||
(byte) 3 <= (byte) 4,
|
||||
(char) 3 <= (char) 4,
|
||||
(short) 3 <= (short) 4,
|
||||
3 <= 4,
|
||||
3L <= 4L,
|
||||
3F <= 4F,
|
||||
3.0 <= 4.0);
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testGreaterThan() {
|
||||
return ImmutableSet.of(
|
||||
(byte) 3 > (byte) 4, (short) 3 > (short) 4, 3 > 4, 3L > 4L, 3F > 4F, 3.0 > 4.0);
|
||||
(byte) 3 > (byte) 4,
|
||||
(char) 3 > (char) 4,
|
||||
(short) 3 > (short) 4,
|
||||
3 > 4,
|
||||
3L > 4L,
|
||||
3F > 4F,
|
||||
3.0 > 4.0);
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testGreaterThanOrEqualTo() {
|
||||
return ImmutableSet.of(
|
||||
(byte) 3 >= (byte) 4, (short) 3 >= (short) 4, 3 >= 4, 3L >= 4L, 3F >= 4F, 3.0 >= 4.0);
|
||||
(byte) 3 >= (byte) 4,
|
||||
(char) 3 >= (char) 4,
|
||||
(short) 3 >= (short) 4,
|
||||
3 >= 4,
|
||||
3L >= 4L,
|
||||
3F >= 4F,
|
||||
3.0 >= 4.0);
|
||||
}
|
||||
|
||||
int testLongToIntExact() {
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Supplier;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -19,6 +20,15 @@ final class ReactorTemplatesTest implements RefasterTemplateTestCase {
|
||||
return ImmutableSet.of(assertThat(0));
|
||||
}
|
||||
|
||||
ImmutableSet<Mono<?>> testMonoFromSupplier() {
|
||||
return ImmutableSet.of(
|
||||
Mono.fromCallable((Callable<?>) null),
|
||||
Mono.fromCallable(() -> getClass().getDeclaredConstructor()),
|
||||
Mono.fromCallable(() -> toString()),
|
||||
Mono.fromCallable(getClass()::getDeclaredConstructor),
|
||||
Mono.fromCallable(this::toString));
|
||||
}
|
||||
|
||||
ImmutableSet<Mono<Integer>> testMonoFromOptional() {
|
||||
return ImmutableSet.of(
|
||||
Mono.fromCallable(() -> Optional.of(1).orElse(null)),
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Supplier;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -20,6 +21,15 @@ final class ReactorTemplatesTest implements RefasterTemplateTestCase {
|
||||
return ImmutableSet.of(assertThat(0));
|
||||
}
|
||||
|
||||
ImmutableSet<Mono<?>> testMonoFromSupplier() {
|
||||
return ImmutableSet.of(
|
||||
Mono.fromCallable((Callable<?>) null),
|
||||
Mono.fromCallable(() -> getClass().getDeclaredConstructor()),
|
||||
Mono.fromSupplier(() -> toString()),
|
||||
Mono.fromCallable(getClass()::getDeclaredConstructor),
|
||||
Mono.fromSupplier(this::toString));
|
||||
}
|
||||
|
||||
ImmutableSet<Mono<Integer>> testMonoFromOptional() {
|
||||
return ImmutableSet.of(
|
||||
Mono.defer(() -> Mono.justOrEmpty(Optional.of(1))),
|
||||
|
||||
@@ -7,8 +7,6 @@ import io.reactivex.Flowable;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import java.util.Arrays;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.adapter.rxjava.RxJava2Adapter;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -21,24 +19,20 @@ final class RxJava2AdapterTemplatesTest implements RefasterTemplateTestCase {
|
||||
Completable.complete().to(RxJava2Adapter::completableToMono));
|
||||
}
|
||||
|
||||
ImmutableSet<Publisher<Integer>> testFlowableToFlux() {
|
||||
// The `Arrays.asList` is to avoid confusing `javac`; `ImmutableSet.of` uses varargs from the
|
||||
// seventh parameter onwards.
|
||||
return ImmutableSet.copyOf(
|
||||
Arrays.asList(
|
||||
Flowable.just(1).compose(Flux::from),
|
||||
Flowable.just(2).to(Flux::from),
|
||||
Flowable.just(3).as(Flux::from),
|
||||
Flowable.just(4).compose(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(5).<Publisher<Integer>>to(RxJava2Adapter::flowableToFlux)));
|
||||
ImmutableSet<Flux<Integer>> testFlowableToFlux() {
|
||||
return ImmutableSet.of(
|
||||
Flux.from(Flowable.just(1)),
|
||||
Flowable.just(2).to(Flux::from),
|
||||
Flowable.just(3).as(Flux::from),
|
||||
RxJava2Adapter.flowableToFlux(Flowable.just(4)),
|
||||
Flowable.just(5).to(RxJava2Adapter::flowableToFlux));
|
||||
}
|
||||
|
||||
ImmutableSet<Publisher<String>> testFluxToFlowable() {
|
||||
ImmutableSet<Flowable<String>> testFluxToFlowable() {
|
||||
return ImmutableSet.of(
|
||||
Flowable.fromPublisher(Flux.just("foo")),
|
||||
Flux.just("bar").transform(Flowable::fromPublisher),
|
||||
Flux.just("baz").as(Flowable::fromPublisher),
|
||||
Flux.just("qux").transform(RxJava2Adapter::fluxToFlowable));
|
||||
Flux.just("bar").as(Flowable::fromPublisher),
|
||||
RxJava2Adapter.fluxToFlowable(Flux.just("baz")));
|
||||
}
|
||||
|
||||
ImmutableSet<Observable<Integer>> testFluxToObservable() {
|
||||
@@ -61,12 +55,11 @@ final class RxJava2AdapterTemplatesTest implements RefasterTemplateTestCase {
|
||||
RxJava2Adapter.monoToCompletable(Mono.empty()));
|
||||
}
|
||||
|
||||
ImmutableSet<Publisher<Integer>> testMonoToFlowable() {
|
||||
ImmutableSet<Flowable<Integer>> testMonoToFlowable() {
|
||||
return ImmutableSet.of(
|
||||
Flowable.fromPublisher(Mono.just(1)),
|
||||
Mono.just(2).transform(Flowable::fromPublisher),
|
||||
Mono.just(3).as(Flowable::fromPublisher),
|
||||
Mono.just(4).transform(RxJava2Adapter::monoToFlowable));
|
||||
Mono.just(2).as(Flowable::fromPublisher),
|
||||
RxJava2Adapter.monoToFlowable(Mono.just(3)));
|
||||
}
|
||||
|
||||
Maybe<String> testMonoToMaybe() {
|
||||
|
||||
@@ -7,8 +7,6 @@ import io.reactivex.Flowable;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import java.util.Arrays;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.adapter.rxjava.RxJava2Adapter;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -21,24 +19,20 @@ final class RxJava2AdapterTemplatesTest implements RefasterTemplateTestCase {
|
||||
Completable.complete().as(RxJava2Adapter::completableToMono));
|
||||
}
|
||||
|
||||
ImmutableSet<Publisher<Integer>> testFlowableToFlux() {
|
||||
// The `Arrays.asList` is to avoid confusing `javac`; `ImmutableSet.of` uses varargs from the
|
||||
// seventh parameter onwards.
|
||||
return ImmutableSet.copyOf(
|
||||
Arrays.asList(
|
||||
Flowable.just(1).as(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(2).as(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(3).as(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(4).as(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(5).as(RxJava2Adapter::flowableToFlux)));
|
||||
ImmutableSet<Flux<Integer>> testFlowableToFlux() {
|
||||
return ImmutableSet.of(
|
||||
Flowable.just(1).as(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(2).as(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(3).as(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(4).as(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(5).as(RxJava2Adapter::flowableToFlux));
|
||||
}
|
||||
|
||||
ImmutableSet<Publisher<String>> testFluxToFlowable() {
|
||||
ImmutableSet<Flowable<String>> testFluxToFlowable() {
|
||||
return ImmutableSet.of(
|
||||
Flux.just("foo").as(RxJava2Adapter::fluxToFlowable),
|
||||
Flux.just("bar").as(RxJava2Adapter::fluxToFlowable),
|
||||
Flux.just("baz").as(RxJava2Adapter::fluxToFlowable),
|
||||
Flux.just("qux").as(RxJava2Adapter::fluxToFlowable));
|
||||
Flux.just("baz").as(RxJava2Adapter::fluxToFlowable));
|
||||
}
|
||||
|
||||
ImmutableSet<Observable<Integer>> testFluxToObservable() {
|
||||
@@ -61,12 +55,11 @@ final class RxJava2AdapterTemplatesTest implements RefasterTemplateTestCase {
|
||||
Mono.empty().as(RxJava2Adapter::monoToCompletable));
|
||||
}
|
||||
|
||||
ImmutableSet<Publisher<Integer>> testMonoToFlowable() {
|
||||
ImmutableSet<Flowable<Integer>> testMonoToFlowable() {
|
||||
return ImmutableSet.of(
|
||||
Mono.just(1).as(RxJava2Adapter::monoToFlowable),
|
||||
Mono.just(2).as(RxJava2Adapter::monoToFlowable),
|
||||
Mono.just(3).as(RxJava2Adapter::monoToFlowable),
|
||||
Mono.just(4).as(RxJava2Adapter::monoToFlowable));
|
||||
Mono.just(3).as(RxJava2Adapter::monoToFlowable));
|
||||
}
|
||||
|
||||
Maybe<String> testMonoToMaybe() {
|
||||
|
||||
@@ -10,7 +10,9 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Streams;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterTemplateTestCase;
|
||||
|
||||
@@ -18,7 +20,7 @@ final class StringTemplatesTest implements RefasterTemplateTestCase {
|
||||
@Override
|
||||
public ImmutableSet<?> elidedTypesAndStaticImports() {
|
||||
return ImmutableSet.of(
|
||||
Arrays.class, Joiner.class, Stream.class, Streams.class, joining(), UTF_8);
|
||||
Arrays.class, Joiner.class, Objects.class, Stream.class, Streams.class, joining(), UTF_8);
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testStringIsEmpty() {
|
||||
@@ -59,6 +61,14 @@ final class StringTemplatesTest implements RefasterTemplateTestCase {
|
||||
ImmutableList.of("foo", "bar").stream().collect(joining("f")));
|
||||
}
|
||||
|
||||
String testStringValueOf() {
|
||||
return Objects.toString("foo");
|
||||
}
|
||||
|
||||
Function<Object, String> testStringValueOfMethodReference() {
|
||||
return Objects::toString;
|
||||
}
|
||||
|
||||
String testSubstringRemainder() {
|
||||
return "foo".substring(1, "foo".length());
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Streams;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterTemplateTestCase;
|
||||
|
||||
@@ -19,7 +21,7 @@ final class StringTemplatesTest implements RefasterTemplateTestCase {
|
||||
@Override
|
||||
public ImmutableSet<?> elidedTypesAndStaticImports() {
|
||||
return ImmutableSet.of(
|
||||
Arrays.class, Joiner.class, Stream.class, Streams.class, joining(), UTF_8);
|
||||
Arrays.class, Joiner.class, Objects.class, Stream.class, Streams.class, joining(), UTF_8);
|
||||
}
|
||||
|
||||
ImmutableSet<Boolean> testStringIsEmpty() {
|
||||
@@ -59,6 +61,14 @@ final class StringTemplatesTest implements RefasterTemplateTestCase {
|
||||
String.join("f", ImmutableList.of("foo", "bar")));
|
||||
}
|
||||
|
||||
String testStringValueOf() {
|
||||
return String.valueOf("foo");
|
||||
}
|
||||
|
||||
Function<Object, String> testStringValueOfMethodReference() {
|
||||
return String::valueOf;
|
||||
}
|
||||
|
||||
String testSubstringRemainder() {
|
||||
return "foo".substring(1);
|
||||
}
|
||||
|
||||
10
logo-dark.svg
Normal file
10
logo-dark.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 63 KiB |
10
logo.svg
Normal file
10
logo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 63 KiB |
124
pom.xml
124
pom.xml
@@ -4,7 +4,7 @@
|
||||
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.1.1-SNAPSHOT</version>
|
||||
<version>0.3.0</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Picnic :: Error Prone Support</name>
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:PicnicSupermarket/error-prone-support.git</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
<tag>v0.3.0</tag>
|
||||
<url>https://github.com/PicnicSupermarket/error-prone-support</url>
|
||||
</scm>
|
||||
<issueManagement>
|
||||
@@ -56,8 +56,8 @@
|
||||
<url>https://github.com/PicnicSupermarket/error-prone-support/issues</url>
|
||||
</issueManagement>
|
||||
<ciManagement>
|
||||
<system>Travis CI</system>
|
||||
<url>https://travis-ci.org/PicnicSupermarket/error-prone-support</url>
|
||||
<system>GitHub Actions</system>
|
||||
<url>https://github.com/PicnicSupermarket/error-prone-support/actions</url>
|
||||
</ciManagement>
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
@@ -87,6 +87,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
|
||||
@@ -94,7 +95,6 @@
|
||||
--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
|
||||
<!-- The test JVMs are short-running. By disabling certain
|
||||
expensive JIT optimizations we actually speed up most tests. -->
|
||||
@@ -144,16 +144,15 @@
|
||||
<version.auto-service>1.0.1</version.auto-service>
|
||||
<version.auto-value>1.9</version.auto-value>
|
||||
<version.error-prone>${version.error-prone-orig}</version.error-prone>
|
||||
<version.error-prone-fork>v${version.error-prone-orig}-picnic-2</version.error-prone-fork>
|
||||
<version.error-prone-orig>2.14.0</version.error-prone-orig>
|
||||
<version.error-prone-slf4j>0.1.13</version.error-prone-slf4j>
|
||||
<version.findbugs-format-string>3.0.0</version.findbugs-format-string>
|
||||
<version.error-prone-fork>v${version.error-prone-orig}-picnic-3</version.error-prone-fork>
|
||||
<version.error-prone-orig>2.15.0</version.error-prone-orig>
|
||||
<version.error-prone-slf4j>0.1.15</version.error-prone-slf4j>
|
||||
<version.guava-beta-checker>1.0</version.guava-beta-checker>
|
||||
<version.jdk>11</version.jdk>
|
||||
<version.maven>3.8.6</version.maven>
|
||||
<version.mockito>4.7.0</version.mockito>
|
||||
<version.mockito>4.8.0</version.mockito>
|
||||
<version.nopen-checker>1.0.1</version.nopen-checker>
|
||||
<version.nullaway>0.9.9</version.nullaway>
|
||||
<version.nullaway>0.10.1</version.nullaway>
|
||||
<!-- XXX: Two other dependencies are potentially of interest:
|
||||
`com.palantir.assertj-automation:assertj-refaster-rules` and
|
||||
`com.palantir.baseline:baseline-refaster-rules` contain Refaster rules
|
||||
@@ -216,7 +215,7 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson</groupId>
|
||||
<artifactId>jackson-bom</artifactId>
|
||||
<version>2.13.3</version>
|
||||
<version>2.13.4</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -240,23 +239,11 @@
|
||||
<artifactId>auto-value-annotations</artifactId>
|
||||
<version>${version.auto-value}</version>
|
||||
</dependency>
|
||||
<!-- Specified as a workaround for
|
||||
https://github.com/mojohaus/versions-maven-plugin/issues/244. -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jFormatString</artifactId>
|
||||
<version>${version.findbugs-format-string}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>javac</artifactId>
|
||||
<version>9+181-r4173-1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.googlejavaformat</groupId>
|
||||
<artifactId>google-java-format</artifactId>
|
||||
@@ -291,7 +278,7 @@
|
||||
<dependency>
|
||||
<groupId>com.newrelic.agent.java</groupId>
|
||||
<artifactId>newrelic-api</artifactId>
|
||||
<version>7.9.0</version>
|
||||
<version>7.10.0</version>
|
||||
</dependency>
|
||||
<!-- Specified as a workaround for
|
||||
https://github.com/mojohaus/versions-maven-plugin/issues/244. -->
|
||||
@@ -317,7 +304,7 @@
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-bom</artifactId>
|
||||
<version>2020.0.22</version>
|
||||
<version>2020.0.23</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -379,7 +366,7 @@
|
||||
<dependency>
|
||||
<groupId>org.checkerframework</groupId>
|
||||
<artifactId>checker-qual</artifactId>
|
||||
<version>3.24.0</version>
|
||||
<version>3.25.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
@@ -389,7 +376,12 @@
|
||||
<dependency>
|
||||
<groupId>org.immutables</groupId>
|
||||
<artifactId>value-annotations</artifactId>
|
||||
<version>2.9.0</version>
|
||||
<version>2.9.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jspecify</groupId>
|
||||
<artifactId>jspecify</artifactId>
|
||||
<version>0.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit</groupId>
|
||||
@@ -405,11 +397,6 @@
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.reactivestreams</groupId>
|
||||
<artifactId>reactive-streams</artifactId>
|
||||
<version>1.0.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
@@ -418,7 +405,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>5.3.22</version>
|
||||
<version>5.3.23</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -527,7 +514,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.1.2</version>
|
||||
<version>3.2.0</version>
|
||||
<configuration>
|
||||
<checkstyleRules>
|
||||
<!-- We only enable rules that are not enforced by
|
||||
@@ -746,6 +733,7 @@
|
||||
<module name="VisibilityModifier" />
|
||||
</module>
|
||||
<module name="UniqueProperties" />
|
||||
<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck" />
|
||||
</module>
|
||||
</checkstyleRules>
|
||||
<failOnViolation>false</failOnViolation>
|
||||
@@ -769,7 +757,12 @@
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>10.3.2</version>
|
||||
<version>10.3.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.spring.nohttp</groupId>
|
||||
<artifactId>nohttp-checkstyle</artifactId>
|
||||
<version>0.0.10</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
@@ -791,7 +784,7 @@
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.10.1</version>
|
||||
<configuration>
|
||||
<!-- XXX: MCOMPILER-503: The plugin contructs a highly
|
||||
<!-- XXX: MCOMPILER-503: The plugin constructs a highly
|
||||
unintuitive annotation processor classpath, in which
|
||||
some indirect dependencies take precedence over
|
||||
explicitly defined dependencies. This can largely be
|
||||
@@ -851,13 +844,19 @@
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs>
|
||||
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
|
||||
<arg>-Xmaxerrs</arg>
|
||||
<arg>10000</arg>
|
||||
<arg>-Xmaxwarns</arg>
|
||||
<arg>10000</arg>
|
||||
</compilerArgs>
|
||||
<parameters>true</parameters>
|
||||
<release>${version.jdk}</release>
|
||||
<source>${version.jdk}</source>
|
||||
<target>${version.jdk}</target>
|
||||
<!-- Erroneously inverted logic... for details, see
|
||||
https://issues.apache.org/jira/browse/MCOMPILER-209. -->
|
||||
<useIncrementalCompilation>false</useIncrementalCompilation>
|
||||
@@ -959,7 +958,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<skipIfEmpty>true</skipIfEmpty>
|
||||
<archive>
|
||||
@@ -989,6 +988,13 @@
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<configuration>
|
||||
<additionalJOptions>
|
||||
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</additionalJOption>
|
||||
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</additionalJOption>
|
||||
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</additionalJOption>
|
||||
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</additionalJOption>
|
||||
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</additionalJOption>
|
||||
</additionalJOptions>
|
||||
<!-- All relevant doclint checks are performed during
|
||||
the compilation phase; no need to recheck during
|
||||
Javadoc generation. -->
|
||||
@@ -1013,7 +1019,6 @@
|
||||
<autoVersionSubmodules>true</autoVersionSubmodules>
|
||||
<releaseProfiles>release</releaseProfiles>
|
||||
<tagNameFormat>v@{project.version}</tagNameFormat>
|
||||
<useReleaseProfile>false</useReleaseProfile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
@@ -1203,7 +1208,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>versions-maven-plugin</artifactId>
|
||||
<version>2.11.0</version>
|
||||
<version>2.12.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.gaul</groupId>
|
||||
@@ -1245,7 +1250,7 @@
|
||||
<plugin>
|
||||
<groupId>org.pitest</groupId>
|
||||
<artifactId>pitest-maven</artifactId>
|
||||
<version>1.9.4</version>
|
||||
<version>1.9.5</version>
|
||||
<configuration>
|
||||
<!-- Use multiple threads to speed things up. Extend
|
||||
timeouts to prevent false positives as a result of
|
||||
@@ -1719,41 +1724,6 @@
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<!-- Some code in this project interfaces directly with the Java
|
||||
compiler. The following `add-exports` arguments are not necessary
|
||||
for the code to compile because `com.google.errorprone:javac` is on
|
||||
the classpath. In fact, enabling this profile when building with
|
||||
Maven on the command line will cause a build failure, because these
|
||||
flags are incompatible with the `release` flag. This profile exists
|
||||
solely to be enabled within an IDE: without them IntelliJ IDEA
|
||||
reports compilation errors. -->
|
||||
<id>add-exports</id>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<compilerArgs combine.children="append">
|
||||
<arg>--add-exports</arg>
|
||||
<arg>jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports</arg>
|
||||
<arg>jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports</arg>
|
||||
<arg>jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports</arg>
|
||||
<arg>jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports</arg>
|
||||
<arg>jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<!-- This profile is auto-activated when performing a release. -->
|
||||
<id>release</id>
|
||||
|
||||
@@ -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.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>refaster-compiler</artifactId>
|
||||
@@ -37,11 +37,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.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
|
||||
@@ -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.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>refaster-runner</artifactId>
|
||||
@@ -47,11 +47,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.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
|
||||
@@ -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.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>refaster-support</artifactId>
|
||||
@@ -52,8 +52,8 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>javac</artifactId>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
|
||||
/** A matcher of array-typed expressions, for use with Refaster's {@code @Matches} annotation. */
|
||||
/** A matcher of array-typed expressions. */
|
||||
public final class IsArray implements Matcher<ExpressionTree> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> DELEGATE = isArrayType();
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package tech.picnic.errorprone.refaster.util;
|
||||
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.isSameType;
|
||||
import static com.google.errorprone.suppliers.Suppliers.CHAR_TYPE;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
|
||||
/** A matcher of {@code char}- and {@link Character}-typed expressions. */
|
||||
public final class IsCharacter implements Matcher<ExpressionTree> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> DELEGATE =
|
||||
anyOf(isSameType(CHAR_TYPE), isSameType(Character.class));
|
||||
|
||||
@Override
|
||||
public boolean matches(ExpressionTree tree, VisitorState state) {
|
||||
return DELEGATE.matches(tree, state);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package tech.picnic.errorprone.refaster.util;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.LambdaExpressionTree;
|
||||
import com.sun.source.tree.MemberReferenceTree;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A matcher of functional interface expressions for which execution of the functional interface
|
||||
* method may throw a checked exception.
|
||||
*/
|
||||
public final class ThrowsCheckedException implements Matcher<ExpressionTree> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public boolean matches(ExpressionTree tree, VisitorState state) {
|
||||
return containsCheckedException(getThrownTypes(tree, state), state);
|
||||
}
|
||||
|
||||
private static Collection<Type> getThrownTypes(ExpressionTree tree, VisitorState state) {
|
||||
if (tree instanceof LambdaExpressionTree) {
|
||||
return ASTHelpers.getThrownExceptions(((LambdaExpressionTree) tree).getBody(), state);
|
||||
}
|
||||
|
||||
if (tree instanceof MemberReferenceTree) {
|
||||
Symbol symbol = ASTHelpers.getSymbol(tree);
|
||||
if (symbol == null) {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
|
||||
return symbol.type.getThrownTypes();
|
||||
}
|
||||
|
||||
Type type = ASTHelpers.getType(tree);
|
||||
if (type == null) {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
|
||||
try {
|
||||
return state.getTypes().findDescriptorType(type).getThrownTypes();
|
||||
} catch (FunctionDescriptorLookupError e) {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean containsCheckedException(Collection<Type> types, VisitorState state) {
|
||||
return !types.stream()
|
||||
.allMatch(
|
||||
t ->
|
||||
ASTHelpers.isSubtype(t, state.getSymtab().runtimeExceptionType, state)
|
||||
|| ASTHelpers.isSubtype(t, state.getSymtab().errorType, state));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* A collection of zero-argument {@link com.google.errorprone.matchers.Matcher Matcher}
|
||||
* implementations for use with Refaster's {@link
|
||||
* com.google.errorprone.refaster.annotation.Matches @Matches} annotation.
|
||||
* com.google.errorprone.refaster.annotation.Matches @Matches} and {@link
|
||||
* com.google.errorprone.refaster.annotation.NotMatches @NotMatches} annotations.
|
||||
*/
|
||||
@com.google.errorprone.annotations.CheckReturnValue
|
||||
@javax.annotation.ParametersAreNonnullByDefault
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package tech.picnic.errorprone.refaster.util;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.CompilationUnitTreeMatcher;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.CompilationUnitTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.ImportTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An abstract {@link BugChecker} that reports a match for each expression matched by the given
|
||||
* {@link Matcher}.
|
||||
*
|
||||
* <p>Only {@link ExpressionTree}s that represent proper Java expressions (i.e. {@link
|
||||
* ExpressionTree}s that may be matched by Refaster) are considered.
|
||||
*/
|
||||
abstract class AbstractMatcherTestChecker extends BugChecker implements CompilationUnitTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Matcher<ExpressionTree> delegate;
|
||||
|
||||
AbstractMatcherTestChecker(Matcher<ExpressionTree> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description matchCompilationUnit(CompilationUnitTree compilationUnit, VisitorState state) {
|
||||
new TreeScanner<Void, Void>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Void scan(Tree tree, @Nullable Void unused) {
|
||||
if (tree instanceof ExpressionTree && delegate.matches((ExpressionTree) tree, state)) {
|
||||
state.reportMatch(
|
||||
Description.builder(tree, canonicalName(), null, defaultSeverity(), message())
|
||||
.build());
|
||||
}
|
||||
|
||||
return super.scan(tree, unused);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Void visitImport(ImportTree node, @Nullable Void unused) {
|
||||
/*
|
||||
* We're not interested in matching import statements. While components of these
|
||||
* can be `ExpressionTree`s, they will never be matched by Refaster.
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Void visitMethod(MethodTree node, @Nullable Void unused) {
|
||||
/*
|
||||
* We're not interested in matching e.g. parameter and return type declarations. While these
|
||||
* can be `ExpressionTree`s, they will never be matched by Refaster.
|
||||
*/
|
||||
return scan(node.getBody(), unused);
|
||||
}
|
||||
}.scan(compilationUnit, null);
|
||||
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,13 @@ import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class IsArrayTest {
|
||||
@Test
|
||||
void matches() {
|
||||
CompilationTestHelper.newInstance(TestChecker.class, getClass())
|
||||
CompilationTestHelper.newInstance(MatcherTestChecker.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"class A {",
|
||||
@@ -53,13 +49,15 @@ final class IsArrayTest {
|
||||
}
|
||||
|
||||
/** A {@link BugChecker} which simply delegates to {@link IsArray}. */
|
||||
@BugPattern(summary = "Flags array-returning method invocations", severity = ERROR)
|
||||
public static final class TestChecker extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
@BugPattern(summary = "Flags expressions matched by `IsArray`", severity = ERROR)
|
||||
public static final class MatcherTestChecker extends AbstractMatcherTestChecker {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
return new IsArray().matches(tree, state) ? describeMatch(tree) : Description.NO_MATCH;
|
||||
// XXX: This is a false positive reported by Checkstyle. See
|
||||
// https://github.com/checkstyle/checkstyle/issues/10161#issuecomment-1242732120.
|
||||
@SuppressWarnings("RedundantModifier")
|
||||
public MatcherTestChecker() {
|
||||
super(new IsArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package tech.picnic.errorprone.refaster.util;
|
||||
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class IsCharacterTest {
|
||||
@Test
|
||||
void matches() {
|
||||
CompilationTestHelper.newInstance(MatcherTestChecker.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"class A {",
|
||||
" String negative1() {",
|
||||
" return \"a\";",
|
||||
" }",
|
||||
"",
|
||||
" char[] negative2() {",
|
||||
" return \"a\".toCharArray();",
|
||||
" }",
|
||||
"",
|
||||
" byte negative3() {",
|
||||
" return (byte) 0;",
|
||||
" }",
|
||||
"",
|
||||
" int negative4() {",
|
||||
" return 0;",
|
||||
" }",
|
||||
"",
|
||||
" char positive1() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return 'a';",
|
||||
" }",
|
||||
"",
|
||||
" Character positive2() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return (Character) null;",
|
||||
" }",
|
||||
"",
|
||||
" char positive3() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" return (char) 1;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
/** A {@link BugChecker} which simply delegates to {@link IsCharacter}. */
|
||||
@BugPattern(summary = "Flags expressions matched by `IsCharacter`", severity = ERROR)
|
||||
public static final class MatcherTestChecker extends AbstractMatcherTestChecker {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// XXX: This is a false positive reported by Checkstyle. See
|
||||
// https://github.com/checkstyle/checkstyle/issues/10161#issuecomment-1242732120.
|
||||
@SuppressWarnings("RedundantModifier")
|
||||
public MatcherTestChecker() {
|
||||
super(new IsCharacter());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package tech.picnic.errorprone.refaster.util;
|
||||
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class ThrowsCheckedExceptionTest {
|
||||
@Test
|
||||
void matches() {
|
||||
CompilationTestHelper.newInstance(MatcherTestChecker.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import java.util.concurrent.Callable;",
|
||||
"import java.util.function.Supplier;",
|
||||
"",
|
||||
"class A {",
|
||||
" void negative1() {",
|
||||
" callableSink(null);",
|
||||
" }",
|
||||
"",
|
||||
" void negative2() {",
|
||||
" supplierSink(null);",
|
||||
" }",
|
||||
"",
|
||||
" void negative3() {",
|
||||
" callableSink(() -> toString());",
|
||||
" }",
|
||||
"",
|
||||
" void negative4() {",
|
||||
" supplierSink(() -> toString());",
|
||||
" }",
|
||||
"",
|
||||
" void negative5() {",
|
||||
" callableSink(this::toString);",
|
||||
" }",
|
||||
"",
|
||||
" void negative6() {",
|
||||
" supplierSink(this::toString);",
|
||||
" }",
|
||||
"",
|
||||
" void negative7() {",
|
||||
" supplierSink(",
|
||||
" new Supplier<>() {",
|
||||
" @Override",
|
||||
" public Object get() {",
|
||||
" return getClass();",
|
||||
" }",
|
||||
" });",
|
||||
" }",
|
||||
"",
|
||||
" void positive1() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" callableSink(() -> getClass().getDeclaredConstructor());",
|
||||
" }",
|
||||
"",
|
||||
" void positive2() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" callableSink(getClass()::getDeclaredConstructor);",
|
||||
" }",
|
||||
"",
|
||||
" void positive3() {",
|
||||
" callableSink(",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" new Callable<>() {",
|
||||
" @Override",
|
||||
" public Object call() throws NoSuchMethodException {",
|
||||
" return getClass().getDeclaredConstructor();",
|
||||
" }",
|
||||
" });",
|
||||
" }",
|
||||
"",
|
||||
" private static void callableSink(Callable<?> callable) {}",
|
||||
"",
|
||||
" private static void supplierSink(Supplier<?> supplier) {}",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
/** A {@link BugChecker} which simply delegates to {@link ThrowsCheckedException}. */
|
||||
@BugPattern(summary = "Flags expressions matched by `ThrowsCheckedException`", severity = ERROR)
|
||||
public static final class MatcherTestChecker extends AbstractMatcherTestChecker {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// XXX: This is a false positive reported by Checkstyle. See
|
||||
// https://github.com/checkstyle/checkstyle/issues/10161#issuecomment-1242732120.
|
||||
@SuppressWarnings("RedundantModifier")
|
||||
public MatcherTestChecker() {
|
||||
super(new ThrowsCheckedException());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>refaster-test-support</artifactId>
|
||||
@@ -50,11 +50,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.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
|
||||
@@ -7,7 +7,6 @@ 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.refaster.annotation.UseImportPolicy;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -30,7 +29,7 @@ final class ValidTemplates {
|
||||
static final class StaticImportStringLength {
|
||||
@BeforeTemplate
|
||||
boolean before(@Nullable String string) {
|
||||
return Objects.isNull(string) || string.isEmpty();
|
||||
return string == null || string.toCharArray().length == 0;
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
|
||||
@@ -3,14 +3,13 @@ package tech.picnic.errorprone.refaster.test;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/** Code to test the Refaster templates from {@link ValidTemplates}. */
|
||||
final class ValidTemplatesTest implements RefasterTemplateTestCase {
|
||||
@Override
|
||||
public ImmutableSet<?> elidedTypesAndStaticImports() {
|
||||
return ImmutableSet.of(Objects.class, Strings.class);
|
||||
return ImmutableSet.of(Strings.class);
|
||||
}
|
||||
|
||||
boolean testStringIsEmpty2() {
|
||||
@@ -18,7 +17,7 @@ final class ValidTemplatesTest implements RefasterTemplateTestCase {
|
||||
}
|
||||
|
||||
boolean testStaticImportStringLength() {
|
||||
return Objects.isNull("foo") || "foo".isEmpty();
|
||||
return "foo" == null || "foo".toCharArray().length == 0;
|
||||
}
|
||||
|
||||
void testBlockTemplateSetAddElement() {
|
||||
|
||||
@@ -5,14 +5,13 @@ import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/** Code to test the Refaster templates from {@link ValidTemplates}. */
|
||||
final class ValidTemplatesTest implements RefasterTemplateTestCase {
|
||||
@Override
|
||||
public ImmutableSet<?> elidedTypesAndStaticImports() {
|
||||
return ImmutableSet.of(Objects.class, Strings.class);
|
||||
return ImmutableSet.of(Strings.class);
|
||||
}
|
||||
|
||||
boolean testStringIsEmpty2() {
|
||||
|
||||
Reference in New Issue
Block a user