Compare commits
52 Commits
sschroever
...
gdejong/do
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
525df33e42 | ||
|
|
368f70fc98 | ||
|
|
ec023cd8cf | ||
|
|
9ae3c70dff | ||
|
|
f781c85143 | ||
|
|
d1054bfb8b | ||
|
|
ac86140420 | ||
|
|
1ed4892d34 | ||
|
|
8ee643e9b9 | ||
|
|
272245b9d4 | ||
|
|
d8009e9c26 | ||
|
|
d7f1fc6cff | ||
|
|
8760f7f31f | ||
|
|
81ddfdb6fb | ||
|
|
ccf3ce4962 | ||
|
|
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 |
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
@@ -2,8 +2,10 @@ name: Build and verify
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
branches: [$default-branch]
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
46
.github/workflows/deploy-website.yaml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Update `error-prone.picnic.tech` website contents
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'gdejong/docgen_old'
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3.0.2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v3.4.1
|
||||
with:
|
||||
java-version: 11.0.16
|
||||
distribution: temurin
|
||||
cache: maven
|
||||
- name: Configure Github Pages
|
||||
uses: actions/configure-pages@v2.0.0
|
||||
- name: Generate documentation
|
||||
run: bash ./generate-docs.sh
|
||||
- name: Build website with Jekyll
|
||||
uses: actions/jekyll-build-pages@v1.0.5
|
||||
with:
|
||||
source: website/
|
||||
destination: ./_site
|
||||
- name: Upload website artifact
|
||||
uses: actions/upload-pages-artifact@v1.0.3
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1.0.9
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
231
README.md
Normal file
@@ -0,0 +1,231 @@
|
||||
<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>
|
||||
</div>
|
||||
|
||||
# Error Prone Support
|
||||
|
||||
Error Prone Support is a [Picnic][picnic-blog]-opinionated extension of
|
||||
Google's [Error Prone][error-prone-orig-repo]. It aims to improve code quality,
|
||||
focussing on maintainability, consistency and avoidance of common pitfalls.
|
||||
|
||||
> Error Prone is a static analysis tool for Java that catches common
|
||||
> programming mistakes at compile-time.
|
||||
|
||||
Read more on how Picnic uses Error Prone (Support) in the blog post [_Picnic
|
||||
loves Error Prone: producing high-quality and consistent Java
|
||||
code_][picnic-blog-ep-post].
|
||||
|
||||
[![Maven Central][maven-central-badge]][maven-central-search]
|
||||
[![GitHub Actions][github-actions-build-badge]][github-actions-build-master]
|
||||
[![License][license-badge]][license]
|
||||
[![PRs Welcome][pr-badge]][contributing]
|
||||
|
||||
[Getting started](#-getting-started) •
|
||||
[Developing Error Prone Support](#-developing-error-prone-support) •
|
||||
[How it works](#-how-it-works) • [Contributing](#%EF%B8%8F-contributing)
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Getting started
|
||||
|
||||
### Installation
|
||||
|
||||
This library is built on top of [Error Prone][error-prone-orig-repo]. To use
|
||||
it:
|
||||
|
||||
1. First, follow Error Prone's [installation
|
||||
guide][error-prone-installation-guide].
|
||||
2. Next, edit your `pom.xml` file to add one or more Error Prone Support
|
||||
modules to the `annotationProcessorPaths` of the `maven-compiler-plugin`:
|
||||
|
||||
```xml
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<!-- Error Prone itself. -->
|
||||
<path>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>error_prone_core</artifactId>
|
||||
<version>${error-prone.version}</version>
|
||||
</path>
|
||||
<!-- Error Prone Support's additional bug checkers. -->
|
||||
<path>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-contrib</artifactId>
|
||||
<version>${error-prone-support.version}</version>
|
||||
</path>
|
||||
<!-- Error Prone Support's Refaster rules. -->
|
||||
<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.refasterrules.BigDecimalTemplates.BigDecimalZero]
|
||||
Did you mean 'return BigDecimal.ZERO;'?
|
||||
[WARNING] Example.java:[13,35] [IdentityConversion] This method invocation appears redundant; remove it or suppress this warning and add a comment explaining its purpose
|
||||
(see https://error-prone.picnic.tech/bugpatterns/IdentityConversion)
|
||||
Did you mean 'return set;' or '@SuppressWarnings("IdentityConversion") public ImmutableSet<Integer> getSet() {'?
|
||||
[INFO] 2 warnings
|
||||
[INFO] -------------------------------------------------------------
|
||||
...
|
||||
```
|
||||
|
||||
Two things are kicking in here:
|
||||
|
||||
1. An Error Prone [`BugChecker`][error-prone-bugchecker] that flags unnecessary
|
||||
[identity conversions][bug-checks-identity-conversion].
|
||||
2. A [Refaster][refaster] rule capable of
|
||||
[rewriting][refaster-rules-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
|
||||
rules][refaster-rules].
|
||||
|
||||
## 👷 Developing Error Prone Support
|
||||
|
||||
This is a [Maven][maven] project, so running `mvn clean install` performs a
|
||||
full clean build and installs the library to your local Maven repository. Some
|
||||
relevant flags:
|
||||
|
||||
- `-Dverification.warn` makes the warnings and errors emitted by various
|
||||
plugins and the Java compiler non-fatal, where possible.
|
||||
- `-Dverification.skip` disables various non-essential plugins and compiles the
|
||||
code with minimal checks (i.e. without linting, Error Prone checks, etc.).
|
||||
- `-Dversion.error-prone=some-version` runs the build using the specified
|
||||
version of Error Prone. This is useful e.g. when testing a locally built
|
||||
Error Prone SNAPSHOT.
|
||||
- `-Perror-prone-fork` runs the build using Picnic's [Error Prone
|
||||
fork][error-prone-fork-repo], hosted on [Jitpack][error-prone-fork-jitpack].
|
||||
This fork generally contains a few changes on top of the latest Error Prone
|
||||
release.
|
||||
- `-Pself-check` runs the checks defined by this project against itself.
|
||||
Pending a release of [google/error-prone#3301][error-prone-pull-3301], this
|
||||
flag must currently be used in combination with `-Perror-prone-fork`.
|
||||
|
||||
Some other commands one may find relevant:
|
||||
|
||||
- `mvn fmt:format` formats the code using
|
||||
[`google-java-format`][google-java-format].
|
||||
- `./run-mutation-tests.sh` runs mutation tests using [PIT][pitest]. The
|
||||
results can be reviewed by opening the respective
|
||||
`target/pit-reports/index.html` files. For more information check the [PIT
|
||||
Maven plugin][pitest-maven].
|
||||
- `./apply-error-prone-suggestions.sh` applies Error Prone and Error Prone
|
||||
Support code suggestions to this project. Before running this command, make
|
||||
sure to have installed the project (`mvn clean install`) and make sure that
|
||||
the current working directory does not contain unstaged or uncommited
|
||||
changes.
|
||||
|
||||
When running the project's tests in IntelliJ IDEA, you might see the following
|
||||
error:
|
||||
|
||||
```
|
||||
java: exporting a package from system module jdk.compiler is not allowed with --release
|
||||
```
|
||||
|
||||
If this happens, go to _Settings -> Build, Execution, Deployment -> Compiler ->
|
||||
Java Compiler_ and deselect the option _Use '--release' option for
|
||||
cross-compilation (Java 9 and later)_. See [IDEA-288052][idea-288052] for
|
||||
details.
|
||||
|
||||
## 💡 How it works
|
||||
|
||||
This project provides additional [`BugChecker`][error-prone-bugchecker]
|
||||
implementations.
|
||||
|
||||
<!-- XXX: Extend this section. -->
|
||||
|
||||
## ✍️ Contributing
|
||||
|
||||
Want to report or fix a bug, suggest or add a new feature, or improve the
|
||||
documentation? That's awesome! Please read our [contribution
|
||||
guidelines][contributing].
|
||||
|
||||
[bug-checks]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/
|
||||
[bug-checks-identity-conversion]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IdentityConversion.java
|
||||
[contributing]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/CONTRIBUTING.md
|
||||
[error-prone-bugchecker]: https://github.com/google/error-prone/blob/master/check_api/src/main/java/com/google/errorprone/bugpatterns/BugChecker.java
|
||||
[error-prone-fork-jitpack]: https://jitpack.io/#PicnicSupermarket/error-prone
|
||||
[error-prone-fork-repo]: https://github.com/PicnicSupermarket/error-prone
|
||||
[error-prone-installation-guide]: https://errorprone.info/docs/installation#maven
|
||||
[error-prone-orig-repo]: https://github.com/google/error-prone
|
||||
[error-prone-pull-3301]: https://github.com/google/error-prone/pull/3301
|
||||
[github-actions-build-badge]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/build.yaml/badge.svg
|
||||
[github-actions-build-master]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/build.yaml?query=branch%3Amaster
|
||||
[google-java-format]: https://github.com/google/google-java-format
|
||||
[idea-288052]: https://youtrack.jetbrains.com/issue/IDEA-288052
|
||||
[license-badge]: https://img.shields.io/github/license/PicnicSupermarket/error-prone-support
|
||||
[license]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/LICENSE.md
|
||||
[maven-central-badge]: https://img.shields.io/maven-central/v/tech.picnic.error-prone-support/error-prone-support?color=blue
|
||||
[maven-central-search]: https://search.maven.org/artifact/tech.picnic.error-prone-support/error-prone-support
|
||||
[maven]: https://maven.apache.org
|
||||
[picnic-blog]: https://blog.picnic.nl
|
||||
[picnic-blog-ep-post]: https://blog.picnic.nl/picnic-loves-error-prone-producing-high-quality-and-consistent-java-code-b8a566be6886
|
||||
[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-rules-bigdecimal]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/BigDecimalTemplates.java
|
||||
[refaster-rules]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/
|
||||
162
docgen/pom.xml
Normal file
@@ -0,0 +1,162 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2011 The Error Prone Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.2.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Picnic :: Error Prone Support :: DocGen</name>
|
||||
<artifactId>error_prone_docgen</artifactId>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>com.google.auto.value</groupId>
|
||||
<artifactId>auto-value</artifactId>
|
||||
<version>${version.auto-value}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>com.google.auto.service</groupId>
|
||||
<artifactId>auto-service</artifactId>
|
||||
<version>${version.auto-service}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/java</directory>
|
||||
<includes>
|
||||
<include>**/*.mustache</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${groupId.error-prone}</groupId>
|
||||
<artifactId>error_prone_annotation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${groupId.error-prone}</groupId>
|
||||
<artifactId>error_prone_core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>error-prone-contrib</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error_prone_docgen_processor</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>1.30</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.beust</groupId>
|
||||
<artifactId>jcommander</artifactId>
|
||||
<version>1.82</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto.value</groupId>
|
||||
<artifactId>auto-value-annotations</artifactId>
|
||||
<version>${version.auto-value}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.spullara.mustache.java</groupId>
|
||||
<artifactId>compiler</artifactId>
|
||||
<version>0.9.10</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.truth</groupId>
|
||||
<artifactId>truth</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>run-annotation-processor</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>site</phase>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<mainClass>com.google.errorprone.DocGenTool</mainClass>
|
||||
<arguments>
|
||||
<argument>
|
||||
-bug_patterns=${basedir}/../error-prone-contrib/target/generated-sources/annotations/bugPatterns.txt
|
||||
</argument>
|
||||
<argument>-docs_repository=${basedir}/target/generated-wiki/</argument>
|
||||
<argument>-explanations=${basedir}/../docs/bugpatterns/</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2014 The Error Prone Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.errorprone;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.github.mustachejava.DefaultMustacheFactory;
|
||||
import com.github.mustachejava.Mustache;
|
||||
import com.github.mustachejava.MustacheFactory;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.LineProcessor;
|
||||
import com.google.errorprone.BugPattern.SeverityLevel;
|
||||
import com.google.gson.Gson;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Reads each line of the bugpatterns.txt tab-delimited data file, and generates a GitHub Jekyll
|
||||
* page for each one.
|
||||
*
|
||||
* @author alexeagle@google.com (Alex Eagle)
|
||||
*/
|
||||
class BugPatternFileGenerator implements LineProcessor<List<BugPatternInstance>> {
|
||||
|
||||
private final Path outputDir;
|
||||
private final Path explanationDir;
|
||||
private final List<BugPatternInstance> result;
|
||||
|
||||
private final Function<BugPatternInstance, SeverityLevel> severityRemapper;
|
||||
|
||||
/** The base url for links to bugpatterns. */
|
||||
@Nullable private final String baseUrl;
|
||||
|
||||
public BugPatternFileGenerator(
|
||||
Path bugpatternDir,
|
||||
Path explanationDir,
|
||||
String baseUrl,
|
||||
Function<BugPatternInstance, SeverityLevel> severityRemapper) {
|
||||
this.outputDir = bugpatternDir;
|
||||
this.explanationDir = explanationDir;
|
||||
this.severityRemapper = severityRemapper;
|
||||
this.baseUrl = baseUrl;
|
||||
result = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processLine(String line) throws IOException {
|
||||
BugPatternInstance pattern = new Gson().fromJson(line, BugPatternInstance.class);
|
||||
pattern.severity = severityRemapper.apply(pattern);
|
||||
result.add(pattern);
|
||||
|
||||
// replace spaces in filename with underscores
|
||||
Path checkPath = Paths.get(pattern.name.replace(' ', '_') + ".md");
|
||||
|
||||
try (Writer writer = Files.newBufferedWriter(outputDir.resolve(checkPath), UTF_8)) {
|
||||
|
||||
// load side-car explanation file, if it exists
|
||||
Path sidecarExplanation = explanationDir.resolve(checkPath);
|
||||
if (Files.exists(sidecarExplanation)) {
|
||||
if (!pattern.explanation.isEmpty()) {
|
||||
throw new AssertionError(
|
||||
String.format(
|
||||
"%s specifies an explanation via @BugPattern and side-car", pattern.name));
|
||||
}
|
||||
pattern.explanation = Files.readString(sidecarExplanation).trim();
|
||||
}
|
||||
|
||||
// Construct an appropriate page for this {@code BugPattern}. Include altNames if
|
||||
// there are any, and explain the correct way to suppress.
|
||||
|
||||
ImmutableMap.Builder<String, Object> templateData =
|
||||
ImmutableMap.<String, Object>builder()
|
||||
.put(
|
||||
"tags", Arrays.stream(pattern.tags).map(Style::styleTag).collect(joining("\n\n")))
|
||||
.put("severity", Style.styleSeverity(pattern.severity))
|
||||
.put("name", pattern.name)
|
||||
.put("bugpattern", String.format("%s.java", pattern.className.replace(".", "/")))
|
||||
.put("className", pattern.className)
|
||||
.put("summary", pattern.summary.trim())
|
||||
.put("altNames", Joiner.on(", ").join(pattern.altNames))
|
||||
.put("explanation", pattern.explanation.trim());
|
||||
|
||||
if (pattern.sampleInput != null && pattern.sampleOutput != null) {
|
||||
templateData.put("hasSamples", true);
|
||||
templateData.put("sampleInput", pattern.sampleInput);
|
||||
templateData.put("sampleOutput", pattern.sampleOutput);
|
||||
}
|
||||
|
||||
if (baseUrl != null) {
|
||||
templateData.put("baseUrl", baseUrl);
|
||||
}
|
||||
|
||||
if (pattern.documentSuppression) {
|
||||
String suppressionString;
|
||||
if (pattern.suppressionAnnotations.length == 0) {
|
||||
suppressionString = "This check may not be suppressed.";
|
||||
} else {
|
||||
suppressionString =
|
||||
pattern.suppressionAnnotations.length == 1
|
||||
? "Suppress false positives by adding the suppression annotation %s to the "
|
||||
+ "enclosing element."
|
||||
: "Suppress false positives by adding one of these suppression annotations to "
|
||||
+ "the enclosing element: %s";
|
||||
suppressionString =
|
||||
String.format(
|
||||
suppressionString,
|
||||
Arrays.stream(pattern.suppressionAnnotations)
|
||||
.map((String anno) -> standardizeAnnotation(anno, pattern.name))
|
||||
.collect(joining(", ")));
|
||||
}
|
||||
templateData.put("suppression", suppressionString);
|
||||
}
|
||||
|
||||
MustacheFactory mf = new DefaultMustacheFactory();
|
||||
Mustache mustache = mf.compile("com/google/errorprone/resources/bugpattern.mustache");
|
||||
mustache.execute(writer, templateData.buildOrThrow());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String standardizeAnnotation(String fullAnnotationName, String patternName) {
|
||||
String annotationName =
|
||||
fullAnnotationName.endsWith(".class")
|
||||
? fullAnnotationName.substring(0, fullAnnotationName.length() - ".class".length())
|
||||
: fullAnnotationName;
|
||||
if (annotationName.equals(SuppressWarnings.class.getName())) {
|
||||
annotationName = SuppressWarnings.class.getSimpleName() + "(\"" + patternName + "\")";
|
||||
}
|
||||
return "`@" + annotationName + "`";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BugPatternInstance> getResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
112
docgen/src/main/java/com/google/errorprone/DocGenTool.java
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2015 The Error Prone Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.errorprone;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.io.Files.asCharSource;
|
||||
import static com.google.errorprone.scanner.BuiltInCheckerSuppliers.ENABLED_ERRORS;
|
||||
import static com.google.errorprone.scanner.BuiltInCheckerSuppliers.ENABLED_WARNINGS;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.beust.jcommander.IStringConverter;
|
||||
import com.beust.jcommander.JCommander;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* Utility main which consumes the same tab-delimited text file and generates GitHub pages for the
|
||||
* BugPatterns.
|
||||
*/
|
||||
public final class DocGenTool {
|
||||
|
||||
@Parameters(separators = "=")
|
||||
static class Options {
|
||||
@Parameter(names = "-bug_patterns", description = "Path to bugPatterns.txt", required = true)
|
||||
private String bugPatterns;
|
||||
|
||||
@Parameter(
|
||||
names = "-explanations",
|
||||
description = "Path to side-car explanations",
|
||||
required = true)
|
||||
private String explanations;
|
||||
|
||||
@Parameter(names = "-docs_repository", description = "Path to docs repository", required = true)
|
||||
private String docsRepository;
|
||||
|
||||
@Parameter(
|
||||
names = "-base_url",
|
||||
description = "The base url for links to bugpatterns",
|
||||
arity = 1)
|
||||
private String baseUrl = null;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
Options options = new Options();
|
||||
new JCommander(options).parse(args);
|
||||
|
||||
Path bugPatterns = Paths.get(options.bugPatterns);
|
||||
if (!Files.exists(bugPatterns)) {
|
||||
usage("Cannot find bugPatterns file: " + options.bugPatterns);
|
||||
}
|
||||
Path explanationDir = Paths.get(options.explanations);
|
||||
if (!Files.exists(explanationDir)) {
|
||||
usage("Cannot find explanations dir: " + options.explanations);
|
||||
}
|
||||
Path wikiDir = Paths.get(options.docsRepository);
|
||||
Files.createDirectories(wikiDir);
|
||||
Path bugpatternDir = wikiDir.resolve("bugpatterns");
|
||||
if (!Files.exists(bugpatternDir)) {
|
||||
Files.createDirectories(bugpatternDir);
|
||||
}
|
||||
BugPatternFileGenerator generator =
|
||||
new BugPatternFileGenerator(
|
||||
bugpatternDir,
|
||||
explanationDir,
|
||||
options.baseUrl,
|
||||
input -> input.severity);
|
||||
try (Writer w =
|
||||
Files.newBufferedWriter(wikiDir.resolve("bugpatterns.md"), StandardCharsets.UTF_8)) {
|
||||
List<BugPatternInstance> patterns =
|
||||
asCharSource(bugPatterns.toFile(), UTF_8).readLines(generator);
|
||||
}
|
||||
}
|
||||
|
||||
private static ImmutableSet<String> enabledCheckNames() {
|
||||
return StreamSupport.stream(
|
||||
Iterables.concat(ENABLED_ERRORS, ENABLED_WARNINGS).spliterator(), false)
|
||||
.map(BugCheckerInfo::canonicalName)
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
private static void usage(String err) {
|
||||
System.err.println(err);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
private DocGenTool() {}
|
||||
}
|
||||
27
docgen/src/main/java/com/google/errorprone/Style.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.google.errorprone;
|
||||
|
||||
import com.google.errorprone.BugPattern.SeverityLevel;
|
||||
|
||||
public final class Style {
|
||||
|
||||
public static String styleSeverity (SeverityLevel severityLevel) {
|
||||
return String.format("%s\n {: .label .label-%s}", severityLevel.toString(), getSeverityLabelColour(severityLevel));
|
||||
}
|
||||
|
||||
private static String getSeverityLabelColour (SeverityLevel severityLevel) {
|
||||
switch (severityLevel) {
|
||||
case ERROR:
|
||||
return "red";
|
||||
case WARNING:
|
||||
return "yellow";
|
||||
case SUGGESTION:
|
||||
return "green";
|
||||
default:
|
||||
return "blue";
|
||||
}
|
||||
}
|
||||
|
||||
public static String styleTag (String tagName) {
|
||||
return String.format("%s\n {: .label }", tagName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
layout: default
|
||||
title: {{name}}
|
||||
description: "{{{summary}}}"
|
||||
parent: Bug Patterns
|
||||
nav_order: 1
|
||||
---
|
||||
<!--
|
||||
*** AUTO-GENERATED, DO NOT MODIFY ***
|
||||
To make changes, edit the @BugPattern annotation or the explanation in docs/bugpattern.
|
||||
-->
|
||||
|
||||
# {{name}}
|
||||
|
||||
{{{severity}}}
|
||||
{{{tags}}}
|
||||
|
||||
{: .summary }
|
||||
{{{summary}}}
|
||||
|
||||
{{{explanation}}}
|
||||
|
||||
{{#suppression}}
|
||||
## Suppression
|
||||
{{{suppression}}}
|
||||
{{/suppression}}
|
||||
|
||||
{{#hasSamples}}
|
||||
## Samples
|
||||
|
||||
### Before
|
||||
|
||||
```java
|
||||
{{{sampleInput}}}
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```java
|
||||
{{{sampleOutput}}}
|
||||
```
|
||||
{{/hasSamples}}
|
||||
|
||||
<a href="https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/{{bugpattern}}" class="fs-3 btn external"
|
||||
target="_blank">
|
||||
View source code on GitHub
|
||||
<svg viewBox="0 0 24 24" aria-labelledby="svg-external-link-title">
|
||||
<use xlink:href="#svg-external-link"></use>
|
||||
</svg>
|
||||
</a>
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2014 The Error Prone Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.errorprone;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.errorprone.BugPattern.SeverityLevel;
|
||||
import com.google.gson.Gson;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class BugPatternFileGeneratorTest {
|
||||
|
||||
@Rule public TemporaryFolder tmpfolder = new TemporaryFolder();
|
||||
|
||||
private Path wikiDir;
|
||||
private Path explanationDirBase;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
wikiDir = tmpfolder.newFolder("wiki").toPath();
|
||||
explanationDirBase = tmpfolder.newFolder("explanations").toPath();
|
||||
}
|
||||
|
||||
private static BugPatternInstance deadExceptionTestInfo() {
|
||||
BugPatternInstance instance = new BugPatternInstance();
|
||||
instance.className = "com.google.errorprone.bugpatterns.DeadException";
|
||||
instance.name = "DeadException";
|
||||
instance.summary = "Exception created but not thrown";
|
||||
instance.explanation =
|
||||
"The exception is created with new, but is not thrown, and the reference is lost.";
|
||||
instance.altNames = new String[] {"ThrowableInstanceNeverThrown"};
|
||||
instance.tags = new String[] {"LikelyError"};
|
||||
instance.severity = SeverityLevel.ERROR;
|
||||
instance.suppressionAnnotations = new String[] {"java.lang.SuppressWarnings.class"};
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static final String BUGPATTERN_LINE;
|
||||
|
||||
static {
|
||||
BugPatternInstance instance = deadExceptionTestInfo();
|
||||
BUGPATTERN_LINE = new Gson().toJson(instance);
|
||||
}
|
||||
|
||||
private static final String BUGPATTERN_LINE_SIDECAR;
|
||||
|
||||
static {
|
||||
BugPatternInstance instance = deadExceptionTestInfo();
|
||||
instance.explanation = "";
|
||||
BUGPATTERN_LINE_SIDECAR = new Gson().toJson(instance);
|
||||
}
|
||||
|
||||
// Assert that the generator produces the same output it did before.
|
||||
// This is brittle, but you can open the golden file
|
||||
// src/test/resources/com/google/errorprone/DeadException.md
|
||||
// in the same Jekyll environment you use for prod, and verify it looks good.
|
||||
@Test
|
||||
public void regressionTest_frontmatter_pygments() throws Exception {
|
||||
BugPatternFileGenerator generator =
|
||||
new BugPatternFileGenerator(
|
||||
wikiDir, explanationDirBase, null, input -> input.severity);
|
||||
generator.processLine(BUGPATTERN_LINE);
|
||||
String expected =
|
||||
CharStreams.toString(
|
||||
new InputStreamReader(
|
||||
getClass().getResourceAsStream("testdata/DeadException_frontmatter_pygments.md"),
|
||||
UTF_8));
|
||||
String actual =
|
||||
CharStreams.toString(Files.newBufferedReader(wikiDir.resolve("DeadException.md"), UTF_8));
|
||||
assertThat(actual.trim()).isEqualTo(expected.trim());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void regressionTest_nofrontmatter_gfm() throws Exception {
|
||||
BugPatternFileGenerator generator =
|
||||
new BugPatternFileGenerator(
|
||||
wikiDir, explanationDirBase, null, input -> input.severity);
|
||||
generator.processLine(BUGPATTERN_LINE);
|
||||
String expected =
|
||||
CharStreams.toString(
|
||||
new InputStreamReader(
|
||||
getClass().getResourceAsStream("testdata/DeadException_nofrontmatter_gfm.md"),
|
||||
UTF_8));
|
||||
String actual = new String(Files.readAllBytes(wikiDir.resolve("DeadException.md")), UTF_8);
|
||||
assertThat(actual.trim()).isEqualTo(expected.trim());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void regressionTest_sidecar() throws Exception {
|
||||
BugPatternFileGenerator generator =
|
||||
new BugPatternFileGenerator(
|
||||
wikiDir, explanationDirBase, null, input -> input.severity);
|
||||
Files.write(
|
||||
explanationDirBase.resolve("DeadException.md"),
|
||||
Arrays.asList(
|
||||
"The exception is created with new, but is not thrown, and the reference is lost."),
|
||||
UTF_8);
|
||||
generator.processLine(BUGPATTERN_LINE_SIDECAR);
|
||||
String expected =
|
||||
CharStreams.toString(
|
||||
new InputStreamReader(
|
||||
getClass().getResourceAsStream("testdata/DeadException_nofrontmatter_gfm.md"),
|
||||
UTF_8));
|
||||
String actual = new String(Files.readAllBytes(wikiDir.resolve("DeadException.md")), UTF_8);
|
||||
assertThat(actual.trim()).isEqualTo(expected.trim());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEscapeAngleBracketsInSummary() throws Exception {
|
||||
// Create a BugPattern with angle brackets in the summary
|
||||
BugPatternInstance instance = new BugPatternInstance();
|
||||
instance.className = "com.google.errorprone.bugpatterns.DontDoThis";
|
||||
instance.name = "DontDoThis";
|
||||
instance.summary = "Don't do this; do List<Foo> instead";
|
||||
instance.explanation = "This is a bad idea, you want `List<Foo>` instead";
|
||||
instance.altNames = new String[0];
|
||||
instance.tags = new String[] {"LikelyError"};
|
||||
instance.severity = SeverityLevel.ERROR;
|
||||
instance.suppressionAnnotations = new String[] {"java.lang.SuppressWarnings.class"};
|
||||
|
||||
// Write markdown file
|
||||
BugPatternFileGenerator generator =
|
||||
new BugPatternFileGenerator(
|
||||
wikiDir, explanationDirBase, null, input -> input.severity);
|
||||
generator.processLine(new Gson().toJson(instance));
|
||||
String expected =
|
||||
CharStreams.toString(
|
||||
new InputStreamReader(
|
||||
getClass().getResourceAsStream("testdata/DontDoThis_nofrontmatter_gfm.md"), UTF_8));
|
||||
String actual = new String(Files.readAllBytes(wikiDir.resolve("DontDoThis.md")), UTF_8);
|
||||
assertThat(actual.trim()).isEqualTo(expected.trim());
|
||||
}
|
||||
}
|
||||
20
docgen/src/test/java/com/google/errorprone/testdata/DeadException_frontmatter_pygments.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: DeadException
|
||||
summary: Exception created but not thrown
|
||||
layout: bugpattern
|
||||
tags: LikelyError
|
||||
severity: ERROR
|
||||
---
|
||||
|
||||
<!--
|
||||
*** AUTO-GENERATED, DO NOT MODIFY ***
|
||||
To make changes, edit the @BugPattern annotation or the explanation in docs/bugpattern.
|
||||
-->
|
||||
|
||||
_Alternate names: ThrowableInstanceNeverThrown_
|
||||
|
||||
## The problem
|
||||
The exception is created with new, but is not thrown, and the reference is lost.
|
||||
|
||||
## Suppression
|
||||
Suppress false positives by adding the suppression annotation `@SuppressWarnings("DeadException")` to the enclosing element.
|
||||
21
docgen/src/test/java/com/google/errorprone/testdata/DeadException_nofrontmatter_gfm.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
*** AUTO-GENERATED, DO NOT MODIFY ***
|
||||
To make changes, edit the @BugPattern annotation or the explanation in docs/bugpattern.
|
||||
-->
|
||||
|
||||
# DeadException
|
||||
|
||||
__Exception created but not thrown__
|
||||
|
||||
<div style="float:right;"><table id="metadata">
|
||||
<tr><td>Severity</td><td>ERROR</td></tr>
|
||||
<tr><td>Tags</td><td>LikelyError</td></tr>
|
||||
</table></div>
|
||||
|
||||
_Alternate names: ThrowableInstanceNeverThrown_
|
||||
|
||||
## The problem
|
||||
The exception is created with new, but is not thrown, and the reference is lost.
|
||||
|
||||
## Suppression
|
||||
Suppress false positives by adding the suppression annotation `@SuppressWarnings("DeadException")` to the enclosing element.
|
||||
20
docgen/src/test/java/com/google/errorprone/testdata/DontDoThis_nofrontmatter_gfm.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<!--
|
||||
*** AUTO-GENERATED, DO NOT MODIFY ***
|
||||
To make changes, edit the @BugPattern annotation or the explanation in docs/bugpattern.
|
||||
-->
|
||||
|
||||
# DontDoThis
|
||||
|
||||
__Don't do this; do List<Foo> instead__
|
||||
|
||||
<div style="float:right;"><table id="metadata">
|
||||
<tr><td>Severity</td><td>ERROR</td></tr>
|
||||
<tr><td>Tags</td><td>LikelyError</td></tr>
|
||||
</table></div>
|
||||
|
||||
|
||||
## The problem
|
||||
This is a bad idea, you want `List<Foo>` instead
|
||||
|
||||
## Suppression
|
||||
Suppress false positives by adding the suppression annotation `@SuppressWarnings("DontDoThis")` to the enclosing element.
|
||||
94
docgen_processor/pom.xml
Normal file
@@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2011 The Error Prone Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.2.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Picnic :: Error Prone Support :: DocGen Processor</name>
|
||||
<artifactId>error_prone_docgen_processor</artifactId>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${groupId.error-prone}</groupId>
|
||||
<artifactId>error_prone_annotation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${groupId.error-prone}</groupId>
|
||||
<artifactId>error_prone_core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto.service</groupId>
|
||||
<artifactId>auto-service-annotations</artifactId>
|
||||
<version>${version.auto-service}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.googlejavaformat</groupId>
|
||||
<artifactId>google-java-format</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-text</artifactId>
|
||||
<version>1.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>com.google.auto.service</groupId>
|
||||
<artifactId>auto-service</artifactId>
|
||||
<version>${version.auto-service}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2015 The Error Prone Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.errorprone;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.errorprone.BugPattern.SeverityLevel;
|
||||
import com.google.googlejavaformat.java.Formatter;
|
||||
import com.google.googlejavaformat.java.FormatterException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
|
||||
/** A serialization-friendly POJO of the information in a {@link BugPattern}. */
|
||||
public final class BugPatternInstance {
|
||||
private static final Formatter FORMATTER = new Formatter();
|
||||
|
||||
private static final Pattern INPUT_LINES_PATTERN = Pattern.compile("\\.addInputLines\\((.*?)\\)\n", Pattern.DOTALL);
|
||||
private static final Pattern OUTPUT_LINES_PATTERN = Pattern.compile("\\.addOutputLines\\((.*?)\\)\n", Pattern.DOTALL);
|
||||
private static final Pattern LINES_PATTERN = Pattern.compile("\"(.*)\"");
|
||||
|
||||
public String className;
|
||||
public String name;
|
||||
public String summary;
|
||||
public String explanation;
|
||||
public String[] altNames;
|
||||
public String category;
|
||||
public String[] tags;
|
||||
public SeverityLevel severity;
|
||||
public String[] suppressionAnnotations;
|
||||
public boolean documentSuppression = true;
|
||||
|
||||
public String testContent;
|
||||
public String sampleInput;
|
||||
public String sampleOutput;
|
||||
|
||||
public static BugPatternInstance fromElement(Element element) {
|
||||
BugPatternInstance instance = new BugPatternInstance();
|
||||
instance.className = element.toString();
|
||||
|
||||
BugPattern annotation = element.getAnnotation(BugPattern.class);
|
||||
instance.name = annotation.name().isEmpty() ? element.getSimpleName().toString() : annotation.name();
|
||||
instance.altNames = annotation.altNames();
|
||||
instance.tags = annotation.tags();
|
||||
instance.severity = annotation.severity();
|
||||
instance.summary = annotation.summary();
|
||||
instance.explanation = annotation.explanation();
|
||||
instance.documentSuppression = annotation.documentSuppression();
|
||||
|
||||
Map<String, Object> keyValues = getAnnotation(element, BugPattern.class.getName());
|
||||
Object suppression = keyValues.get("suppressionAnnotations");
|
||||
if (suppression == null) {
|
||||
instance.suppressionAnnotations = new String[] { SuppressWarnings.class.getName() };
|
||||
} else {
|
||||
Preconditions.checkState(suppression instanceof List);
|
||||
@SuppressWarnings("unchecked") // Always List<? extends AnnotationValue>, see above.
|
||||
List<? extends AnnotationValue> resultList = (List<? extends AnnotationValue>) suppression;
|
||||
instance.suppressionAnnotations = resultList.stream().map(AnnotationValue::toString).toArray(String[]::new);
|
||||
}
|
||||
|
||||
Path testPath = getPath(instance);
|
||||
System.out.println("test class for " + instance.name + " = " + testPath.toAbsolutePath());
|
||||
|
||||
try {
|
||||
instance.testContent = String.join("\n", Files.readAllLines(testPath));
|
||||
instance.sampleInput = getInputLines(instance.testContent);
|
||||
instance.sampleOutput = getOutputLines(instance.testContent);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static Path getPath(BugPatternInstance instance) {
|
||||
return Path.of(
|
||||
"error-prone-contrib/src/test/java/"
|
||||
+ instance.className.replace(".", "/")
|
||||
+ "Test.java");
|
||||
}
|
||||
|
||||
private static String getInputLines(String content) {
|
||||
return getLines(INPUT_LINES_PATTERN, content);
|
||||
}
|
||||
|
||||
private static String getOutputLines(String content) {
|
||||
return getLines(OUTPUT_LINES_PATTERN, content);
|
||||
}
|
||||
|
||||
private static String getLines(Pattern pattern, String content) {
|
||||
Matcher match = pattern.matcher(content);
|
||||
|
||||
if (!match.find()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String argument = match.group(1);
|
||||
|
||||
List<String> lines = findAllGroups(argument, LINES_PATTERN);
|
||||
// Remove in/A.java and out/A.java
|
||||
lines.remove(0);
|
||||
|
||||
String sampleCode = String.join("\n", lines);
|
||||
sampleCode = StringEscapeUtils.unescapeJava(sampleCode);
|
||||
|
||||
try {
|
||||
// Trim to remove trailing line-break.
|
||||
return FORMATTER.formatSource(sampleCode).trim();
|
||||
} catch (FormatterException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> findAllGroups(String text, Pattern pattern) {
|
||||
List<String> list = new ArrayList<>();
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
while (matcher.find()) {
|
||||
for (int i = 1; i <= matcher.groupCount(); i++) {
|
||||
list.add(matcher.group(i));
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static Map<String, Object> getAnnotation(Element element, String name) {
|
||||
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
|
||||
if (mirror.getAnnotationType().toString().equals(name)) {
|
||||
return annotationKeyValues(mirror);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(String.format("%s has no annotation %s", element, name));
|
||||
}
|
||||
|
||||
private static Map<String, Object> annotationKeyValues(AnnotationMirror mirror) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
for (ExecutableElement key : mirror.getElementValues().keySet()) {
|
||||
result.put(key.getSimpleName().toString(), mirror.getElementValues().get(key).getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2011 The Error Prone Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.errorprone;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.googlejavaformat.java.Formatter;
|
||||
import com.google.gson.Gson;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.Processor;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
|
||||
/**
|
||||
* Annotation processor which visits all classes that have a {@code BugPattern} annotation, and
|
||||
* writes a tab-delimited text file dumping the data found.
|
||||
*
|
||||
* @author eaftan@google.com (Eddie Aftandilian)
|
||||
* @author alexeagle@google.com (Alex Eagle)
|
||||
*/
|
||||
@AutoService(Processor.class)
|
||||
@SupportedAnnotationTypes("com.google.errorprone.BugPattern")
|
||||
public class DocGenProcessor extends AbstractProcessor {
|
||||
@Override
|
||||
public SourceVersion getSupportedSourceVersion() {
|
||||
return SourceVersion.latest();
|
||||
}
|
||||
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
private PrintWriter pw;
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment processingEnv) {
|
||||
super.init(processingEnv);
|
||||
try {
|
||||
FileObject manifest =
|
||||
processingEnv
|
||||
.getFiler()
|
||||
.createResource(StandardLocation.SOURCE_OUTPUT, "", "bugPatterns.txt");
|
||||
pw = new PrintWriter(new OutputStreamWriter(manifest.openOutputStream(), UTF_8), true);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
for (Element element : roundEnv.getElementsAnnotatedWith(BugPattern.class)) {
|
||||
System.out.println("[DOCGEN] HANDLING: " + element.getSimpleName());
|
||||
gson.toJson(BugPatternInstance.fromElement(element), pw);
|
||||
pw.println();
|
||||
}
|
||||
|
||||
if (roundEnv.processingOver()) {
|
||||
// this was the last round, do cleanup
|
||||
cleanup();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Perform cleanup after last round of annotation processing. */
|
||||
private void cleanup() {
|
||||
pw.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package tech.picnic.errorprone.docgen;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.googlejavaformat.java.Formatter;
|
||||
import com.google.googlejavaformat.java.FormatterException;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ExampleExtractorTest {
|
||||
|
||||
private static final String INPUT =
|
||||
String.join("\n",
|
||||
"@Test",
|
||||
"void replacementFirstSuggestedFix() {",
|
||||
" refactoringTestHelper",
|
||||
" .addInputLines(",
|
||||
" \"A.java\",",
|
||||
" \"import static java.util.stream.Collectors.toList;\",",
|
||||
" \"import static java.util.stream.Collectors.toMap;\",",
|
||||
" \"import static java.util.stream.Collectors.toSet;\",",
|
||||
" \"\",",
|
||||
" \"import java.util.stream.Collectors;\",",
|
||||
" \"import java.util.stream.Stream;\",",
|
||||
" \"import reactor.core.publisher.Flux;\",",
|
||||
" \"\",",
|
||||
" \"class A {\",",
|
||||
" \" void m() {\",",
|
||||
" \" Flux.just(1).collect(Collectors.toList());\",",
|
||||
" \" Flux.just(2).collect(toList());\",",
|
||||
" \"\",",
|
||||
" \" Stream.of(\"foo\").collect(Collectors.toMap(String::getBytes, String::length));\",",
|
||||
" \" Stream.of(\"bar\").collect(toMap(String::getBytes, String::length));\",",
|
||||
" \" Flux.just(\"baz\").collect(Collectors.toMap(String::getBytes, String::length, (a, b) -> b));\",",
|
||||
" \" Flux.just(\"qux\").collect(toMap(String::getBytes, String::length, (a, b) -> b));\",",
|
||||
" \"\",",
|
||||
" \" Stream.of(1).collect(Collectors.toSet());\",",
|
||||
" \" Stream.of(2).collect(toSet());\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .addOutputLines(",
|
||||
" \"A.java\",",
|
||||
" \"import static com.google.common.collect.ImmutableList.toImmutableList;\",",
|
||||
" \"import static com.google.common.collect.ImmutableMap.toImmutableMap;\",",
|
||||
" \"import static com.google.common.collect.ImmutableSet.toImmutableSet;\",",
|
||||
" \"import static java.util.stream.Collectors.toList;\",",
|
||||
" \"import static java.util.stream.Collectors.toMap;\",",
|
||||
" \"import static java.util.stream.Collectors.toSet;\",",
|
||||
" \"\",",
|
||||
" \"import java.util.stream.Collectors;\",",
|
||||
" \"import java.util.stream.Stream;\",",
|
||||
" \"import reactor.core.publisher.Flux;\",",
|
||||
" \"\",",
|
||||
" \"class A {\",",
|
||||
" \" void m() {\",",
|
||||
" \" Flux.just(1).collect(toImmutableList());\",",
|
||||
" \" Flux.just(2).collect(toImmutableList());\",",
|
||||
" \"\",",
|
||||
" \" Stream.of(\"foo\").collect(toImmutableMap(String::getBytes, String::length));\",",
|
||||
" \" Stream.of(\"bar\").collect(toImmutableMap(String::getBytes, String::length));\",",
|
||||
" \" Flux.just(\"baz\").collect(toImmutableMap(String::getBytes, String::length, (a, b) -> b));\",",
|
||||
" \" Flux.just(\"qux\").collect(toImmutableMap(String::getBytes, String::length, (a, b) -> b));\",",
|
||||
" \"\",",
|
||||
" \" Stream.of(1).collect(toImmutableSet());\",",
|
||||
" \" Stream.of(2).collect(toImmutableSet());\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .doTest(TestMode.TEXT_MATCH);",
|
||||
"}");
|
||||
|
||||
@Test
|
||||
void regexTest() throws FormatterException {
|
||||
final Formatter FORMATTER = new Formatter();
|
||||
Pattern pattern =
|
||||
Pattern.compile("\\.addInputLines\\((\n.*?\".*?\",)\n(.*?)\\)\n", Pattern.DOTALL);
|
||||
Matcher matcher = pattern.matcher(INPUT);
|
||||
int count = matcher.groupCount();
|
||||
if(!matcher.find()) {
|
||||
System.out.println("no match!");
|
||||
return;
|
||||
}
|
||||
|
||||
String src = matcher.group(2);
|
||||
System.out.println("\\\"foo\\\"".replaceAll("\\\\\"(.*?)\\\\\"", "\"$1\""));
|
||||
}
|
||||
}
|
||||
1
docs/bugpatterns/EmptyMethod.md
Normal file
@@ -0,0 +1 @@
|
||||
There's not much use to keep empty methods.
|
||||
12
docs/refasterrules/BigDecimalTemplates.md
Normal file
@@ -0,0 +1,12 @@
|
||||
## Problem
|
||||
|
||||
The results of the `BigDecimal` constructor can be somewhat unpredictable. One
|
||||
might assume that writing `new BigDecimal(0.1)` in Java creates a `BigDecimal`
|
||||
which is exactly equal to `0.1` (an unscaled value of `1`, with a scale of
|
||||
`1`), but it is actually equal to
|
||||
`0.1000000000000000055511151231257827021181583404541015625`.
|
||||
|
||||
This is because
|
||||
`0.1` cannot be represented exactly as a `double` (or, for that matter, as a
|
||||
binary fraction of any finite length). Thus, the value that is being passed in
|
||||
to the constructor is not exactly equal to `0.1`, appearances notwithstanding.
|
||||
@@ -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
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.1.1-SNAPSHOT</version>
|
||||
<version>0.2.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>error-prone-contrib</artifactId>
|
||||
@@ -69,11 +70,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 +124,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 +139,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>
|
||||
@@ -236,11 +232,57 @@
|
||||
<ignoredUnusedDeclaredDependencies>
|
||||
<!-- XXX: Figure out why the plugin thinks this
|
||||
dependency is unused. -->
|
||||
<ignoredUnusedDeclaredDependency>${project.groupId}:refaster-support</ignoredUnusedDeclaredDependency>
|
||||
<ignoredUnusedDeclaredDependency>${project.groupId}:refaster-support
|
||||
</ignoredUnusedDeclaredDependency>
|
||||
</ignoredUnusedDeclaredDependencies>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<!-- run annotation processor -->
|
||||
<profile>
|
||||
<id>run-annotation-processor</id>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>error_prone_docgen_processor</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>com.google.auto.value</groupId>
|
||||
<artifactId>auto-value</artifactId>
|
||||
<version>${version.auto-value}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>com.google.auto.service</groupId>
|
||||
<artifactId>auto-service</artifactId>
|
||||
<version>${version.auto-service}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>error_prone_docgen_processor</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
98
generate-docs.sh
Executable file
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
WEBSITE_FOLDER="website"
|
||||
DOCS_FOLDER="docs"
|
||||
|
||||
BUGPATTERN_FOLDER="${WEBSITE_FOLDER}/bugpatterns"
|
||||
BUGPATTERN_DOCS_FOLDER="${DOCS_FOLDER}/bugpatterns"
|
||||
|
||||
REFASTER_FOLDER="${WEBSITE_FOLDER}/refasterrules"
|
||||
REFASTER_DOCS_FOLDER="${DOCS_FOLDER}/refasterrules"
|
||||
|
||||
HOMEPAGE="${WEBSITE_FOLDER}/index.md"
|
||||
|
||||
configure() {
|
||||
cd "$(git rev-parse --show-toplevel || echo .)"
|
||||
mkdir "${BUGPATTERN_FOLDER}" 2>/dev/null
|
||||
mkdir "${REFASTER_FOLDER}" 2>/dev/null
|
||||
}
|
||||
|
||||
generate_homepage() {
|
||||
echo "Generating ${HOMEPAGE}"
|
||||
cat > "${HOMEPAGE}" << EOF
|
||||
---
|
||||
# Do not modify. This file is generated.
|
||||
layout: default
|
||||
title: Home
|
||||
nav_order: 1
|
||||
---
|
||||
EOF
|
||||
|
||||
cat "README.md" >> ${HOMEPAGE}
|
||||
|
||||
SEDOPTION="-i"
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
SEDOPTION="-i .bak"
|
||||
fi
|
||||
sed $SEDOPTION 's/src="/src="assets\/images\//g' ${HOMEPAGE}
|
||||
sed $SEDOPTION 's/srcset="/srcset="assets\/images\//g' ${HOMEPAGE}
|
||||
}
|
||||
|
||||
generate_bugpattern_docs() {
|
||||
# The "mvn clean" is necessary since the wiki docs are generated by an
|
||||
# annotation processor that also compiles the code. If Maven thinks the code
|
||||
# does not need to be recompiled, the wiki docs will not be generated either.
|
||||
mvn clean
|
||||
|
||||
# This will create markdown files for each bug pattern in `docgen/target/generated-wiki/bugpatterns`
|
||||
mvn -P run-annotation-processor compile site -Dverification.skip
|
||||
|
||||
cp -r docgen/target/generated-wiki/bugpatterns/*.md website/bugpatterns
|
||||
}
|
||||
|
||||
generate_refaster_docs() {
|
||||
TEMPLATES=$(find error-prone-contrib/src/main/java/tech/picnic/errorprone/refastertemplates -type f -iname "*.java" ! -iname "package-info.java")
|
||||
for TEMPLATE in $TEMPLATES; do
|
||||
NAME=$(basename "${TEMPLATE}" ".java")
|
||||
FILENAME="${REFASTER_FOLDER}/${NAME}.md"
|
||||
|
||||
EXTRA_DOCS=$(cat "${REFASTER_DOCS_FOLDER}/${NAME}.md" 2>/dev/null)
|
||||
|
||||
echo "Generating ${FILENAME}"
|
||||
cat > "${FILENAME}" << EOF
|
||||
---
|
||||
layout: default
|
||||
title: ${NAME}
|
||||
parent: Refaster Rules
|
||||
nav_order: 1
|
||||
---
|
||||
|
||||
# ${NAME}
|
||||
|
||||
Style
|
||||
{: .label .label-blue }
|
||||
|
||||
Error
|
||||
{: .label .label-red }
|
||||
|
||||
${EXTRA_DOCS}
|
||||
|
||||
## Samples
|
||||
|
||||
\`\`\`java
|
||||
public static void sample() {}
|
||||
\`\`\`
|
||||
|
||||
<a href="https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/${NAME}.java" class="fs-3 btn external" target="_blank">
|
||||
View source code on GitHub
|
||||
<svg viewBox="0 0 24 24" aria-labelledby="svg-external-link-title"><use xlink:href="#svg-external-link"></use></svg>
|
||||
</a>
|
||||
EOF
|
||||
done
|
||||
}
|
||||
|
||||
# Do it
|
||||
configure
|
||||
generate_homepage
|
||||
generate_bugpattern_docs
|
||||
generate_refaster_docs
|
||||
10
logo-dark.svg
Normal file
|
After Width: | Height: | Size: 63 KiB |
10
logo.svg
Normal file
|
After Width: | Height: | Size: 63 KiB |
116
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.2.1-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Picnic :: Error Prone Support</name>
|
||||
@@ -39,6 +39,8 @@
|
||||
</developers>
|
||||
|
||||
<modules>
|
||||
<module>docgen</module>
|
||||
<module>docgen_processor</module>
|
||||
<module>error-prone-contrib</module>
|
||||
<module>refaster-compiler</module>
|
||||
<module>refaster-runner</module>
|
||||
@@ -56,8 +58,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 +89,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 +97,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 +146,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 +217,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 +241,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 +280,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 +306,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 +368,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 +378,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 +399,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 +407,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 +516,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 +735,7 @@
|
||||
<module name="VisibilityModifier" />
|
||||
</module>
|
||||
<module name="UniqueProperties" />
|
||||
<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck" />
|
||||
</module>
|
||||
</checkstyleRules>
|
||||
<failOnViolation>false</failOnViolation>
|
||||
@@ -769,7 +759,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 +786,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 +846,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 +960,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>
|
||||
@@ -1203,7 +1204,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 +1246,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 +1720,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.2.1-SNAPSHOT</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.2.1-SNAPSHOT</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.2.1-SNAPSHOT</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.2.1-SNAPSHOT</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() {
|
||||
|
||||
16
website/.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
### Jekyll ###
|
||||
_site/
|
||||
.sass-cache/
|
||||
.jekyll-cache/
|
||||
.jekyll-metadata
|
||||
Gemfile.lock
|
||||
|
||||
# Ignore folders generated by Bundler
|
||||
.bundle/
|
||||
vendor/
|
||||
|
||||
# Generated content
|
||||
*.bak
|
||||
index.md
|
||||
bugpatterns/
|
||||
refasterrules/
|
||||
9
website/404.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
layout: default
|
||||
nav_exclude: true
|
||||
search_exclude: true
|
||||
---
|
||||
|
||||
## Page not found :(
|
||||
|
||||
The requested page could not be found.
|
||||
4
website/Gemfile
Normal file
@@ -0,0 +1,4 @@
|
||||
source "https://rubygems.org"
|
||||
gem "github-pages", "~> 227"
|
||||
gem "rake", "~> 13.0" # Required for "just-the-docs" theme
|
||||
gem "webrick", "~> 1.7"
|
||||
44
website/README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Error Prone Support website
|
||||
|
||||
This directory contains the majority of the source code that powers
|
||||
[error-prone.picnic.tech][error-prone-support-website]. The website is
|
||||
statically generated using [Jekyll][jekyll].
|
||||
|
||||
# Local development
|
||||
|
||||
To view the website on `localhost`, first follow the [Jekyll installation
|
||||
instructions][jekyll-docs-installation]. Once done, in this directory execute:
|
||||
|
||||
```sh
|
||||
bundle install
|
||||
../generate-docs.sh && bundle exec jekyll serve --livereload
|
||||
```
|
||||
|
||||
The website will now be [available][localhost-port-4000] on port 4000. Source
|
||||
code modifications will automatically be reflected. (An exception is
|
||||
`_config.yml`: changes to this file require a server restart.) Subsequent
|
||||
server restarts do not require running `bundle install`, unless `Gemfile` has
|
||||
been updated in the interim.
|
||||
|
||||
Documentation can be regenerated whist jekyll is running, by executing:
|
||||
|
||||
```sh
|
||||
../generate-docs.sh
|
||||
```
|
||||
|
||||
If you are not familiar with Jekyll, be sure to check out its
|
||||
[documentation][jekyll-docs]. It is recommended to follow the provided
|
||||
step-by-step tutorial.
|
||||
|
||||
# Deployment
|
||||
|
||||
The website is regenerated and deployed using the
|
||||
[`deploy-website.yaml`][error-prone-support-website-deploy-workflow] GitHub
|
||||
Actions workflow any time a change is merged to `master`.
|
||||
|
||||
[error-prone-support-website]: https://error-prone.picnic.tech
|
||||
[error-prone-support-website-deploy-workflow]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/deploy-website.yaml
|
||||
[jekyll]: https://jekyllrb.com
|
||||
[jekyll-docs]: https://jekyllrb.com/docs/
|
||||
[jekyll-docs-installation]: https://jekyllrb.com/docs/installation/
|
||||
[localhost-port-4000]: http://127.0.0.1:4000
|
||||
49
website/_config.yml
Normal file
@@ -0,0 +1,49 @@
|
||||
# General configuration.
|
||||
title: Error Prone Support
|
||||
logo: assets/images/favicon.svg
|
||||
url: https://error-prone.picnic.tech/
|
||||
description: >-
|
||||
Error Prone Support is a Picnic-opinionated extension of Google's Error
|
||||
Prone. It aims to improve code quality, focussing on maintainability,
|
||||
consistency and avoidance of common gotchas.
|
||||
|
||||
remote_theme: just-the-docs/just-the-docs
|
||||
plugins:
|
||||
- jekyll-remote-theme
|
||||
|
||||
# Do not deploy through GitHub pages.
|
||||
exclude:
|
||||
- Gemfile
|
||||
- Gemfile.lock
|
||||
- README.md
|
||||
- vendor
|
||||
|
||||
permalink: pretty # Use /doc/ vs. /doc.html
|
||||
|
||||
# Theme configuration.
|
||||
search_enabled: true
|
||||
heading_anchors: true
|
||||
|
||||
callouts:
|
||||
summary:
|
||||
title: Summary
|
||||
color: blue
|
||||
warning:
|
||||
title: Warning
|
||||
color: red
|
||||
|
||||
nav_external_links:
|
||||
- title: Error Prone Support on GitHub
|
||||
url: https://github.com/PicnicSupermarket/error-prone-support
|
||||
hide_icon: false
|
||||
|
||||
twitter:
|
||||
username: picnic
|
||||
card: summary
|
||||
|
||||
social:
|
||||
name: Picnic
|
||||
links:
|
||||
- https://github.com/PicnicSupermarket
|
||||
- https://twitter.com/picnic
|
||||
- https://www.linkedin.com/company/picnictechnologies
|
||||
5
website/_includes/footer_custom.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<img src="/assets/images/picnic-logo@2x.png" alt="Picnic Logo" id="logo" />
|
||||
|
||||
<p align="center">
|
||||
Copyright © 2017-2022 Picnic Technologies BV
|
||||
</p>
|
||||
17
website/_includes/head_custom.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!-- Generated from https://realfavicongenerator.net/ -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/assets/images/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/assets/images/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/assets/images/favicon-16x16.png">
|
||||
<link rel="manifest" href="/assets/images/site.webmanifest">
|
||||
<link rel="mask-icon" href="/assets/images/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<link rel="shortcut icon" href="/assets/images/favicon.ico">
|
||||
<meta name="msapplication-TileColor" content="#da532c">
|
||||
<meta name="msapplication-config" content="/assets/images/browserconfig.xml">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<!-- Support light and dark mode, as it's not natively supported.
|
||||
See: https://github.com/just-the-docs/just-the-docs/issues/234 -->
|
||||
<link rel="stylesheet" href="{{ '/assets/css/just-the-docs-light.css' | relative_url }}"
|
||||
media="(prefers-color-scheme: light)">
|
||||
<link rel="stylesheet" href="{{ '/assets/css/just-the-docs-dark.css' | relative_url }}"
|
||||
media="(prefers-color-scheme: dark)">
|
||||
24
website/_sass/custom/custom.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
// We should override $nav-width(-md), however this breaks code highlighting and other styles.
|
||||
// This appears an issue wrt the recommended way:
|
||||
// https://github.com/just-the-docs/just-the-docs/issues/982
|
||||
@include mq(lg) {
|
||||
.side-bar {
|
||||
min-width: 400px;
|
||||
}
|
||||
|
||||
.site-nav, .site-header {
|
||||
width: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
// Add support for external anchor icons.
|
||||
.external > svg {
|
||||
width: 1rem;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
footer > img#logo {
|
||||
width: 2rem;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
BIN
website/assets/images/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
website/assets/images/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
website/assets/images/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
9
website/assets/images/browserconfig.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/assets/images/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
BIN
website/assets/images/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 912 B |
BIN
website/assets/images/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
website/assets/images/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
33
website/assets/images/favicon.svg
Executable file
|
After Width: | Height: | Size: 66 KiB |
10
website/assets/images/logo-dark.svg
Normal file
|
After Width: | Height: | Size: 63 KiB |
10
website/assets/images/logo.svg
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
website/assets/images/mstile-150x150.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
website/assets/images/picnic-logo@2x.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
88
website/assets/images/safari-pinned-tab.svg
Normal file
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="2623.000000pt" height="2623.000000pt" viewBox="0 0 2623.000000 2623.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,2623.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M12907 25594 c-1 -1 -87 -5 -190 -9 -104 -3 -207 -8 -230 -10 -23 -2
|
||||
-78 -7 -122 -10 -44 -3 -109 -8 -145 -11 -36 -2 -78 -6 -95 -9 -16 -2 -59 -6
|
||||
-95 -10 -36 -3 -76 -8 -90 -10 -14 -2 -50 -7 -80 -10 -30 -4 -66 -8 -80 -10
|
||||
-14 -2 -61 -9 -105 -15 -44 -6 -91 -13 -105 -16 -14 -2 -41 -7 -60 -10 -461
|
||||
-71 -1060 -217 -1545 -378 -124 -41 -379 -132 -410 -146 -11 -5 -67 -28 -125
|
||||
-51 -505 -197 -1190 -545 -1630 -827 -82 -52 -373 -246 -385 -255 -5 -5 -53
|
||||
-38 -105 -75 -239 -168 -595 -450 -796 -631 -27 -25 -93 -84 -145 -130 -114
|
||||
-103 -545 -533 -650 -651 -156 -173 -189 -212 -316 -365 -32 -39 -71 -88 -88
|
||||
-108 -16 -21 -35 -44 -40 -50 -23 -26 -207 -269 -282 -372 -118 -163 -384
|
||||
-564 -437 -660 -6 -11 -43 -74 -82 -140 -39 -66 -88 -151 -108 -188 -20 -37
|
||||
-46 -84 -58 -105 -12 -20 -71 -136 -132 -257 -101 -200 -142 -285 -199 -415
|
||||
-11 -25 -31 -70 -44 -100 -72 -159 -275 -683 -298 -770 -3 -14 -12 -41 -20
|
||||
-60 -21 -55 -127 -402 -149 -490 -2 -8 -18 -68 -36 -134 -17 -65 -34 -130 -36
|
||||
-145 -3 -14 -16 -71 -29 -126 -24 -104 -37 -161 -36 -170 0 -2 -4 -19 -9 -36
|
||||
-5 -18 -12 -50 -16 -73 -3 -22 -7 -43 -9 -46 -2 -4 -7 -29 -11 -56 -3 -27 -8
|
||||
-52 -10 -55 -2 -4 -6 -24 -9 -45 -3 -22 -10 -61 -15 -89 -6 -27 -12 -68 -15
|
||||
-90 -3 -22 -8 -51 -10 -65 -3 -14 -7 -42 -10 -62 -3 -21 -7 -55 -10 -75 -3
|
||||
-21 -7 -49 -10 -63 -2 -14 -7 -52 -10 -85 -3 -33 -7 -71 -10 -85 -5 -28 -13
|
||||
-106 -20 -195 -3 -33 -7 -78 -10 -100 -2 -22 -7 -83 -10 -135 -4 -52 -9 -122
|
||||
-11 -155 -3 -33 -7 -269 -10 -525 -3 -256 -9 -468 -12 -472 -4 -3 -29 -19 -57
|
||||
-35 -203 -116 -785 -512 -935 -636 -8 -7 -65 -51 -125 -97 -119 -92 -382 -308
|
||||
-455 -374 -25 -23 -79 -70 -120 -106 -273 -240 -657 -632 -850 -870 -320 -394
|
||||
-512 -753 -546 -1020 -25 -193 12 -365 112 -516 127 -192 309 -298 601 -348
|
||||
174 -30 530 -26 747 8 17 3 54 8 81 11 28 4 57 8 65 10 8 2 35 7 60 10 25 3
|
||||
52 8 60 10 8 2 31 7 50 10 19 3 87 17 150 31 63 15 121 27 130 29 36 8 266 69
|
||||
395 106 109 31 572 182 632 205 8 4 11 -466 11 -1913 0 -1055 3 -1952 6 -1993
|
||||
41 -532 186 -1020 438 -1475 82 -149 279 -437 355 -520 10 -11 45 -52 78 -90
|
||||
109 -127 291 -302 440 -423 89 -72 374 -272 389 -272 2 0 33 -18 69 -39 82
|
||||
-50 369 -191 388 -191 8 0 19 -4 25 -9 9 -9 52 -26 214 -82 182 -64 401 -114
|
||||
660 -154 27 -4 95 -11 205 -21 135 -12 518 -5 615 10 14 3 52 7 85 11 33 3 69
|
||||
8 80 10 11 3 37 7 57 10 21 2 57 9 80 14 24 6 57 12 73 15 17 3 66 15 110 26
|
||||
44 12 88 23 98 25 101 22 417 140 572 214 53 25 82 34 86 27 4 -6 10 -36 13
|
||||
-66 4 -30 11 -77 16 -105 5 -27 11 -61 14 -75 4 -27 13 -79 20 -120 7 -37 22
|
||||
-111 51 -245 14 -66 27 -131 30 -145 3 -14 27 -115 55 -225 28 -110 52 -207
|
||||
54 -215 11 -46 28 -107 33 -120 3 -8 7 -19 8 -25 9 -39 27 -104 35 -125 5 -14
|
||||
23 -72 40 -130 63 -208 154 -476 227 -665 74 -190 87 -224 145 -360 111 -257
|
||||
276 -584 372 -735 42 -65 150 -225 155 -230 4 -3 22 -26 41 -53 70 -96 196
|
||||
-227 288 -301 159 -127 307 -186 481 -191 401 -11 693 267 904 861 58 163 130
|
||||
425 152 554 3 19 10 53 15 75 5 22 12 60 15 85 4 24 8 47 10 50 1 3 6 28 9 55
|
||||
4 28 9 57 11 65 2 8 7 36 10 62 5 46 8 73 20 153 6 43 10 75 20 170 4 33 8 71
|
||||
10 85 1 14 5 61 9 105 4 44 9 96 11 115 4 34 19 257 24 366 1 21 2 21 119 -12
|
||||
124 -36 334 -88 392 -98 19 -3 46 -8 60 -11 14 -3 36 -7 50 -10 14 -3 34 -7
|
||||
45 -9 32 -6 226 -35 278 -41 26 -3 66 -7 90 -10 245 -31 756 -36 997 -11 19 2
|
||||
67 7 105 11 71 6 80 7 170 19 159 21 359 60 575 111 193 45 635 192 712 235
|
||||
29 16 38 12 81 -37 211 -238 328 -363 532 -568 197 -197 293 -283 429 -385 26
|
||||
-19 53 -40 60 -47 24 -21 239 -164 311 -206 69 -41 319 -167 330 -167 3 0 24
|
||||
-8 48 -19 219 -98 513 -170 797 -197 54 -5 382 -5 408 1 13 2 52 7 86 10 68 6
|
||||
255 38 298 51 9 3 25 6 35 8 86 17 315 89 423 133 251 102 409 200 545 338
|
||||
149 151 214 297 220 492 9 309 -179 590 -595 887 -138 99 -400 252 -560 326
|
||||
l-66 31 -53 104 c-28 57 -72 141 -97 186 -24 45 -43 84 -41 86 2 1 28 -7 58
|
||||
-18 78 -29 296 -97 379 -118 71 -18 221 -49 270 -57 84 -13 75 -11 220 -29
|
||||
174 -22 537 -22 723 -1 26 3 68 8 94 11 111 12 280 45 416 81 32 8 68 17 80
|
||||
20 65 12 444 150 504 183 10 6 69 35 131 66 98 48 327 179 342 195 3 3 38 27
|
||||
78 54 40 27 101 71 135 97 34 27 67 52 72 56 77 57 356 325 440 423 28 32 139
|
||||
170 157 195 4 5 18 24 31 40 85 108 258 396 337 560 108 226 224 560 266 768
|
||||
25 125 50 281 59 377 3 25 8 70 12 100 3 30 7 930 8 2000 2 1667 4 1945 16
|
||||
1942 8 -2 106 -34 219 -72 175 -58 327 -107 365 -116 6 -1 51 -14 100 -29 106
|
||||
-31 397 -105 440 -112 17 -2 55 -11 85 -18 30 -8 82 -18 115 -24 33 -6 74 -13
|
||||
90 -16 279 -51 619 -72 848 -52 379 34 643 195 764 465 75 169 78 383 8 599
|
||||
-38 115 -152 341 -245 482 -183 280 -375 506 -715 846 -198 199 -347 339 -494
|
||||
465 -36 30 -72 62 -80 70 -9 8 -66 55 -126 105 -61 49 -115 94 -121 100 -6 5
|
||||
-26 21 -44 35 -18 14 -81 62 -139 107 -133 103 -129 101 -311 235 -165 121
|
||||
-495 341 -695 462 l-80 48 -1 346 c0 304 -9 625 -19 732 -2 19 -6 78 -10 130
|
||||
-4 52 -8 108 -10 125 -2 16 -6 59 -9 95 -6 64 -9 88 -21 185 -11 88 -14 115
|
||||
-19 153 -3 20 -8 57 -11 82 -3 25 -8 56 -10 70 -3 14 -7 40 -10 58 -3 17 -7
|
||||
44 -10 60 -2 15 -7 43 -10 62 -18 111 -30 174 -65 345 -22 105 -49 228 -60
|
||||
275 -11 47 -23 95 -25 108 -11 47 -77 293 -115 422 -221 761 -566 1552 -987
|
||||
2265 -97 163 -104 175 -278 438 -688 1039 -1542 1917 -2595 2667 -133 95 -576
|
||||
380 -590 380 -6 0 -10 5 -10 10 0 6 -4 10 -9 10 -5 0 -55 27 -111 60 -56 33
|
||||
-104 60 -107 60 -3 0 -17 8 -31 18 -38 27 -156 89 -397 212 -546 277 -1134
|
||||
507 -1740 680 -99 29 -187 53 -195 55 -8 2 -89 22 -180 44 -91 23 -178 44
|
||||
-195 47 -16 3 -46 9 -65 14 -19 5 -51 12 -70 15 -19 3 -42 8 -50 10 -8 2 -26
|
||||
6 -40 9 -38 6 -142 25 -170 31 -33 7 -127 23 -170 29 -19 3 -48 8 -65 11 -64
|
||||
11 -92 15 -140 20 -27 3 -75 10 -105 15 -30 5 -86 12 -125 15 -38 4 -77 8 -85
|
||||
10 -28 5 -171 18 -330 30 -52 4 -111 8 -130 10 -114 10 -835 26 -843 19z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.0 KiB |
19
website/assets/images/site.webmanifest
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/images/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/assets/images/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||