Compare commits

...

77 Commits

Author SHA1 Message Date
Gijs de Jong
2a9ce49075 Add char rules + fix unit test setup 2023-12-13 11:46:47 +01:00
Gijs de Jong
80a312a829 refaster rules 2023-12-13 11:26:33 +01:00
Picnic-Bot
f0ff5c3fd6 Upgrade dawidd6/action-download-artifact v2.28.0 -> v2.28.1 (#917)
See:
- https://github.com/dawidd6/action-download-artifact/compare/v2.28.0...v2.28.1
2023-12-13 08:57:00 +01:00
Picnic-Bot
738a6706fd Upgrade Checker Framework Annotations 3.40.0 -> 3.41.0 (#907)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.41.0
- https://github.com/typetools/checker-framework/compare/checker-framework-3.40.0...checker-framework-3.41.0
2023-12-12 08:16:29 +01:00
Picnic-Bot
fd97ac3676 Upgrade actions/deploy-pages v3.0.0 -> v3.0.1 (#912)
See:
- https://github.com/actions/deploy-pages/releases/tag/v3.0.1
2023-12-12 08:05:08 +01:00
Picnic-Bot
5b011312e1 Upgrade errorprone-slf4j 0.1.20 -> 0.1.21 (#913)
See:
- https://github.com/KengoTODA/errorprone-slf4j/releases/tag/v0.1.21
- https://github.com/KengoTODA/errorprone-slf4j/compare/v0.1.20...v0.1.21
2023-12-11 21:42:20 +01:00
Picnic-Bot
ff3f963820 Upgrade actions/deploy-pages v2.0.5 -> v3.0.0 (#909)
See:
- https://github.com/actions/deploy-pages/releases/tag/v3.0.0
2023-12-06 09:36:11 +01:00
Picnic-Bot
94edab3b4f Upgrade actions/configure-pages v3.0.7 -> v4.0.0 (#908)
See:
- https://github.com/actions/configure-pages/releases/tag/v4.0.0
2023-12-06 09:15:36 +01:00
Picnic-Bot
276529fd34 Upgrade actions/configure-pages v3.0.6 -> v3.0.7 (#906)
See:
- https://github.com/actions/configure-pages/releases/tag/v3.0.7
2023-12-06 09:05:12 +01:00
Picnic-Bot
f5022efe68 Upgrade actions/deploy-pages v2.0.4 -> v2.0.5 (#901)
See:
- https://github.com/actions/deploy-pages/releases/tag/v2.0.5
2023-12-05 18:25:46 +01:00
Picnic-Bot
49e7313e1c Upgrade maven-javadoc-plugin 3.6.2 -> 3.6.3 (#904)
See:
- https://github.com/apache/maven-javadoc-plugin/releases/tag/maven-javadoc-plugin-3.6.3
- https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.6.2...maven-javadoc-plugin-3.6.3
2023-12-04 08:57:52 +01:00
Picnic-Bot
b3462b0a1e Upgrade ruby/setup-ruby v1.159.0 -> v1.161.0 (#905)
See:
- https://github.com/ruby/setup-ruby/releases/tag/v1.161.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.160.0
2023-12-04 08:27:55 +01:00
Picnic-Bot
a5b71410ae Upgrade actions/setup-java v3.13.0 -> v4.0.0 (#899)
See:
- https://github.com/actions/setup-java/releases/tag/v4.0.0
2023-12-04 07:41:00 +01:00
Picnic-Bot
6b73f03b22 Upgrade CodeQL v2.22.5 -> v2.22.8 (#903)
See:
- https://github.com/github/codeql-action/blob/main/CHANGELOG.md
- https://github.com/github/codeql-action/compare/v2.22.5...v2.22.8
2023-12-04 07:18:13 +01:00
Picnic-Bot
5d120252ac Upgrade Mockito 5.7.0 -> 5.8.0 (#902)
See:
- https://github.com/mockito/mockito/releases/tag/v5.8.0
- https://github.com/mockito/mockito/compare/v5.7.0...v5.8.0
2023-12-03 19:40:39 +01:00
Picnic-Bot
351b886e6c Upgrade NullAway 0.10.17 -> 0.10.18 (#900)
See:
- https://github.com/uber/NullAway/blob/master/CHANGELOG.md
- https://github.com/uber/NullAway/releases/tag/v0.10.18
- https://github.com/uber/NullAway/compare/v0.10.17...v0.10.18
2023-12-01 08:42:19 +01:00
Picnic-Bot
ad5dd92b9a Upgrade Pitest Git plugins 1.1.3 -> 1.1.4 (#898) 2023-11-30 18:59:39 +01:00
Rick Ossendrijver
e9a5295e80 Reference conference talk in README (#877)
While there, fix some URLs broken by eafb73814a.
2023-11-30 12:50:42 +01:00
Picnic-Bot
7ae9356c9e Upgrade Pitest Git plugins 1.1.2 -> 1.1.3 (#897) 2023-11-28 08:58:20 +01:00
Stephan Schroevers
5618de49f4 Introduce ConstantsFormat Refaster rule (#883) 2023-11-27 13:12:17 +01:00
Stephan Schroevers
17c7b396d2 Simplify AbstractMatcherTestChecker (#853)
By using `TreePathScanner` rather than directly using `TreeScanner`.
2023-11-27 11:40:16 +01:00
Stephan Schroevers
eafb73814a Apply assorted build improvements (#866)
Summary of changes:
- Consistently use `.yml` instead of `.yaml`.
- Don't install SNAPSHOT artifacts during the CodeQL build, so that
  they don't end up in the Maven cache.
- Sync a bunch of `pom.xml` changes from our internal Maven parent.
2023-11-27 11:16:48 +01:00
Picnic-Bot
7793006b5e Upgrade Spring Boot 2.7.17 -> 2.7.18 (#895)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v2.7.18
- https://github.com/spring-projects/spring-boot/compare/v2.7.17...v2.7.18
2023-11-26 15:51:44 +01:00
Picnic-Bot
7733ceaaec Upgrade build-helper-maven-plugin 3.4.0 -> 3.5.0 (#896)
See:
- https://github.com/mojohaus/build-helper-maven-plugin/releases/tag/3.5.0
- https://github.com/mojohaus/build-helper-maven-plugin/compare/3.4.0...3.5.0
2023-11-26 15:06:23 +01:00
Picnic-Bot
53e0a0cb41 Upgrade Project Reactor 2022.0.13 -> 2023.0.0 (#887)
See:
- https://github.com/reactor/reactor/releases/tag/2023.0.0-M1
- https://github.com/reactor/reactor/releases/tag/2023.0.0-M2
- https://github.com/reactor/reactor/releases/tag/2023.0.0-M3
- https://github.com/reactor/reactor/releases/tag/2023.0.0-RC1
- https://github.com/reactor/reactor/releases/tag/2023.0.0
- https://github.com/reactor/reactor/compare/2022.0.13...2023.0.0
2023-11-24 08:10:02 +01:00
Picnic-Bot
1d25b78c4c Upgrade Jackson 2.15.3 -> 2.16.0 (#889)
See:
- https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.16
- https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.15.3...jackson-bom-2.16.0
2023-11-23 10:58:24 +01:00
Picnic-Bot
b70da48d70 Upgrade MongoDB driver 4.11.0 -> 4.11.1 (#878)
See:
- https://jira.mongodb.org/issues/?jql=project%20%3D%20JAVA%20AND%20fixVersion%20%3E%204.11.0%20AND%20fixVersion%20%3C%3D%204.11.1
- https://github.com/mongodb/mongo-java-driver/releases/tag/r4.11.1
- https://github.com/mongodb/mongo-java-driver/compare/r4.11.0...r4.11.1
2023-11-22 12:44:57 +01:00
Picnic-Bot
01fd608aac Upgrade Checkstyle 10.12.4 -> 10.12.5 (#885)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.12.5
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.12.4...checkstyle-10.12.5
2023-11-22 09:19:45 +01:00
Picnic-Bot
dd0369a645 Upgrade Project Reactor 2022.0.12 -> 2022.0.13 (#886)
See:
- https://github.com/reactor/reactor/releases/tag/2022.0.13
- https://github.com/reactor/reactor/compare/2022.0.12...2022.0.13
2023-11-21 09:20:48 +01:00
Picnic-Bot
74b1104890 Upgrade NullAway 0.10.16 -> 0.10.17 (#892)
See:
- https://github.com/uber/NullAway/blob/master/CHANGELOG.md
- https://github.com/uber/NullAway/releases/tag/v0.10.17
- https://github.com/uber/NullAway/compare/v0.10.16...v0.10.17
2023-11-21 08:53:45 +01:00
Picnic-Bot
a230bbff12 Upgrade Byte Buddy 1.14.9 -> 1.14.10 (#891)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.14.10
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.9...byte-buddy-1.14.10
2023-11-21 08:01:19 +01:00
Picnic-Bot
100305ed1f Upgrade Spring 5.3.30 -> 5.3.31 (#888)
See:
- https://github.com/spring-projects/spring-framework/releases/tag/v5.3.31
- https://github.com/spring-projects/spring-framework/compare/v5.3.30...v5.3.31
2023-11-20 07:41:53 +01:00
Picnic-Bot
96c836e6a9 Upgrade versions-maven-plugin 2.16.1 -> 2.16.2 (#890)
See:
- https://github.com/mojohaus/versions/releases/tag/2.16.2
- https://github.com/mojohaus/versions-maven-plugin/compare/2.16.1...2.16.2
2023-11-20 07:28:35 +01:00
Picnic-Bot
97acfbc3bc Upgrade Surefire 3.2.1 -> 3.2.2 (#876)
See:
- https://github.com/apache/maven-surefire/releases/tag/surefire-3.2.2
- https://github.com/apache/maven-surefire/compare/surefire-3.2.1...surefire-3.2.2
2023-11-16 08:27:12 +01:00
Picnic-Bot
9ce7c6b40b Upgrade maven-javadoc-plugin 3.6.0 -> 3.6.2 (#875)
See:
- https://github.com/apache/maven-javadoc-plugin/releases/tag/maven-javadoc-plugin-3.6.2
- https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.6.0...maven-javadoc-plugin-3.6.2
2023-11-13 08:39:26 +01:00
Picnic-Bot
57a43a1c85 Upgrade JUnit 5 5.10.0 -> 5.10.1 (#874)
See:
- https://junit.org/junit5/docs/current/release-notes/
- https://github.com/junit-team/junit5/releases/tag/r5.10.1
- https://github.com/junit-team/junit5/compare/r5.10.0...r5.10.1
2023-11-13 08:18:45 +01:00
Picnic-Bot
89c4969a31 Upgrade Swagger 2.2.18 -> 2.2.19 (#879)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.19
- https://github.com/swagger-api/swagger-core/compare/v2.2.18...v2.2.19
2023-11-12 19:10:39 +01:00
Picnic-Bot
dbd4853e3e Upgrade CodeQL v2.22.0 -> v2.22.5 (#872)
See:
- https://github.com/github/codeql-action/blob/main/CHANGELOG.md
- https://github.com/github/codeql-action/compare/v2.22.0...v2.22.5
2023-11-06 08:14:47 +01:00
Picnic-Bot
62a7dacf4d Upgrade ruby/setup-ruby v1.155.0 -> v1.159.0 (873)
See:
- https://github.com/ruby/setup-ruby/releases/tag/v1.159.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.158.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.157.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.156.0
2023-11-06 07:45:10 +01:00
Picnic-Bot
d23684ee6d Upgrade Mockito 5.6.0 -> 5.7.0 (#871)
See:
- https://github.com/mockito/mockito/releases/tag/v5.7.0
- https://github.com/mockito/mockito/compare/v5.6.0...v5.7.0
2023-11-04 09:41:24 +01:00
Picnic-Bot
fd150af6c6 Upgrade maven-checkstyle-plugin 3.3.0 -> 3.3.1 (#861)
See https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.3.0...maven-checkstyle-plugin-3.3.1
2023-11-03 08:29:27 +01:00
Picnic-Bot
a1a865b87a Upgrade Checker Framework Annotations 3.39.0 -> 3.40.0 (#870)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.40.0
- https://github.com/typetools/checker-framework/compare/checker-framework-3.39.0...checker-framework-3.40.0
2023-11-03 08:06:40 +01:00
Picnic-Bot
64000ebb07 Upgrade pitest-maven-plugin 1.15.2 -> 1.15.3 (#869)
See:
- https://github.com/hcoles/pitest/releases/tag/1.15.3
- https://github.com/hcoles/pitest/compare/1.15.2...1.15.3
2023-11-02 10:20:49 +01:00
Picnic-Bot
6122d7a39f Upgrade NullAway 0.10.15 -> 0.10.16 (#868)
See:
- https://github.com/uber/NullAway/blob/master/CHANGELOG.md
- https://github.com/uber/NullAway/releases/tag/v0.10.16
- https://github.com/uber/NullAway/compare/v0.10.15...v0.10.16
2023-11-02 08:47:51 +01:00
Stephan Schroevers
ca01bbb74a Renew Arcmutate OSS license (#864) 2023-11-01 21:30:36 +01:00
Rick Ossendrijver
4ce9f92489 Disallow static import of BugCheckerRefasctoringTestHelper.TestMode members (#862) 2023-10-30 10:46:50 +01:00
Stephan Schroevers
e8d14993a1 Introduce Checkstyle integration test (#865)
The new `integration-tests/checkstyle-10.12.4.sh` script can be executed
locally or by adding a comment containing the string `/integration-test`
to a pull request.

The integration test comprises a shell script that:
1. Checks out a Checkstyle release tag.
2. Applies a small patch to enable Error Prone Support.
3. Runs the build in Error Prone patch mode, until no further changes
   are applied.
4. Validates that:
   - The build (including tests) still passes.
   - The set of changes matches a pre-recorded diff.
   - The set of Error Prone Support-emitted warnings matches a
     pre-recorded list.

The script also has a `--sync` mode using which the expected diff and 
warnings can be updated. Combined, the script makes it easy to assess
and track the impact of changes to Error Prone Support in relation to
the Checkstyle code base.

Over time this setup will be generalized to include integration tests
against other code bases.
2023-10-30 10:34:14 +01:00
Picnic-Bot
88860800b1 Upgrade license-maven-plugin 2.2.0 -> 2.3.0 (#859)
See:
- https://github.com/mojohaus/license-maven-plugin/releases/tag/2.3.0
- https://github.com/mojohaus/license-maven-plugin/compare/2.2.0...2.3.0
2023-10-27 15:26:59 +02:00
Picnic-Bot
e76dd5c31b Upgrade maven-dependency-plugin 3.6.0 -> 3.6.1 (#854)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MDEP%20AND%20fixVersion%20%3E%203.6.0%20AND%20fixVersion%20%3C%3D%203.6.1
- https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.6.0...maven-dependency-plugin-3.6.1
2023-10-27 12:10:12 +02:00
Picnic-Bot
701db7d61e Upgrade Surefire 3.1.2 -> 3.2.1 (#856)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20SUREFIRE%20AND%20fixVersion%20%3E%203.1.2%20AND%20fixVersion%20%3C%3D%203.2.1
- https://github.com/apache/maven-surefire/releases/tag/surefire-3.2.1
- https://github.com/apache/maven-surefire/compare/surefire-3.1.2...surefire-3.2.1
2023-10-27 11:55:29 +02:00
Picnic-Bot
8781a9ede5 Upgrade git-commit-id-maven-plugin 6.0.0 -> 7.0.0 (#852)
See:
- https://github.com/git-commit-id/git-commit-id-maven-plugin/releases/tag/v7.0.0
- https://github.com/git-commit-id/git-commit-id-maven-plugin/compare/v6.0.0...v7.0.0
2023-10-27 11:33:24 +02:00
Picnic-Bot
dc14c4e970 Upgrade maven-clean-plugin 3.3.1 -> 3.3.2 (#863)
See:
- https://github.com/apache/maven-clean-plugin/releases/tag/maven-clean-plugin-3.3.2
- https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.3.1...maven-clean-plugin-3.3.2
2023-10-27 11:15:34 +02:00
Cernat Catalin Stefan
a435b657ae Introduce NonStaticImport check (#450)
This check complements the existing `StaticImport` check. We ensure that the 
two checks do not suggest mutually incompatible approaches to static imports.
2023-10-26 11:00:48 +02:00
Picnic-Bot
00edc5ea1f Upgrade pitest-junit5-plugin 1.2.0 -> 1.2.1 (#857)
See https://github.com/pitest/pitest-junit5-plugin/compare/1.2.0...1.2.1
2023-10-26 10:48:08 +02:00
Picnic-Bot
7383a11da7 Upgrade Swagger 2.2.17 -> 2.2.18 (#860)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.18
- https://github.com/swagger-api/swagger-core/compare/v2.2.17...v2.2.18
2023-10-26 09:32:11 +02:00
Picnic-Bot
e4e17664d0 Upgrade ossf/scorecard-action v2.3.0 -> v2.3.1 (#855)
See:
- https://github.com/ossf/scorecard-action/releases/tag/v2.3.1
2023-10-26 09:14:34 +02:00
Picnic-Bot
5972a8e225 Upgrade pitest-maven-plugin 1.15.1 -> 1.15.2 (#858)
See:
- https://github.com/hcoles/pitest/releases/tag/1.15.2
- https://github.com/hcoles/pitest/compare/1.15.1...1.15.2
2023-10-25 17:20:39 +02:00
Rick Ossendrijver
01b30d767b Introduce BugPatternTestExtractor for documentation generation (#494)
This new `Extractor` implementation collects identification and
replacement source code from `BugChecker` unit tests.

While there:
- Refactor the existing `Extractor` setup such that instances are
  service-loaded and need to implement only a single method, thereby
  avoiding the need to align logic between multiple source code 
  locations.
- Extend the validation performed by the `Compilation` test helper
  class.
- Extend the `ErrorProneTestHelperSourceFormat` check to support source
  code passed to the `Compilation` test helper class.
2023-10-24 13:57:37 +02:00
Stephan Schroevers
c2426d3a8d Drop obsolete Maven configuration (#851) 2023-10-23 16:52:17 +02:00
Stephan Schroevers
8130ddf59c Introduce IsIdentityOperation matcher for use by Refaster templates (#749) 2023-10-23 14:52:16 +02:00
Vincent Koeman
c3b950f114 Introduce JUnitNullaryParameterizedTestDeclaration check (#817)
While there, slightly simplify the `AutowiredConstructor` check.
2023-10-23 09:22:51 +02:00
Stephan Schroevers
fa026de336 Introduce IsEmpty matcher for use by Refaster templates (#744)
While there, generalize a number of Refaster rules using this new matcher.
2023-10-23 09:07:47 +02:00
Picnic-Bot
2ff3095fca Upgrade Spring Boot 2.7.16 -> 2.7.17 (#848)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v2.7.17
- https://github.com/spring-projects/spring-boot/compare/v2.7.16...v2.7.17
2023-10-21 18:15:27 +02:00
Picnic-Bot
2b2d9f498e Upgrade NullAway 0.10.14 -> 0.10.15 (#846)
See:
- https://github.com/uber/NullAway/blob/master/CHANGELOG.md
- https://github.com/uber/NullAway/releases/tag/v0.10.15
- https://github.com/uber/NullAway/compare/v0.10.14...v0.10.15
2023-10-20 10:06:21 +02:00
Picnic-Bot
2a5dd7d56d Upgrade Error Prone 2.22.0 -> 2.23.0 (#847)
See:
- https://github.com/google/error-prone/releases/tag/v2.23.0
- https://github.com/google/error-prone/compare/v2.22.0...v2.23.0
- https://github.com/PicnicSupermarket/error-prone/compare/v2.22.0-picnic-1...v2.23.0-picnic-1
2023-10-20 09:13:05 +02:00
Stephan Schroevers
dd021b3757 Introduce {Integer,Long}SignumIs{Positive,Negative} Refaster rules (#822) 2023-10-19 10:46:09 +02:00
Picnic-Bot
c77cc9a35d Upgrade swagger-annotations 1.6.11 -> 1.6.12 (#842)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v1.6.12
- https://github.com/swagger-api/swagger-core/compare/v1.6.11...v1.6.12
2023-10-18 10:36:45 +02:00
Picnic-Bot
7a7a09d208 Upgrade jacoco-maven-plugin 0.8.10 -> 0.8.11 (#843)
See:
- https://github.com/jacoco/jacoco/releases/tag/v0.8.11
- https://github.com/jacoco/jacoco/compare/v0.8.10...v0.8.11
2023-10-18 09:13:28 +02:00
Vincent Koeman
62077dacbb Introduce assorted BigDecimal#signum Refaster rules (#812) 2023-10-18 08:59:36 +02:00
Picnic-Bot
3a76f91d18 Upgrade actions/checkout v4.1.0 -> v4.1.1 (#845)
See:
- https://github.com/actions/checkout/releases/tag/v4.1.1
2023-10-18 08:17:15 +02:00
Stephan Schroevers
82c0dd95ce Build and test against JDK 21 instead of JDK 20 (#832)
See https://jdk.java.net/21/release-notes
2023-10-16 13:11:57 +02:00
Picnic-Bot
a424a3e949 Upgrade Jackson 2.15.2 -> 2.15.3 (#837)
See:
- https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.15.3
- https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.15.2...jackson-bom-2.15.3
2023-10-15 15:24:37 +02:00
Picnic-Bot
3f530475ab Upgrade MongoDB driver 4.10.2 -> 4.11.0 (#831)
See:
- https://jira.mongodb.org/issues/?jql=project%20%3D%20JAVA%20AND%20fixVersion%20%3E%204.10.2%20AND%20fixVersion%20%3C%3D%204.11.0
- https://github.com/mongodb/mongo-java-driver/releases/tag/r4.11.0
- https://github.com/mongodb/mongo-java-driver/compare/r4.10.2...r4.11.0
2023-10-15 14:54:07 +02:00
Picnic-Bot
24621b7c20 Upgrade Swagger 2.2.16 -> 2.2.17 (#834)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.17
- https://github.com/swagger-api/swagger-core/compare/v2.2.16...v2.2.17
2023-10-15 12:53:37 +02:00
Picnic-Bot
746e757e16 Upgrade Project Reactor 2022.0.11 -> 2022.0.12 (#829)
See:
- https://github.com/reactor/reactor/releases/tag/2022.0.12
- https://github.com/reactor/reactor/compare/2022.0.11...2022.0.12
2023-10-14 17:43:35 +02:00
Picnic-Bot
df42013d5b Upgrade Guava 32.1.2-jre -> 32.1.3-jre (#828)
See:
- https://github.com/google/guava/releases/tag/v32.1.3
- https://github.com/google/guava/compare/v32.1.2...v32.1.3
2023-10-14 09:40:41 +02:00
Picnic-Bot
0336626dc9 Upgrade Arcmutate 1.2.1 -> 1.2.2 (#833) 2023-10-13 16:13:18 +02:00
87 changed files with 67737 additions and 740 deletions

View File

@@ -10,7 +10,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-22.04 ]
jdk: [ 11.0.20, 17.0.8, 20.0.2 ]
jdk: [ 11.0.20, 17.0.8, 21.0.0 ]
distribution: [ temurin ]
experimental: [ false ]
include:
@@ -31,11 +31,11 @@ jobs:
# additionally enabling all checks defined in this project and any Error
# Prone checks available only from other artifact repositories.
- name: Check out code
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
java-version: ${{ matrix.jdk }}
distribution: ${{ matrix.distribution }}

View File

@@ -22,23 +22,23 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
java-version: 17.0.8
distribution: temurin
cache: maven
- name: Initialize CodeQL
uses: github/codeql-action/init@2cb752a87e96af96708ab57187ab6372ee1973ab # v2.22.0
uses: github/codeql-action/init@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
with:
languages: ${{ matrix.language }}
- name: Perform minimal build
if: matrix.language == 'java'
run: mvn -T1C clean install -DskipTests -Dverification.skip
run: mvn -T1C clean package -DskipTests -Dverification.skip
- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@2cb752a87e96af96708ab57187ab6372ee1973ab # v2.22.0
uses: github/codeql-action/analyze@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
with:
category: /language:${{ matrix.language }}

View File

@@ -12,15 +12,15 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- uses: ruby/setup-ruby@d37167af451eb51448db3354e1057b75c4b268f7 # v1.155.0
- uses: ruby/setup-ruby@8575951200e472d5f2d95c625da0c7bec8217c42 # v1.161.0
with:
working-directory: ./website
bundler-cache: true
- name: Configure Github Pages
uses: actions/configure-pages@f156874f8191504dae5b037505266ed5dda6c382 # v3.0.6
uses: actions/configure-pages@1f0c5cde4bc74cd7e1254d0cb4de8d49e9068c7d # v4.0.0
- name: Generate documentation
run: ./generate-docs.sh
- name: Build website with Jekyll
@@ -48,4 +48,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@9dbe3824824f8a1377b8e298bafde1a50ede43e5 # v2.0.4
uses: actions/deploy-pages@13b55b33dd8996121833dbc1db458c793a334630 # v3.0.1

View File

@@ -21,16 +21,16 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- name: Run OpenSSF Scorecard analysis
uses: ossf/scorecard-action@483ef80eb98fb506c348f7d62e28055e49fe2398 # v2.3.0
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
with:
results_file: results.sarif
results_format: sarif
publish_results: ${{ github.ref == 'refs/heads/master' }}
- name: Update GitHub's code scanning dashboard
uses: github/codeql-action/upload-sarif@2cb752a87e96af96708ab57187ab6372ee1973ab # v2.22.0
uses: github/codeql-action/upload-sarif@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
with:
sarif_file: results.sarif

View File

@@ -12,12 +12,12 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
fetch-depth: 2
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
java-version: 17.0.8
distribution: temurin

View File

@@ -20,17 +20,17 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
java-version: 17.0.8
distribution: temurin
cache: maven
- name: Download Pitest analysis artifact
uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e # v2.28.0
uses: dawidd6/action-download-artifact@f29d1b6a8930683e80acedfbe6baa2930cd646b4 # v2.28.1
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
name: pitest-reports

View File

@@ -0,0 +1,43 @@
# If requested by means of a pull request comment, runs integration tests
# against the project, using the code found on the pull request branch.
# XXX: Generalize this to a matrix build of multiple integration tests,
# possibly using multiple JDK or OS versions.
# XXX: Investigate whether the comment can specify which integration tests run
# run. See this example of a dynamic build matrix:
# https://docs.github.com/en/actions/learn-github-actions/expressions#example-returning-a-json-object
name: "Integration tests"
on:
issue_comment:
types: [ created ]
permissions:
contents: read
jobs:
run-integration-tests:
name: On-demand integration test
if: |
github.event.issue.pull_request && contains(github.event.comment.body, '/integration-test')
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
ref: refs/pull/${{ github.event.issue.number }}/head
- name: Set up JDK
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
java-version: 17.0.8
distribution: temurin
cache: maven
- name: Install project to local Maven repository
run: mvn -T1C install -DskipTests -Dverification.skip
- name: Run integration test
run: xvfb-run ./integration-tests/checkstyle-10.12.4.sh "${{ runner.temp }}/artifacts"
- name: Upload artifacts on failure
if: ${{ failure() }}
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
with:
name: integration-test-checkstyle-10.12.4
path: "${{ runner.temp }}/artifacts"
- name: Remove installed project artifacts
run: mvn build-helper:remove-project-artifact

View File

@@ -16,12 +16,12 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
fetch-depth: 0
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
java-version: 17.0.8
distribution: temurin

3
.gitignore vendored
View File

@@ -9,3 +9,6 @@
target
*.iml
*.swp
# The Git repositories checked out by the integration test framework.
integration-tests/.repos

View File

@@ -15,9 +15,11 @@ 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].
To learn more about Error Prone (Support), how you can start using Error Prone
in practice, and how we use it at Picnic, watch the conference talk
[_Automating away bugs with Error Prone in practice_][conference-talk]. Also
consider checking out 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]
[![Reproducible Builds][reproducible-builds-badge]][reproducible-builds-report]
@@ -265,6 +267,7 @@ channel; please see our [security policy][security] for details.
[bug-checks-identity-conversion]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IdentityConversion.java
[codeql-badge]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/codeql.yml/badge.svg?branch=master&event=push
[codeql-master]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/codeql.yml?query=branch:master+event:push
[conference-talk]: https://www.youtube.com/watch?v=-47WD-3wKBs
[contributing]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/CONTRIBUTING.md
[contributing-pull-request]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/CONTRIBUTING.md#-opening-a-pull-request
[error-prone-bugchecker]: https://github.com/google/error-prone/blob/master/check_api/src/main/java/com/google/errorprone/bugpatterns/BugChecker.java
@@ -274,8 +277,8 @@ channel; please see our [security policy][security] for details.
[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:master&event=push
[github-actions-build-badge]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/build.yml/badge.svg
[github-actions-build-master]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/build.yml?query=branch:master&event=push
[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

View File

@@ -1,7 +1,7 @@
# Arcmutate license for Error Prone Support, requested by sending an email to
# support@arcmutate.com.
expires=07/11/2023
expires=27/10/2025
keyVersion=1
signature=MhZxMbnO6UovNfllM0JuVWkZyvRT3/G5o/uT0Mm36c7200VpZNVu03gTAGivnl9W5RzvZhfpIHccuQ5ctjQkrqhsFSrl4fyqPqu3y5V2fsHIdFXP/G72EGj6Kay9ndLpaEHalqE0bEwxdnHMzEYq5y3O9vUPv8MhUl57xk+rvBo\=
signature=aQLVt0J//7nXDAlJuBe9ru7Dq6oApcLh17rxsgHoJqBkUCd2zvrtRxkcPm5PmF1I8gBDT9PHb+kTf6Kcfz+D1fT0wRrZv38ip56521AQPD6zjRSqak9O2Gisjo+QTPMD7oS009aSWKzQ1SMdtuZ+KIRvw4Gl+frtYzexqnGWEUQ\=
packages=tech.picnic.errorprone.*
type=OSSS

View File

@@ -5,17 +5,19 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.Objects.requireNonNull;
import com.google.auto.common.AnnotationMirrors;
import com.google.auto.service.AutoService;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.util.Context;
import java.util.Optional;
import javax.lang.model.element.AnnotationValue;
import tech.picnic.errorprone.documentation.BugPatternExtractor.BugPatternDocumentation;
@@ -23,29 +25,39 @@ import tech.picnic.errorprone.documentation.BugPatternExtractor.BugPatternDocume
* An {@link Extractor} that describes how to extract data from a {@code @BugPattern} annotation.
*/
@Immutable
final class BugPatternExtractor implements Extractor<BugPatternDocumentation> {
@Override
public BugPatternDocumentation extract(ClassTree tree, Context context) {
ClassSymbol symbol = ASTHelpers.getSymbol(tree);
BugPattern annotation = symbol.getAnnotation(BugPattern.class);
requireNonNull(annotation, "BugPattern annotation must be present");
@AutoService(Extractor.class)
@SuppressWarnings("rawtypes" /* See https://github.com/google/auto/issues/870. */)
public final class BugPatternExtractor implements Extractor<BugPatternDocumentation> {
/** Instantiates a new {@link BugPatternExtractor} instance. */
public BugPatternExtractor() {}
return new AutoValue_BugPatternExtractor_BugPatternDocumentation(
symbol.getQualifiedName().toString(),
annotation.name().isEmpty() ? tree.getSimpleName().toString() : annotation.name(),
ImmutableList.copyOf(annotation.altNames()),
annotation.link(),
ImmutableList.copyOf(annotation.tags()),
annotation.summary(),
annotation.explanation(),
annotation.severity(),
annotation.disableable(),
annotation.documentSuppression() ? getSuppressionAnnotations(tree) : ImmutableList.of());
@Override
public String identifier() {
return "bugpattern";
}
@Override
public boolean canExtract(ClassTree tree) {
return ASTHelpers.hasDirectAnnotationWithSimpleName(tree, BugPattern.class.getSimpleName());
public Optional<BugPatternDocumentation> tryExtract(ClassTree tree, VisitorState state) {
ClassSymbol symbol = ASTHelpers.getSymbol(tree);
BugPattern annotation = symbol.getAnnotation(BugPattern.class);
if (annotation == null) {
return Optional.empty();
}
return Optional.of(
new AutoValue_BugPatternExtractor_BugPatternDocumentation(
symbol.getQualifiedName().toString(),
annotation.name().isEmpty() ? tree.getSimpleName().toString() : annotation.name(),
ImmutableList.copyOf(annotation.altNames()),
annotation.link(),
ImmutableList.copyOf(annotation.tags()),
annotation.summary(),
annotation.explanation(),
annotation.severity(),
annotation.disableable(),
annotation.documentSuppression()
? getSuppressionAnnotations(tree)
: ImmutableList.of()));
}
/**

View File

@@ -0,0 +1,229 @@
package tech.picnic.errorprone.documentation;
import static com.google.errorprone.matchers.Matchers.instanceMethod;
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
import static java.util.function.Predicate.not;
import com.google.auto.service.AutoService;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.util.TreeScanner;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.TestCases;
/**
* An {@link Extractor} that describes how to extract data from classes that test a {@code
* BugChecker}.
*/
// XXX: Handle other methods from `{BugCheckerRefactoring,Compilation}TestHelper`:
// - Indicate which custom arguments are specified, if any.
// - For replacement tests, indicate which `FixChooser` is used.
// - ... (We don't use all optional features; TBD what else to support.)
@Immutable
@AutoService(Extractor.class)
@SuppressWarnings("rawtypes" /* See https://github.com/google/auto/issues/870. */)
public final class BugPatternTestExtractor implements Extractor<TestCases> {
/** Instantiates a new {@link BugPatternTestExtractor} instance. */
public BugPatternTestExtractor() {}
@Override
public String identifier() {
return "bugpattern-test";
}
@Override
public Optional<TestCases> tryExtract(ClassTree tree, VisitorState state) {
BugPatternTestCollector collector = new BugPatternTestCollector();
collector.scan(tree, state);
return Optional.of(collector.getCollectedTests())
.filter(not(ImmutableList::isEmpty))
.map(
tests ->
new AutoValue_BugPatternTestExtractor_TestCases(
ASTHelpers.getSymbol(tree).className(), tests));
}
private static final class BugPatternTestCollector
extends TreeScanner<@Nullable Void, VisitorState> {
private static final Matcher<ExpressionTree> COMPILATION_HELPER_DO_TEST =
instanceMethod()
.onDescendantOf("com.google.errorprone.CompilationTestHelper")
.named("doTest");
private static final Matcher<ExpressionTree> TEST_HELPER_NEW_INSTANCE =
staticMethod()
.onDescendantOfAny(
"com.google.errorprone.CompilationTestHelper",
"com.google.errorprone.BugCheckerRefactoringTestHelper")
.named("newInstance")
.withParameters("java.lang.Class", "java.lang.Class");
private static final Matcher<ExpressionTree> IDENTIFICATION_SOURCE_LINES =
instanceMethod()
.onDescendantOf("com.google.errorprone.CompilationTestHelper")
.named("addSourceLines");
private static final Matcher<ExpressionTree> REPLACEMENT_DO_TEST =
instanceMethod()
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper")
.named("doTest");
private static final Matcher<ExpressionTree> REPLACEMENT_EXPECT_UNCHANGED =
instanceMethod()
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper.ExpectOutput")
.named("expectUnchanged");
private static final Matcher<ExpressionTree> REPLACEMENT_OUTPUT_SOURCE_LINES =
instanceMethod()
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper.ExpectOutput")
.namedAnyOf("addOutputLines", "expectUnchanged");
private final List<TestCase> collectedTestCases = new ArrayList<>();
private ImmutableList<TestCase> getCollectedTests() {
return ImmutableList.copyOf(collectedTestCases);
}
@Override
public @Nullable Void visitMethodInvocation(MethodInvocationTree node, VisitorState state) {
boolean isReplacementTest = REPLACEMENT_DO_TEST.matches(node, state);
if (isReplacementTest || COMPILATION_HELPER_DO_TEST.matches(node, state)) {
getClassUnderTest(node, state)
.ifPresent(
classUnderTest -> {
List<TestEntry> entries = new ArrayList<>();
if (isReplacementTest) {
extractReplacementTestCases(node, entries, state);
} else {
extractIdentificationTestCases(node, entries, state);
}
if (!entries.isEmpty()) {
collectedTestCases.add(
new AutoValue_BugPatternTestExtractor_TestCase(
classUnderTest, ImmutableList.copyOf(entries).reverse()));
}
});
}
return super.visitMethodInvocation(node, state);
}
private static Optional<String> getClassUnderTest(
MethodInvocationTree tree, VisitorState state) {
if (TEST_HELPER_NEW_INSTANCE.matches(tree, state)) {
return Optional.ofNullable(ASTHelpers.getSymbol(tree.getArguments().get(0)))
.filter(s -> !s.type.allparams().isEmpty())
.map(s -> s.type.allparams().get(0).tsym.getQualifiedName().toString());
}
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
return receiver instanceof MethodInvocationTree
? getClassUnderTest((MethodInvocationTree) receiver, state)
: Optional.empty();
}
private static void extractIdentificationTestCases(
MethodInvocationTree tree, List<TestEntry> sink, VisitorState state) {
if (IDENTIFICATION_SOURCE_LINES.matches(tree, state)) {
String path = ASTHelpers.constValue(tree.getArguments().get(0), String.class);
Optional<String> sourceCode =
getSourceCode(tree).filter(s -> s.contains("// BUG: Diagnostic"));
if (path != null && sourceCode.isPresent()) {
sink.add(
new AutoValue_BugPatternTestExtractor_IdentificationTestEntry(
path, sourceCode.orElseThrow()));
}
}
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
if (receiver instanceof MethodInvocationTree) {
extractIdentificationTestCases((MethodInvocationTree) receiver, sink, state);
}
}
private static void extractReplacementTestCases(
MethodInvocationTree tree, List<TestEntry> sink, VisitorState state) {
if (REPLACEMENT_OUTPUT_SOURCE_LINES.matches(tree, state)) {
/*
* Retrieve the method invocation that contains the input source code. Note that this cast
* is safe, because this code is guarded by an earlier call to `#getClassUnderTest(..)`,
* which ensures that `tree` is part of a longer method invocation chain.
*/
MethodInvocationTree inputTree = (MethodInvocationTree) ASTHelpers.getReceiver(tree);
String path = ASTHelpers.constValue(inputTree.getArguments().get(0), String.class);
Optional<String> inputCode = getSourceCode(inputTree);
if (path != null && inputCode.isPresent()) {
Optional<String> outputCode =
REPLACEMENT_EXPECT_UNCHANGED.matches(tree, state) ? inputCode : getSourceCode(tree);
if (outputCode.isPresent() && !inputCode.equals(outputCode)) {
sink.add(
new AutoValue_BugPatternTestExtractor_ReplacementTestEntry(
path, inputCode.orElseThrow(), outputCode.orElseThrow()));
}
}
}
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
if (receiver instanceof MethodInvocationTree) {
extractReplacementTestCases((MethodInvocationTree) receiver, sink, state);
}
}
// XXX: This logic is duplicated in `ErrorProneTestSourceFormat`. Can we do better?
private static Optional<String> getSourceCode(MethodInvocationTree tree) {
List<? extends ExpressionTree> sourceLines =
tree.getArguments().subList(1, tree.getArguments().size());
StringBuilder source = new StringBuilder();
for (ExpressionTree sourceLine : sourceLines) {
String value = ASTHelpers.constValue(sourceLine, String.class);
if (value == null) {
return Optional.empty();
}
source.append(value).append('\n');
}
return Optional.of(source.toString());
}
}
@AutoValue
abstract static class TestCases {
abstract String testClass();
abstract ImmutableList<TestCase> testCases();
}
@AutoValue
abstract static class TestCase {
abstract String classUnderTest();
abstract ImmutableList<TestEntry> entries();
}
interface TestEntry {
String path();
}
@AutoValue
abstract static class ReplacementTestEntry implements TestEntry {
abstract String input();
abstract String output();
}
@AutoValue
abstract static class IdentificationTestEntry implements TestEntry {
abstract String code();
}
}

View File

@@ -5,10 +5,14 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.VisitorState;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskEvent.Kind;
import com.sun.source.util.TaskListener;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.util.Context;
import java.io.File;
@@ -19,6 +23,7 @@ import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ServiceLoader;
import javax.tools.JavaFileObject;
/**
@@ -27,6 +32,13 @@ import javax.tools.JavaFileObject;
*/
// XXX: Find a better name for this class; it doesn't generate documentation per se.
final class DocumentationGeneratorTaskListener implements TaskListener {
@SuppressWarnings({"rawtypes", "unchecked"})
private static final ImmutableList<Extractor<?>> EXTRACTORS =
(ImmutableList)
ImmutableList.copyOf(
ServiceLoader.load(
Extractor.class, DocumentationGeneratorTaskListener.class.getClassLoader()));
private static final ObjectMapper OBJECT_MAPPER =
new ObjectMapper().setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
@@ -51,19 +63,25 @@ final class DocumentationGeneratorTaskListener implements TaskListener {
return;
}
ClassTree classTree = JavacTrees.instance(context).getTree(taskEvent.getTypeElement());
JavaFileObject sourceFile = taskEvent.getSourceFile();
if (classTree == null || sourceFile == null) {
CompilationUnitTree compilationUnit = taskEvent.getCompilationUnit();
ClassTree classTree = JavacTrees.instance(context).getTree(taskEvent.getTypeElement());
if (sourceFile == null || compilationUnit == null || classTree == null) {
return;
}
ExtractorType.findMatchingType(classTree)
.ifPresent(
extractorType ->
writeToFile(
extractorType.getIdentifier(),
getSimpleClassName(sourceFile.toUri()),
extractorType.getExtractor().extract(classTree, context)));
VisitorState state =
VisitorState.createForUtilityPurposes(context)
.withPath(new TreePath(new TreePath(compilationUnit), classTree));
for (Extractor<?> extractor : EXTRACTORS) {
extractor
.tryExtract(classTree, state)
.ifPresent(
data ->
writeToFile(
extractor.identifier(), getSimpleClassName(sourceFile.toUri()), data));
}
}
private void createDocsDirectory() {

View File

@@ -1,8 +1,9 @@
package tech.picnic.errorprone.documentation;
import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.Immutable;
import com.sun.source.tree.ClassTree;
import com.sun.tools.javac.util.Context;
import java.util.Optional;
/**
* Interface implemented by classes that define how to extract data of some type {@link T} from a
@@ -13,21 +14,20 @@ import com.sun.tools.javac.util.Context;
@Immutable
interface Extractor<T> {
/**
* Extracts and returns an instance of {@link T} using the provided arguments.
* Returns the unique identifier of this extractor.
*
* @param tree The {@link ClassTree} to analyze and from which to extract instances of {@link T}.
* @param context The {@link Context} in which the current compilation takes place.
* @return A non-null instance of {@link T}.
* @return A non-{@code null} string.
*/
// XXX: Drop `Context` parameter unless used.
T extract(ClassTree tree, Context context);
String identifier();
/**
* Tells whether this {@link Extractor} can extract documentation content from the given {@link
* ClassTree}.
* Attempts to extract an instance of type {@link T} using the provided arguments.
*
* @param tree The {@link ClassTree} of interest.
* @return {@code true} iff data extraction is supported.
* @param tree The {@link ClassTree} to analyze and from which to extract an instance of type
* {@link T}.
* @param state A {@link VisitorState} describing the context in which the given {@link ClassTree}
* is found.
* @return An instance of type {@link T}, if possible.
*/
boolean canExtract(ClassTree tree);
Optional<T> tryExtract(ClassTree tree, VisitorState state);
}

View File

@@ -1,36 +0,0 @@
package tech.picnic.errorprone.documentation;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.sun.source.tree.ClassTree;
import java.util.EnumSet;
import java.util.Optional;
/** An enumeration of {@link Extractor} types. */
enum ExtractorType {
BUG_PATTERN("bugpattern", new BugPatternExtractor());
private static final ImmutableSet<ExtractorType> TYPES =
Sets.immutableEnumSet(EnumSet.allOf(ExtractorType.class));
private final String identifier;
private final Extractor<?> extractor;
ExtractorType(String identifier, Extractor<?> extractor) {
this.identifier = identifier;
this.extractor = extractor;
}
String getIdentifier() {
return identifier;
}
@SuppressWarnings("java:S1452" /* The extractor returns data of an unspecified type. */)
Extractor<?> getExtractor() {
return extractor;
}
static Optional<ExtractorType> findMatchingType(ClassTree tree) {
return TYPES.stream().filter(type -> type.getExtractor().canExtract(tree)).findFirst();
}
}

View File

@@ -1,18 +1,9 @@
package tech.picnic.errorprone.documentation;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import com.google.common.io.Resources;
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.ClassTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.sun.source.tree.ClassTree;
import java.io.IOException;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
@@ -45,10 +36,7 @@ final class BugPatternExtractorTest {
"@BugPattern(summary = \"MinimalBugChecker summary\", severity = SeverityLevel.ERROR)",
"public final class MinimalBugChecker extends BugChecker {}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-MinimalBugChecker.json",
"bugpattern-documentation-minimal.json");
verifyGeneratedFileContent(outputDirectory, "MinimalBugChecker");
}
@Test
@@ -76,10 +64,7 @@ final class BugPatternExtractorTest {
" suppressionAnnotations = {BugPattern.class, Test.class})",
"public final class CompleteBugChecker extends BugChecker {}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-CompleteBugChecker.json",
"bugpattern-documentation-complete.json");
verifyGeneratedFileContent(outputDirectory, "CompleteBugChecker");
}
@Test
@@ -99,55 +84,23 @@ final class BugPatternExtractorTest {
" documentSuppression = false)",
"public final class UndocumentedSuppressionBugPattern extends BugChecker {}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-UndocumentedSuppressionBugPattern.json",
"bugpattern-documentation-undocumented-suppression.json");
verifyGeneratedFileContent(outputDirectory, "UndocumentedSuppressionBugPattern");
}
@Test
void bugPatternAnnotationIsAbsent() {
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.addSourceLines(
"TestChecker.java",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"// BUG: Diagnostic contains: Can extract: false",
"public final class TestChecker extends BugChecker {}")
.doTest();
}
private static void verifyFileMatchesResource(
Path outputDirectory, String fileName, String resourceName) throws IOException {
assertThat(outputDirectory.resolve(fileName))
private static void verifyGeneratedFileContent(Path outputDirectory, String testClass)
throws IOException {
String resourceName = String.format("bugpattern-%s.json", testClass);
assertThat(outputDirectory.resolve(resourceName))
.content(UTF_8)
.isEqualToIgnoringWhitespace(getResource(resourceName));
.isEqualToIgnoringWhitespace(
getResource(
String.join("-", BugPatternExtractorTest.class.getSimpleName(), resourceName)));
}
// XXX: Once we support only JDK 15+, drop this method in favour of including the resources as
// text blocks in this class. (This also requires renaming the `verifyFileMatchesResource`
// method.)
// text blocks in this class.
private static String getResource(String resourceName) throws IOException {
return Resources.toString(
Resources.getResource(BugPatternExtractorTest.class, resourceName), UTF_8);
}
/** A {@link BugChecker} that validates the {@link BugPatternExtractor}. */
@BugPattern(summary = "Validates `BugPatternExtractor` extraction", severity = ERROR)
public static final class TestChecker extends BugChecker implements ClassTreeMatcher {
private static final long serialVersionUID = 1L;
@Override
public Description matchClass(ClassTree tree, VisitorState state) {
BugPatternExtractor extractor = new BugPatternExtractor();
assertThatThrownBy(() -> extractor.extract(tree, state.context))
.isInstanceOf(NullPointerException.class)
.hasMessage("BugPattern annotation must be present");
return buildDescription(tree)
.setMessage(String.format("Can extract: %s", extractor.canExtract(tree)))
.build();
}
}
}

View File

@@ -0,0 +1,468 @@
package tech.picnic.errorprone.documentation;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.io.Resources;
import java.io.IOException;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
final class BugPatternTestExtractorTest {
@Test
void noTestClass(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerWithoutAnnotation.java",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"public final class TestCheckerWithoutAnnotation extends BugChecker {}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void noDoTestInvocation(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\");",
"",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\");",
" }",
"}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void nullBugCheckerInstance(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" void m() {",
" CompilationTestHelper.newInstance((Class<BugChecker>) null, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance((Class<BugChecker>) null, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void rawBugCheckerInstance(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" @SuppressWarnings(\"unchecked\")",
" void m() {",
" @SuppressWarnings(\"rawtypes\")",
" Class bugChecker = TestChecker.class;",
"",
" CompilationTestHelper.newInstance(bugChecker, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(bugChecker, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void scannerSupplierInstance(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import com.google.errorprone.scanner.ScannerSupplier;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(",
" ScannerSupplier.fromBugCheckerClasses(TestChecker.class), getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(",
" ScannerSupplier.fromBugCheckerClasses(TestChecker.class), getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void nonCompileTimeConstantStrings(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(toString() + \"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .addSourceLines(\"B.java\", \"// BUG: Diagnostic contains:\", \"class B {}\", toString())",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(toString() + \"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .addInputLines(\"B.java\", \"class B {}\", toString())",
" .addOutputLines(\"B.java\", \"class B { /* This is a change. */ }\")",
" .addInputLines(\"C.java\", \"class C {}\")",
" .addOutputLines(\"C.java\", \"class C { /* This is a change. */ }\", toString())",
" .doTest();",
" }",
"}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void nonFluentTestHelperExpressions(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper testHelper =",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"class A {}\");",
" testHelper.doTest();",
"",
" BugCheckerRefactoringTestHelper.ExpectOutput expectedOutput =",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\");",
" expectedOutput.addOutputLines(\"A.java\", \"class A {}\").doTest();",
" expectedOutput.expectUnchanged().doTest();",
" }",
"}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void noSource(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass()).doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass()).doTest();",
" }",
"}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void noDiagnostics(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"class A {}\")",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A {}\")",
" .addInputLines(\"B.java\", \"class B {}\")",
" .expectUnchanged()",
" .doTest();",
" }",
"}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void singleFileCompilationTestHelper(@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"SingleFileCompilationTestHelperTest.java",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class SingleFileCompilationTestHelperTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
" }",
"}");
verifyGeneratedFileContent(outputDirectory, "SingleFileCompilationTestHelperTest");
}
@Test
void singleFileCompilationTestHelperWithSetArgs(@TempDir Path outputDirectory)
throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"SingleFileCompilationTestHelperWithSetArgsTest.java",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class SingleFileCompilationTestHelperWithSetArgsTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .setArgs(\"-XepAllSuggestionsAsWarnings\")",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
" }",
"}");
verifyGeneratedFileContent(outputDirectory, "SingleFileCompilationTestHelperWithSetArgsTest");
}
@Test
void multiFileCompilationTestHelper(@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"MultiFileCompilationTestHelperTest.java",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class MultiFileCompilationTestHelperTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .addSourceLines(\"B.java\", \"// BUG: Diagnostic contains:\", \"class B {}\")",
" .doTest();",
" }",
"}");
verifyGeneratedFileContent(outputDirectory, "MultiFileCompilationTestHelperTest");
}
@Test
void singleFileBugCheckerRefactoringTestHelper(@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"SingleFileBugCheckerRefactoringTestHelperTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class SingleFileBugCheckerRefactoringTestHelperTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
verifyGeneratedFileContent(outputDirectory, "SingleFileBugCheckerRefactoringTestHelperTest");
}
@Test
void singleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestMode(
@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .setArgs(\"-XepAllSuggestionsAsWarnings\")",
" .setFixChooser(FixChoosers.SECOND)",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest(TestMode.TEXT_MATCH);",
" }",
"}");
verifyGeneratedFileContent(
outputDirectory,
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest");
}
@Test
void multiFileBugCheckerRefactoringTestHelper(@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"MultiFileBugCheckerRefactoringTestHelperTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class MultiFileBugCheckerRefactoringTestHelperTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .addInputLines(\"B.java\", \"class B {}\")",
" .addOutputLines(\"B.java\", \"class B { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
verifyGeneratedFileContent(outputDirectory, "MultiFileBugCheckerRefactoringTestHelperTest");
}
@Test
void compilationAndBugCheckerRefactoringTestHelpers(@TempDir Path outputDirectory)
throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"CompilationAndBugCheckerRefactoringTestHelpersTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class CompilationAndBugCheckerRefactoringTestHelpersTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
verifyGeneratedFileContent(
outputDirectory, "CompilationAndBugCheckerRefactoringTestHelpersTest");
}
@Test
void compilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNames(
@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.java",
"package pkg;",
"",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest {",
" private static class CustomTestChecker extends BugChecker {}",
"",
" private static class CustomTestChecker2 extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(CustomTestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(CustomTestChecker2.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
verifyGeneratedFileContent(
outputDirectory,
"CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest");
}
private static void verifyGeneratedFileContent(Path outputDirectory, String testClass)
throws IOException {
String resourceName = String.format("bugpattern-test-%s.json", testClass);
assertThat(outputDirectory.resolve(resourceName))
.content(UTF_8)
.isEqualToIgnoringWhitespace(
getResource(
String.join("-", BugPatternTestExtractorTest.class.getSimpleName(), resourceName)));
}
// XXX: Once we support only JDK 15+, drop this method in favour of including the resources as
// text blocks in this class.
private static String getResource(String resourceName) throws IOException {
return Resources.toString(
Resources.getResource(BugPatternTestExtractorTest.class, resourceName), UTF_8);
}
}

View File

@@ -1,5 +1,7 @@
package tech.picnic.errorprone.documentation;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.FileManagers;
import com.google.errorprone.FileObjects;
@@ -7,39 +9,66 @@ import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.api.JavacTool;
import com.sun.tools.javac.file.JavacFileManager;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
// XXX: Generalize and move this class so that it can also be used by `refaster-compiler`.
// XXX: Add support for this class to the `ErrorProneTestHelperSourceFormat` check.
// XXX: This class is supported by the `ErrorProneTestHelperSourceFormat` check, but until that
// support is covered by unit tests, make sure to update that logic if this class or its methods are
// moved/renamed.
public final class Compilation {
private Compilation() {}
public static void compileWithDocumentationGenerator(
Path outputDirectory, String fileName, String... lines) {
compileWithDocumentationGenerator(outputDirectory.toAbsolutePath().toString(), fileName, lines);
Path outputDirectory, String path, String... lines) {
compileWithDocumentationGenerator(outputDirectory.toAbsolutePath().toString(), path, lines);
}
public static void compileWithDocumentationGenerator(
String outputDirectory, String fileName, String... lines) {
String outputDirectory, String path, String... lines) {
/*
* The compiler options specified here largely match those used by Error Prone's
* `CompilationTestHelper`. A key difference is the stricter linting configuration. When
* compiling using JDK 21+, these lint options also require that certain JDK modules are
* explicitly exported.
*/
compile(
ImmutableList.of("-Xplugin:DocumentationGenerator -XoutputDirectory=" + outputDirectory),
FileObjects.forSourceLines(fileName, lines));
ImmutableList.of(
"--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
"-encoding",
"UTF-8",
"-parameters",
"-proc:none",
"-Werror",
"-Xlint:all,-serial",
"-Xplugin:DocumentationGenerator -XoutputDirectory=" + outputDirectory,
"-XDdev",
"-XDcompilePolicy=simple"),
FileObjects.forSourceLines(path, lines));
}
private static void compile(ImmutableList<String> options, JavaFileObject javaFileObject) {
JavacFileManager javacFileManager = FileManagers.testFileManager();
JavaCompiler compiler = JavacTool.create();
List<Diagnostic<?>> diagnostics = new ArrayList<>();
JavacTaskImpl task =
(JavacTaskImpl)
compiler.getTask(
null,
javacFileManager,
null,
diagnostics::add,
options,
ImmutableList.of(),
ImmutableList.of(javaFileObject));
task.call();
Boolean result = task.call();
assertThat(diagnostics).isEmpty();
assertThat(result).isTrue();
}
}

View File

@@ -1,19 +1,29 @@
package tech.picnic.errorprone.documentation;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.attribute.AclEntryPermission.ADD_SUBDIRECTORY;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.condition.OS.WINDOWS;
import com.google.auto.service.AutoService;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.Immutable;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.Tree;
import java.io.IOException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.AclEntry;
import java.nio.file.attribute.AclFileAttributeView;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.EnabledOnOs;
@@ -75,4 +85,56 @@ final class DocumentationGeneratorTaskListenerTest {
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Precisely one path must be provided");
}
@Test
void extraction(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"DocumentationGeneratorTaskListenerTestClass.java",
"class DocumentationGeneratorTaskListenerTestClass {}");
// XXX: Once we support only JDK 15+, use a text block for the `expected` string.
assertThat(
outputDirectory.resolve(
"documentation-generator-task-listener-test-DocumentationGeneratorTaskListenerTestClass.json"))
.content(UTF_8)
.isEqualToIgnoringWhitespace(
"{\"className\":\"DocumentationGeneratorTaskListenerTestClass\",\"path\":[\"CLASS: DocumentationGeneratorTaskListenerTestClass\",\"COMPILATION_UNIT\"]}");
}
@Immutable
@AutoService(Extractor.class)
@SuppressWarnings("rawtypes" /* See https://github.com/google/auto/issues/870. */)
public static final class TestExtractor implements Extractor<ExtractionParameters> {
@Override
public String identifier() {
return "documentation-generator-task-listener-test";
}
@Override
public Optional<ExtractionParameters> tryExtract(ClassTree tree, VisitorState state) {
return Optional.of(tree.getSimpleName().toString())
.filter(n -> n.contains(DocumentationGeneratorTaskListenerTest.class.getSimpleName()))
.map(
className ->
new AutoValue_DocumentationGeneratorTaskListenerTest_ExtractionParameters(
className,
Streams.stream(state.getPath())
.map(TestExtractor::describeTree)
.collect(toImmutableList())));
}
private static String describeTree(Tree tree) {
return (tree instanceof ClassTree)
? String.join(": ", String.valueOf(tree.getKind()), ((ClassTree) tree).getSimpleName())
: tree.getKind().toString();
}
}
@AutoValue
abstract static class ExtractionParameters {
abstract String className();
abstract ImmutableList<String> path();
}
}

View File

@@ -0,0 +1,24 @@
{
"testClass": "CompilationAndBugCheckerRefactoringTestHelpersTest",
"testCases": [
{
"classUnderTest": "CompilationAndBugCheckerRefactoringTestHelpersTest.TestChecker",
"entries": [
{
"path": "A.java",
"code": "// BUG: Diagnostic contains:\nclass A {}\n"
}
]
},
{
"classUnderTest": "CompilationAndBugCheckerRefactoringTestHelpersTest.TestChecker",
"entries": [
{
"path": "A.java",
"input": "class A {}\n",
"output": "class A { /* This is a change. */ }\n"
}
]
}
]
}

View File

@@ -0,0 +1,24 @@
{
"testClass": "pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest",
"testCases": [
{
"classUnderTest": "pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.CustomTestChecker",
"entries": [
{
"path": "A.java",
"code": "// BUG: Diagnostic contains:\nclass A {}\n"
}
]
},
{
"classUnderTest": "pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.CustomTestChecker2",
"entries": [
{
"path": "A.java",
"input": "class A {}\n",
"output": "class A { /* This is a change. */ }\n"
}
]
}
]
}

View File

@@ -0,0 +1,20 @@
{
"testClass": "MultiFileBugCheckerRefactoringTestHelperTest",
"testCases": [
{
"classUnderTest": "MultiFileBugCheckerRefactoringTestHelperTest.TestChecker",
"entries": [
{
"path": "A.java",
"input": "class A {}\n",
"output": "class A { /* This is a change. */ }\n"
},
{
"path": "B.java",
"input": "class B {}\n",
"output": "class B { /* This is a change. */ }\n"
}
]
}
]
}

View File

@@ -0,0 +1,18 @@
{
"testClass": "MultiFileCompilationTestHelperTest",
"testCases": [
{
"classUnderTest": "MultiFileCompilationTestHelperTest.TestChecker",
"entries": [
{
"path": "A.java",
"code": "// BUG: Diagnostic contains:\nclass A {}\n"
},
{
"path": "B.java",
"code": "// BUG: Diagnostic contains:\nclass B {}\n"
}
]
}
]
}

View File

@@ -0,0 +1,15 @@
{
"testClass": "SingleFileBugCheckerRefactoringTestHelperTest",
"testCases": [
{
"classUnderTest": "SingleFileBugCheckerRefactoringTestHelperTest.TestChecker",
"entries": [
{
"path": "A.java",
"input": "class A {}\n",
"output": "class A { /* This is a change. */ }\n"
}
]
}
]
}

View File

@@ -0,0 +1,15 @@
{
"testClass": "SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest",
"testCases": [
{
"classUnderTest": "SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest.TestChecker",
"entries": [
{
"path": "A.java",
"input": "class A {}\n",
"output": "class A { /* This is a change. */ }\n"
}
]
}
]
}

View File

@@ -0,0 +1,14 @@
{
"testClass": "SingleFileCompilationTestHelperTest",
"testCases": [
{
"classUnderTest": "SingleFileCompilationTestHelperTest.TestChecker",
"entries": [
{
"path": "A.java",
"code": "// BUG: Diagnostic contains:\nclass A {}\n"
}
]
}
]
}

View File

@@ -0,0 +1,14 @@
{
"testClass": "SingleFileCompilationTestHelperWithSetArgsTest",
"testCases": [
{
"classUnderTest": "SingleFileCompilationTestHelperWithSetArgsTest.TestChecker",
"entries": [
{
"path": "A.java",
"code": "// BUG: Diagnostic contains:\nclass A {}\n"
}
]
}
]
}

View File

@@ -72,6 +72,11 @@
<artifactId>auto-service-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.googlejavaformat</groupId>
<artifactId>google-java-format</artifactId>
@@ -248,17 +253,6 @@
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<ignoredUnusedDeclaredDependencies>
<!-- XXX: Figure out why the plugin thinks this
dependency is unused. -->
<ignoredUnusedDeclaredDependency>${project.groupId}:refaster-support</ignoredUnusedDeclaredDependency>
</ignoredUnusedDeclaredDependencies>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>

View File

@@ -9,7 +9,6 @@ import static com.google.errorprone.matchers.Matchers.isType;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
@@ -17,6 +16,7 @@ import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.MultiMatcher;
import com.google.errorprone.matchers.MultiMatcher.MultiMatchResult;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
@@ -48,11 +48,9 @@ public final class AutowiredConstructor extends BugChecker implements ClassTreeM
return Description.NO_MATCH;
}
ImmutableList<AnnotationTree> annotations =
AUTOWIRED_ANNOTATION
.multiMatchResult(Iterables.getOnlyElement(constructors), state)
.matchingNodes();
if (annotations.size() != 1) {
MultiMatchResult<AnnotationTree> hasAutowiredAnnotation =
AUTOWIRED_ANNOTATION.multiMatchResult(Iterables.getOnlyElement(constructors), state);
if (!hasAutowiredAnnotation.matches()) {
return Description.NO_MATCH;
}
@@ -61,7 +59,7 @@ public final class AutowiredConstructor extends BugChecker implements ClassTreeM
* means that the associated import can be removed as well. Rather than adding code for this
* case we leave flagging the unused import to Error Prone's `RemoveUnusedImports` check.
*/
AnnotationTree annotation = Iterables.getOnlyElement(annotations);
AnnotationTree annotation = hasAutowiredAnnotation.onlyMatchingNode();
return describeMatch(annotation, SourceCode.deleteWithTrailingWhitespace(annotation, state));
}
}

View File

@@ -5,6 +5,7 @@ import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
import static com.google.errorprone.BugPattern.StandardTags.STYLE;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.instanceMethod;
import static com.google.errorprone.matchers.Matchers.staticMethod;
import static java.util.stream.Collectors.joining;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
@@ -66,7 +67,12 @@ public final class ErrorProneTestHelperSourceFormat extends BugChecker
.named("addSourceLines"),
instanceMethod()
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper")
.named("addInputLines"));
.named("addInputLines"),
// XXX: Add tests for `Compilation.compileWithDocumentationGenerator`. Until done, make
// sure to update this matcher if that method's class or name is changed/moved.
staticMethod()
.onClass("tech.picnic.errorprone.documentation.Compilation")
.named("compileWithDocumentationGenerator"));
private static final Matcher<ExpressionTree> OUTPUT_SOURCE_ACCEPTING_METHOD =
instanceMethod()
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper.ExpectOutput")
@@ -83,7 +89,8 @@ public final class ErrorProneTestHelperSourceFormat extends BugChecker
}
List<? extends ExpressionTree> sourceLines =
tree.getArguments().subList(1, tree.getArguments().size());
tree.getArguments()
.subList(ASTHelpers.getSymbol(tree).params().size() - 1, tree.getArguments().size());
if (sourceLines.isEmpty()) {
return buildDescription(tree).setMessage("No source code provided").build();
}
@@ -149,12 +156,13 @@ public final class ErrorProneTestHelperSourceFormat extends BugChecker
return FORMATTER.formatSource(withOptionallyRemovedImports);
}
// XXX: This logic is duplicated in `BugPatternTestExtractor`. Can we do better?
private static Optional<String> getConstantSourceCode(
List<? extends ExpressionTree> sourceLines) {
StringBuilder source = new StringBuilder();
for (ExpressionTree sourceLine : sourceLines) {
Object value = ASTHelpers.constValue(sourceLine);
String value = ASTHelpers.constValue(sourceLine, String.class);
if (value == null) {
return Optional.empty();
}

View File

@@ -37,6 +37,8 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
// the target method such a modification may change the code's semantics or performance.
// XXX: Also flag `Stream#map`, `Mono#map` and `Flux#map` invocations where the given transformation
// is effectively the identity operation.
// XXX: Also flag nullary instance method invocations that represent an identity conversion, such as
// `Boolean#booleanValue()`, `Byte#byteValue()` and friends.
@AutoService(BugChecker.class)
@BugPattern(
summary = "Avoid or clarify identity conversions",

View File

@@ -0,0 +1,91 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
import static com.google.errorprone.matchers.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
import static com.google.errorprone.matchers.Matchers.annotations;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.isType;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import static tech.picnic.errorprone.bugpatterns.util.MoreMatchers.hasMetaAnnotation;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.fixes.SuggestedFixes;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.MultiMatcher;
import com.google.errorprone.matchers.MultiMatcher.MultiMatchResult;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.MethodTree;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
/**
* A {@link BugChecker} that flags nullary {@link
* org.junit.jupiter.params.ParameterizedTest @ParameterizedTest} test methods.
*
* <p>Such tests are unnecessarily executed more than necessary. This checker suggests annotating
* the method with {@link org.junit.jupiter.api.Test @Test}, and to drop all declared {@link
* org.junit.jupiter.params.provider.ArgumentsSource argument sources}.
*/
@AutoService(BugChecker.class)
@BugPattern(
summary = "Nullary JUnit test methods should not be parameterized",
link = BUG_PATTERNS_BASE_URL + "JUnitNullaryParameterizedTestDeclaration",
linkType = CUSTOM,
severity = SUGGESTION,
tags = SIMPLIFICATION)
public final class JUnitNullaryParameterizedTestDeclaration extends BugChecker
implements MethodTreeMatcher {
private static final long serialVersionUID = 1L;
private static final MultiMatcher<MethodTree, AnnotationTree> IS_PARAMETERIZED_TEST =
annotations(AT_LEAST_ONE, isType("org.junit.jupiter.params.ParameterizedTest"));
private static final Matcher<AnnotationTree> IS_ARGUMENT_SOURCE =
anyOf(
isType("org.junit.jupiter.params.provider.ArgumentsSource"),
isType("org.junit.jupiter.params.provider.ArgumentsSources"),
hasMetaAnnotation("org.junit.jupiter.params.provider.ArgumentsSource"));
/** Instantiates a new {@link JUnitNullaryParameterizedTestDeclaration} instance. */
public JUnitNullaryParameterizedTestDeclaration() {}
@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
if (!tree.getParameters().isEmpty()) {
return Description.NO_MATCH;
}
MultiMatchResult<AnnotationTree> isParameterizedTest =
IS_PARAMETERIZED_TEST.multiMatchResult(tree, state);
if (!isParameterizedTest.matches()) {
return Description.NO_MATCH;
}
/*
* This method is vacuously parameterized. Suggest replacing `@ParameterizedTest` with `@Test`.
* (As each method is checked independently, we cannot in general determine whether this
* suggestion makes a `ParameterizedTest` type import obsolete; that task is left to Error
* Prone's `RemoveUnusedImports` check.)
*/
SuggestedFix.Builder fix = SuggestedFix.builder();
fix.merge(
SuggestedFix.replace(
isParameterizedTest.onlyMatchingNode(),
'@' + SuggestedFixes.qualifyType(state, fix, "org.junit.jupiter.api.Test")));
/*
* Also suggest dropping all (explicit and implicit) `@ArgumentsSource`s. No attempt is made to
* assess whether a dropped `@MethodSource` also makes the referenced factory method(s) unused.
*/
tree.getModifiers().getAnnotations().stream()
.filter(a -> IS_ARGUMENT_SOURCE.matches(a, state))
.forEach(a -> fix.merge(SourceCode.deleteWithTrailingWhitespace(a, state)));
return describeMatch(tree, fix.build());
}
}

View File

@@ -71,7 +71,7 @@ public final class MockitoMockClassReference extends BugChecker
Tree parent = state.getPath().getParentPath().getLeaf();
switch (parent.getKind()) {
case VARIABLE:
return !ASTHelpers.hasNoExplicitType((VariableTree) parent, state)
return !ASTHelpers.hasImplicitType((VariableTree) parent, state)
&& MoreASTHelpers.areSameType(tree, parent, state);
case ASSIGNMENT:
return MoreASTHelpers.areSameType(tree, parent, state);

View File

@@ -0,0 +1,215 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
import static com.google.errorprone.BugPattern.StandardTags.STYLE;
import static tech.picnic.errorprone.bugpatterns.StaticImport.STATIC_IMPORT_CANDIDATE_MEMBERS;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableTable;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.CompilationUnitTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.fixes.SuggestedFixes;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.ImportTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.TreeScanner;
import com.sun.tools.javac.code.Symbol;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
/**
* A {@link BugChecker} that flags static imports of type members that should *not* be statically
* imported.
*/
// XXX: This check is closely linked to `StaticImport`. Consider merging the two.
// XXX: Add suppression support. If qualification of one more more identifiers is suppressed, then
// the associated static import should *not* be removed.
// XXX: Also introduce logic that disallows statically importing `ZoneOffset.ofHours` and other
// `ofXXX`-style methods.
@AutoService(BugChecker.class)
@BugPattern(
summary = "Member should not be statically imported",
link = BUG_PATTERNS_BASE_URL + "NonStaticImport",
linkType = CUSTOM,
severity = SUGGESTION,
tags = STYLE)
public final class NonStaticImport extends BugChecker implements CompilationUnitTreeMatcher {
private static final long serialVersionUID = 1L;
/**
* Types whose members should not be statically imported, unless exempted by {@link
* StaticImport#STATIC_IMPORT_CANDIDATE_MEMBERS}.
*
* <p>Types listed here should be mutually exclusive with {@link
* StaticImport#STATIC_IMPORT_CANDIDATE_TYPES}.
*/
@VisibleForTesting
static final ImmutableSet<String> NON_STATIC_IMPORT_CANDIDATE_TYPES =
ImmutableSet.of(
"com.google.common.base.Strings",
"com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode",
"java.time.Clock",
"java.time.ZoneOffset");
/**
* Type members that should never be statically imported.
*
* <p>Please note that:
*
* <ul>
* <li>Types listed by {@link #NON_STATIC_IMPORT_CANDIDATE_TYPES} and members listed by {@link
* #NON_STATIC_IMPORT_CANDIDATE_IDENTIFIERS} should be omitted from this collection.
* <li>This collection should be mutually exclusive with {@link
* StaticImport#STATIC_IMPORT_CANDIDATE_MEMBERS}.
* </ul>
*/
// XXX: Perhaps the set of exempted `java.util.Collections` methods is too strict. For now any
// method name that could be considered "too vague" or could conceivably mean something else in a
// specific context is left out.
static final ImmutableSetMultimap<String, String> NON_STATIC_IMPORT_CANDIDATE_MEMBERS =
ImmutableSetMultimap.<String, String>builder()
.put("com.google.common.base.Predicates", "contains")
.putAll(
"java.util.Collections",
"addAll",
"copy",
"fill",
"list",
"max",
"min",
"nCopies",
"rotate",
"sort",
"swap")
.put("java.util.Locale", "ROOT")
.putAll("java.util.regex.Pattern", "compile", "matches", "quote")
.put("org.springframework.http.MediaType", "ALL")
.build();
/**
* Identifiers that should never be statically imported.
*
* <p>Please note that:
*
* <ul>
* <li>Identifiers listed by {@link StaticImport#STATIC_IMPORT_CANDIDATE_MEMBERS} should be
* mutually exclusive with identifiers listed here.
* <li>This list should contain a superset of the identifiers flagged by {@link
* com.google.errorprone.bugpatterns.BadImport}.
* </ul>
*/
static final ImmutableSet<String> NON_STATIC_IMPORT_CANDIDATE_IDENTIFIERS =
ImmutableSet.of(
"builder",
"copyOf",
"create",
"empty",
"from",
"getDefaultInstance",
"INSTANCE",
"MAX",
"MAX_VALUE",
"MIN",
"MIN_VALUE",
"newBuilder",
"newInstance",
"of",
"ONE",
"parse",
"valueOf",
"ZERO");
/** Instantiates a new {@link NonStaticImport} instance. */
public NonStaticImport() {}
@Override
public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) {
ImmutableTable<String, String, UndesiredStaticImport> undesiredStaticImports =
getUndesiredStaticImports(tree, state);
if (!undesiredStaticImports.isEmpty()) {
replaceUndesiredStaticImportUsages(tree, undesiredStaticImports, state);
for (UndesiredStaticImport staticImport : undesiredStaticImports.values()) {
state.reportMatch(
describeMatch(staticImport.importTree(), staticImport.fixBuilder().build()));
}
}
/* Any violations have been flagged against the offending static import statement. */
return Description.NO_MATCH;
}
private static ImmutableTable<String, String, UndesiredStaticImport> getUndesiredStaticImports(
CompilationUnitTree tree, VisitorState state) {
ImmutableTable.Builder<String, String, UndesiredStaticImport> imports =
ImmutableTable.builder();
for (ImportTree importTree : tree.getImports()) {
Tree qualifiedIdentifier = importTree.getQualifiedIdentifier();
if (importTree.isStatic() && qualifiedIdentifier instanceof MemberSelectTree) {
MemberSelectTree memberSelectTree = (MemberSelectTree) qualifiedIdentifier;
String type = SourceCode.treeToString(memberSelectTree.getExpression(), state);
String member = memberSelectTree.getIdentifier().toString();
if (shouldNotBeStaticallyImported(type, member)) {
imports.put(
type,
member,
new AutoValue_NonStaticImport_UndesiredStaticImport(
importTree, SuggestedFix.builder().removeStaticImport(type + '.' + member)));
}
}
}
return imports.build();
}
private static boolean shouldNotBeStaticallyImported(String type, String member) {
return (NON_STATIC_IMPORT_CANDIDATE_TYPES.contains(type)
&& !STATIC_IMPORT_CANDIDATE_MEMBERS.containsEntry(type, member))
|| NON_STATIC_IMPORT_CANDIDATE_MEMBERS.containsEntry(type, member)
|| NON_STATIC_IMPORT_CANDIDATE_IDENTIFIERS.contains(member);
}
private static void replaceUndesiredStaticImportUsages(
CompilationUnitTree tree,
ImmutableTable<String, String, UndesiredStaticImport> undesiredStaticImports,
VisitorState state) {
new TreeScanner<@Nullable Void, @Nullable Void>() {
@Override
public @Nullable Void visitIdentifier(IdentifierTree node, @Nullable Void unused) {
Symbol symbol = ASTHelpers.getSymbol(node);
if (symbol != null) {
UndesiredStaticImport staticImport =
undesiredStaticImports.get(
symbol.owner.getQualifiedName().toString(), symbol.name.toString());
if (staticImport != null) {
SuggestedFix.Builder fix = staticImport.fixBuilder();
fix.prefixWith(node, SuggestedFixes.qualifyType(state, fix, symbol.owner) + '.');
}
}
return super.visitIdentifier(node, unused);
}
}.scan(tree, null);
}
@AutoValue
abstract static class UndesiredStaticImport {
abstract ImportTree importTree();
abstract SuggestedFix.Builder fixBuilder();
}
}

View File

@@ -2,8 +2,10 @@ package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
import static com.google.errorprone.BugPattern.StandardTags.STYLE;
import static java.util.Objects.requireNonNull;
import static tech.picnic.errorprone.bugpatterns.NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_IDENTIFIERS;
import static tech.picnic.errorprone.bugpatterns.NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_MEMBERS;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
@@ -27,35 +29,30 @@ import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Type;
import java.util.Optional;
/**
* A {@link BugChecker} that flags methods and constants that can and should be statically imported.
*/
/** A {@link BugChecker} that flags type members that can and should be statically imported. */
// XXX: This check is closely linked to `NonStaticImport`. Consider merging the two.
// XXX: Tricky cases:
// - `org.springframework.http.HttpStatus` (not always an improvement, and `valueOf` must
// certainly be excluded)
// - `com.google.common.collect.Tables`
// - `ch.qos.logback.classic.Level.{DEBUG, ERROR, INFO, TRACE, WARN"}`
// XXX: Also introduce a check that disallows static imports of certain methods. Candidates:
// - `com.google.common.base.Strings`
// - `java.util.Optional.empty`
// - `java.util.Locale.ROOT`
// - `ZoneOffset.ofHours` and other `ofXXX`-style methods.
// - `java.time.Clock`.
// - Several other `java.time` classes.
// - Likely any of `*.{ZERO, ONE, MIX, MAX, MIN_VALUE, MAX_VALUE}`.
// - `ch.qos.logback.classic.Level.{DEBUG, ERROR, INFO, TRACE, WARN}`
@AutoService(BugChecker.class)
@BugPattern(
summary = "Identifier should be statically imported",
link = BUG_PATTERNS_BASE_URL + "StaticImport",
linkType = CUSTOM,
severity = SUGGESTION,
tags = SIMPLIFICATION)
tags = STYLE)
public final class StaticImport extends BugChecker implements MemberSelectTreeMatcher {
private static final long serialVersionUID = 1L;
/**
* Types whose members should be statically imported, unless exempted by {@link
* #STATIC_IMPORT_EXEMPTED_MEMBERS} or {@link #STATIC_IMPORT_EXEMPTED_IDENTIFIERS}.
* NonStaticImport#NON_STATIC_IMPORT_CANDIDATE_MEMBERS} or {@link
* NonStaticImport#NON_STATIC_IMPORT_CANDIDATE_IDENTIFIERS}.
*
* <p>Types listed here should be mutually exclusive with {@link
* NonStaticImport#NON_STATIC_IMPORT_CANDIDATE_TYPES}.
*/
@VisibleForTesting
static final ImmutableSet<String> STATIC_IMPORT_CANDIDATE_TYPES =
@@ -104,8 +101,20 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa
"reactor.function.TupleUtils",
"tech.picnic.errorprone.bugpatterns.util.MoreTypes");
/** Type members that should be statically imported. */
@VisibleForTesting
/**
* Type members that should be statically imported.
*
* <p>Please note that:
*
* <ul>
* <li>Types listed by {@link #STATIC_IMPORT_CANDIDATE_TYPES} should be omitted from this
* collection.
* <li>This collection should be mutually exclusive with {@link
* NonStaticImport#NON_STATIC_IMPORT_CANDIDATE_MEMBERS}.
* <li>This collection should not list members contained in {@link
* NonStaticImport#NON_STATIC_IMPORT_CANDIDATE_IDENTIFIERS}.
* </ul>
*/
static final ImmutableSetMultimap<String, String> STATIC_IMPORT_CANDIDATE_MEMBERS =
ImmutableSetMultimap.<String, String>builder()
.putAll(
@@ -143,55 +152,6 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa
.putAll("com.google.common.collect.Comparators", "emptiesFirst", "emptiesLast")
.build();
/**
* Type members that should never be statically imported.
*
* <p>Identifiers listed by {@link #STATIC_IMPORT_EXEMPTED_IDENTIFIERS} should be omitted from
* this collection.
*/
// XXX: Perhaps the set of exempted `java.util.Collections` methods is too strict. For now any
// method name that could be considered "too vague" or could conceivably mean something else in a
// specific context is left out.
@VisibleForTesting
static final ImmutableSetMultimap<String, String> STATIC_IMPORT_EXEMPTED_MEMBERS =
ImmutableSetMultimap.<String, String>builder()
.put("com.google.common.base.Predicates", "contains")
.put("com.mongodb.client.model.Filters", "empty")
.putAll(
"java.util.Collections",
"addAll",
"copy",
"fill",
"list",
"max",
"min",
"nCopies",
"rotate",
"sort",
"swap")
.putAll("java.util.regex.Pattern", "compile", "matches", "quote")
.put("org.springframework.http.MediaType", "ALL")
.build();
/**
* Identifiers that should never be statically imported.
*
* <p>This should be a superset of the identifiers flagged by {@link
* com.google.errorprone.bugpatterns.BadImport}.
*/
@VisibleForTesting
static final ImmutableSet<String> STATIC_IMPORT_EXEMPTED_IDENTIFIERS =
ImmutableSet.of(
"builder",
"create",
"copyOf",
"from",
"getDefaultInstance",
"INSTANCE",
"newBuilder",
"of",
"valueOf");
/** Instantiates a new {@link StaticImport} instance. */
public StaticImport() {}
@@ -229,13 +189,13 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa
private static boolean isCandidate(MemberSelectTree tree) {
String identifier = tree.getIdentifier().toString();
if (STATIC_IMPORT_EXEMPTED_IDENTIFIERS.contains(identifier)) {
if (NON_STATIC_IMPORT_CANDIDATE_IDENTIFIERS.contains(identifier)) {
return false;
}
Type type = ASTHelpers.getType(tree.getExpression());
return type != null
&& !STATIC_IMPORT_EXEMPTED_MEMBERS.containsEntry(type.toString(), identifier);
&& !NON_STATIC_IMPORT_CANDIDATE_MEMBERS.containsEntry(type.toString(), identifier);
}
private static Optional<String> getCandidateSimpleName(StaticImportInfo importInfo) {

View File

@@ -23,7 +23,7 @@ import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.util.Convert;
import com.sun.tools.javac.util.Constants;
import java.util.Formattable;
import java.util.Iterator;
import java.util.List;
@@ -150,7 +150,7 @@ public final class StringJoin extends BugChecker implements MethodInvocationTree
SuggestedFix.Builder fix =
SuggestedFix.builder()
.replace(tree.getMethodSelect(), "String.join")
.replace(arguments.next(), String.format("\"%s\"", Convert.quote(separator)));
.replace(arguments.next(), Constants.format(separator));
while (arguments.hasNext()) {
ExpressionTree argument = arguments.next();

View File

@@ -3,80 +3,40 @@ package tech.picnic.errorprone.refasterrules;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedMultiset;
import com.google.common.collect.ImmutableSortedSet;
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.Matches;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AbstractBooleanAssert;
import org.assertj.core.api.AbstractCollectionAssert;
import org.assertj.core.api.AbstractMapAssert;
import org.assertj.core.api.MapAssert;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
import tech.picnic.errorprone.refaster.matchers.IsEmpty;
@OnlineDocumentation
final class AssertJMapRules {
private AssertJMapRules() {}
// XXX: Reduce boilerplate using a `Matcher` that identifies "empty" instances.
static final class AbstractMapAssertIsEmpty<K, V> {
@BeforeTemplate
@SuppressWarnings("unchecked")
void before(AbstractMapAssert<?, ?, K, V> mapAssert) {
void before(
AbstractMapAssert<?, ?, K, V> mapAssert,
@Matches(IsEmpty.class) Map<? extends K, ? extends V> wellTypedMap,
@Matches(IsEmpty.class) Map<?, ?> arbitrarilyTypedMap,
@Matches(IsEmpty.class) Iterable<? extends K> keys) {
Refaster.anyOf(
mapAssert.containsExactlyEntriesOf(
Refaster.anyOf(
ImmutableMap.of(),
ImmutableBiMap.of(),
ImmutableSortedMap.of(),
new HashMap<>(),
new LinkedHashMap<>(),
new TreeMap<>())),
mapAssert.hasSameSizeAs(
Refaster.anyOf(
ImmutableMap.of(),
ImmutableBiMap.of(),
ImmutableSortedMap.of(),
new HashMap<>(),
new LinkedHashMap<>(),
new TreeMap<>())),
mapAssert.isEqualTo(
Refaster.anyOf(
ImmutableMap.of(),
ImmutableBiMap.of(),
ImmutableSortedMap.of(),
new HashMap<>(),
new LinkedHashMap<>(),
new TreeMap<>())),
mapAssert.containsOnlyKeys(
Refaster.anyOf(
ImmutableList.of(),
new ArrayList<>(),
ImmutableSet.of(),
new HashSet<>(),
new LinkedHashSet<>(),
ImmutableSortedSet.of(),
new TreeSet<>(),
ImmutableMultiset.of(),
ImmutableSortedMultiset.of())),
mapAssert.containsExactlyEntriesOf(wellTypedMap),
mapAssert.containsExactlyInAnyOrderEntriesOf(wellTypedMap),
mapAssert.hasSameSizeAs(arbitrarilyTypedMap),
mapAssert.isEqualTo(arbitrarilyTypedMap),
mapAssert.containsOnlyKeys(keys),
mapAssert.containsExactly(),
mapAssert.containsOnly(),
mapAssert.containsOnlyKeys());
@@ -112,15 +72,9 @@ final class AssertJMapRules {
static final class AbstractMapAssertIsNotEmpty<K, V> {
@BeforeTemplate
AbstractMapAssert<?, ?, K, V> before(AbstractMapAssert<?, ?, K, V> mapAssert) {
return mapAssert.isNotEqualTo(
Refaster.anyOf(
ImmutableMap.of(),
ImmutableBiMap.of(),
ImmutableSortedMap.of(),
new HashMap<>(),
new LinkedHashMap<>(),
new TreeMap<>()));
AbstractMapAssert<?, ?, K, V> before(
AbstractMapAssert<?, ?, K, V> mapAssert, @Matches(IsEmpty.class) Map<?, ?> map) {
return mapAssert.isNotEqualTo(map);
}
@AfterTemplate
@@ -148,12 +102,14 @@ final class AssertJMapRules {
static final class AbstractMapAssertContainsExactlyInAnyOrderEntriesOf<K, V> {
@BeforeTemplate
AbstractMapAssert<?, ?, K, V> before(AbstractMapAssert<?, ?, K, V> mapAssert, Map<K, V> map) {
AbstractMapAssert<?, ?, K, V> before(
AbstractMapAssert<?, ?, K, V> mapAssert, Map<? extends K, ? extends V> map) {
return mapAssert.isEqualTo(map);
}
@AfterTemplate
AbstractMapAssert<?, ?, K, V> after(AbstractMapAssert<?, ?, K, V> mapAssert, Map<K, V> map) {
AbstractMapAssert<?, ?, K, V> after(
AbstractMapAssert<?, ?, K, V> mapAssert, Map<? extends K, ? extends V> map) {
return mapAssert.containsExactlyInAnyOrderEntriesOf(map);
}
}
@@ -187,12 +143,12 @@ final class AssertJMapRules {
static final class AbstractMapAssertHasSameSizeAs<K, V> {
@BeforeTemplate
AbstractMapAssert<?, ?, K, V> before(AbstractMapAssert<?, ?, K, V> mapAssert, Map<K, V> map) {
AbstractMapAssert<?, ?, K, V> before(AbstractMapAssert<?, ?, K, V> mapAssert, Map<?, ?> map) {
return mapAssert.hasSize(map.size());
}
@AfterTemplate
AbstractMapAssert<?, ?, K, V> after(AbstractMapAssert<?, ?, K, V> mapAssert, Map<K, V> map) {
AbstractMapAssert<?, ?, K, V> after(AbstractMapAssert<?, ?, K, V> mapAssert, Map<?, ?> map) {
return mapAssert.hasSameSizeAs(map);
}
}
@@ -225,13 +181,14 @@ final class AssertJMapRules {
static final class AssertThatMapContainsOnlyKeys<K, V> {
@BeforeTemplate
AbstractCollectionAssert<?, Collection<? extends K>, K, ?> before(Map<K, V> map, Set<K> keys) {
AbstractCollectionAssert<?, Collection<? extends K>, K, ?> before(
Map<K, V> map, Set<? extends K> keys) {
return assertThat(map.keySet()).hasSameElementsAs(keys);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
MapAssert<K, V> after(Map<K, V> map, Set<K> keys) {
MapAssert<K, V> after(Map<K, V> map, Set<? extends K> keys) {
return assertThat(map).containsOnlyKeys(keys);
}
}

View File

@@ -6,28 +6,23 @@ import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMultiset;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multiset;
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.Matches;
import com.google.errorprone.refaster.annotation.NotMatches;
import com.google.errorprone.refaster.annotation.Repeated;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Stream;
@@ -47,6 +42,7 @@ import org.assertj.core.api.OptionalIntAssert;
import org.assertj.core.api.OptionalLongAssert;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
import tech.picnic.errorprone.refaster.matchers.IsArray;
import tech.picnic.errorprone.refaster.matchers.IsEmpty;
/** Refaster rules related to AssertJ expressions and statements. */
// XXX: Most `AbstractIntegerAssert` rules can also be applied for other primitive types. Generate
@@ -181,63 +177,16 @@ final class AssertJRules {
static final class AssertThatObjectEnumerableIsEmpty<E> {
@BeforeTemplate
@SuppressWarnings("unchecked")
void before(ObjectEnumerableAssert<?, E> enumAssert) {
void before(
ObjectEnumerableAssert<?, E> enumAssert,
@Matches(IsEmpty.class) Iterable<? extends E> wellTypedIterable,
@Matches(IsEmpty.class) Iterable<?> arbitrarilyTypedIterable) {
Refaster.anyOf(
enumAssert.containsExactlyElementsOf(
Refaster.anyOf(
ImmutableList.of(),
new ArrayList<>(),
ImmutableSet.of(),
new HashSet<>(),
new LinkedHashSet<>(),
ImmutableSortedSet.of(),
new TreeSet<>(),
ImmutableMultiset.of(),
ImmutableSortedMultiset.of())),
enumAssert.containsExactlyInAnyOrderElementsOf(
Refaster.anyOf(
ImmutableList.of(),
new ArrayList<>(),
ImmutableSet.of(),
new HashSet<>(),
new LinkedHashSet<>(),
ImmutableSortedSet.of(),
new TreeSet<>(),
ImmutableMultiset.of(),
ImmutableSortedMultiset.of())),
enumAssert.hasSameElementsAs(
Refaster.anyOf(
ImmutableList.of(),
new ArrayList<>(),
ImmutableSet.of(),
new HashSet<>(),
new LinkedHashSet<>(),
ImmutableSortedSet.of(),
new TreeSet<>(),
ImmutableMultiset.of(),
ImmutableSortedMultiset.of())),
enumAssert.hasSameSizeAs(
Refaster.anyOf(
ImmutableList.of(),
new ArrayList<>(),
ImmutableSet.of(),
new HashSet<>(),
new LinkedHashSet<>(),
ImmutableSortedSet.of(),
new TreeSet<>(),
ImmutableMultiset.of(),
ImmutableSortedMultiset.of())),
enumAssert.isSubsetOf(
Refaster.anyOf(
ImmutableList.of(),
new ArrayList<>(),
ImmutableSet.of(),
new HashSet<>(),
new LinkedHashSet<>(),
ImmutableSortedSet.of(),
new TreeSet<>(),
ImmutableMultiset.of(),
ImmutableSortedMultiset.of())),
enumAssert.containsExactlyElementsOf(wellTypedIterable),
enumAssert.containsExactlyInAnyOrderElementsOf(wellTypedIterable),
enumAssert.hasSameElementsAs(wellTypedIterable),
enumAssert.hasSameSizeAs(arbitrarilyTypedIterable),
enumAssert.isSubsetOf(wellTypedIterable),
enumAssert.containsExactly(),
enumAssert.containsExactlyInAnyOrder(),
enumAssert.containsOnly(),

View File

@@ -2,6 +2,7 @@ package tech.picnic.errorprone.refasterrules;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.AlsoNegation;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import java.math.BigDecimal;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
@@ -66,4 +67,61 @@ final class BigDecimalRules {
return BigDecimal.valueOf(value);
}
}
/** Prefer using {@link BigDecimal#signum()} over more contrived alternatives. */
static final class BigDecimalSignumIsZero {
@BeforeTemplate
boolean before(BigDecimal value) {
return Refaster.anyOf(
value.compareTo(BigDecimal.ZERO) == 0, BigDecimal.ZERO.compareTo(value) == 0);
}
@AfterTemplate
@AlsoNegation
boolean after(BigDecimal value) {
return value.signum() == 0;
}
}
/**
* Prefer a {@link BigDecimal#signum()} comparison to 1 over more contrived or less clear
* alternatives.
*/
static final class BigDecimalSignumIsPositive {
@BeforeTemplate
boolean before(BigDecimal value) {
return Refaster.anyOf(
value.compareTo(BigDecimal.ZERO) > 0,
BigDecimal.ZERO.compareTo(value) < 0,
value.signum() > 0,
value.signum() >= 1);
}
@AfterTemplate
@AlsoNegation
boolean after(BigDecimal value) {
return value.signum() == 1;
}
}
/**
* Prefer a {@link BigDecimal#signum()} comparison to -1 over more contrived or less clear
* alternatives.
*/
static final class BigDecimalSignumIsNegative {
@BeforeTemplate
boolean before(BigDecimal value) {
return Refaster.anyOf(
value.compareTo(BigDecimal.ZERO) < 0,
BigDecimal.ZERO.compareTo(value) > 0,
value.signum() < 0,
value.signum() <= -1);
}
@AfterTemplate
@AlsoNegation
boolean after(BigDecimal value) {
return value.signum() == -1;
}
}
}

View File

@@ -7,6 +7,8 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.sun.tools.javac.util.Constants;
import com.sun.tools.javac.util.Convert;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to {@link com.google.errorprone.bugpatterns.BugChecker} classes. */
@@ -52,4 +54,17 @@ final class BugCheckerRules {
return helper.addInputLines(path, source).expectUnchanged();
}
}
/** Prefer using the {@link Constants} API over more verbose alternatives. */
static final class ConstantsFormat {
@BeforeTemplate
String before(String value) {
return String.format("\"%s\"", Convert.quote(value));
}
@AfterTemplate
String after(String value) {
return Constants.format(value);
}
}
}

View File

@@ -7,7 +7,6 @@ import static java.util.Comparator.comparingInt;
import static java.util.Comparator.comparingLong;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.reverseOrder;
import static java.util.function.Function.identity;
import com.google.common.collect.Comparators;
import com.google.common.collect.ImmutableList;
@@ -16,6 +15,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.Matches;
import com.google.errorprone.refaster.annotation.Repeated;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Arrays;
@@ -28,6 +28,7 @@ import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.stream.Stream;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
import tech.picnic.errorprone.refaster.matchers.IsIdentityOperation;
/** Refaster rules related to expressions dealing with {@link Comparator}s. */
@OnlineDocumentation
@@ -36,12 +37,12 @@ final class ComparatorRules {
/** Prefer {@link Comparator#naturalOrder()} over more complicated constructs. */
static final class NaturalOrder<T extends Comparable<? super T>> {
// XXX: Drop the `Refaster.anyOf` if/when we decide to rewrite one to the other.
@BeforeTemplate
Comparator<T> before() {
Comparator<T> before(
@Matches(IsIdentityOperation.class) Function<? super T, ? extends T> keyExtractor) {
return Refaster.anyOf(
T::compareTo,
comparing(Refaster.anyOf(identity(), v -> v)),
comparing(keyExtractor),
Collections.<T>reverseOrder(reverseOrder()),
Comparator.<T>reverseOrder().reversed());
}
@@ -72,10 +73,11 @@ final class ComparatorRules {
}
static final class CustomComparator<T> {
// XXX: Drop the `Refaster.anyOf` if/when we decide to rewrite one to the other.
@BeforeTemplate
Comparator<T> before(Comparator<T> cmp) {
return comparing(Refaster.anyOf(identity(), v -> v), cmp);
Comparator<T> before(
Comparator<T> cmp,
@Matches(IsIdentityOperation.class) Function<? super T, ? extends T> keyExtractor) {
return comparing(keyExtractor, cmp);
}
@AfterTemplate
@@ -183,14 +185,15 @@ final class ComparatorRules {
}
/**
* Where applicable, prefer {@link Comparator#naturalOrder()} over {@link Function#identity()}, as
* it more clearly states intent.
* Where applicable, prefer {@link Comparator#naturalOrder()} over identity function-based
* comparisons, as the former more clearly states intent.
*/
static final class ThenComparingNaturalOrder<T extends Comparable<? super T>> {
// XXX: Drop the `Refaster.anyOf` if/when we decide to rewrite one to the other.
@BeforeTemplate
Comparator<T> before(Comparator<T> cmp) {
return cmp.thenComparing(Refaster.anyOf(identity(), v -> v));
Comparator<T> before(
Comparator<T> cmp,
@Matches(IsIdentityOperation.class) Function<? super T, ? extends T> keyExtractor) {
return cmp.thenComparing(keyExtractor);
}
@AfterTemplate

View File

@@ -3,7 +3,6 @@ package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableListMultimap.flatteningToImmutableListMultimap;
import static com.google.common.collect.ImmutableListMultimap.toImmutableListMultimap;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import static java.util.function.Function.identity;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMultimap;
@@ -16,6 +15,7 @@ import com.google.common.collect.Streams;
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.Matches;
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
import com.google.errorprone.refaster.annotation.Placeholder;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
@@ -25,6 +25,7 @@ import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
import tech.picnic.errorprone.refaster.matchers.IsIdentityOperation;
/** Refaster rules related to expressions dealing with {@link ImmutableListMultimap}s. */
@OnlineDocumentation
@@ -173,27 +174,29 @@ final class ImmutableListMultimapRules {
* Prefer {@link Multimaps#index(Iterable, com.google.common.base.Function)} over the stream-based
* alternative.
*/
// XXX: Drop the `Refaster.anyOf` if/when we decide to rewrite one to the other.
static final class IndexIterableToImmutableListMultimap<K, V> {
@BeforeTemplate
ImmutableListMultimap<K, V> before(
Iterator<V> iterable, Function<? super V, ? extends K> keyFunction) {
return Streams.stream(iterable)
.collect(toImmutableListMultimap(keyFunction, Refaster.anyOf(identity(), v -> v)));
Iterator<V> iterable,
Function<? super V, ? extends K> keyFunction,
@Matches(IsIdentityOperation.class) Function<? super V, ? extends V> valueFunction) {
return Streams.stream(iterable).collect(toImmutableListMultimap(keyFunction, valueFunction));
}
@BeforeTemplate
ImmutableListMultimap<K, V> before(
Iterable<V> iterable, Function<? super V, ? extends K> keyFunction) {
return Streams.stream(iterable)
.collect(toImmutableListMultimap(keyFunction, Refaster.anyOf(identity(), v -> v)));
Iterable<V> iterable,
Function<? super V, ? extends K> keyFunction,
@Matches(IsIdentityOperation.class) Function<? super V, ? extends V> valueFunction) {
return Streams.stream(iterable).collect(toImmutableListMultimap(keyFunction, valueFunction));
}
@BeforeTemplate
ImmutableListMultimap<K, V> before(
Collection<V> iterable, Function<? super V, ? extends K> keyFunction) {
return iterable.stream()
.collect(toImmutableListMultimap(keyFunction, Refaster.anyOf(identity(), v -> v)));
Collection<V> iterable,
Function<? super V, ? extends K> keyFunction,
@Matches(IsIdentityOperation.class) Function<? super V, ? extends V> valueFunction) {
return iterable.stream().collect(toImmutableListMultimap(keyFunction, valueFunction));
}
@AfterTemplate

View File

@@ -4,7 +4,6 @@ import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static java.util.function.Function.identity;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
@@ -13,6 +12,7 @@ import com.google.common.collect.Streams;
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.Matches;
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
import com.google.errorprone.refaster.annotation.Placeholder;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
@@ -23,6 +23,7 @@ import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
import tech.picnic.errorprone.refaster.matchers.IsIdentityOperation;
/** Refaster rules related to expressions dealing with {@link ImmutableMap}s. */
@OnlineDocumentation
@@ -63,27 +64,29 @@ final class ImmutableMapRules {
* Prefer {@link Maps#toMap(Iterable, com.google.common.base.Function)} over more contrived
* alternatives.
*/
// XXX: Drop the `Refaster.anyOf` if/when we decide to rewrite one to the other.
static final class IterableToImmutableMap<K, V> {
@BeforeTemplate
ImmutableMap<K, V> before(
Iterator<K> iterable, Function<? super K, ? extends V> valueFunction) {
return Streams.stream(iterable)
.collect(toImmutableMap(Refaster.anyOf(identity(), k -> k), valueFunction));
Iterator<K> iterable,
Function<? super K, ? extends V> valueFunction,
@Matches(IsIdentityOperation.class) Function<? super K, ? extends K> keyFunction) {
return Streams.stream(iterable).collect(toImmutableMap(keyFunction, valueFunction));
}
@BeforeTemplate
ImmutableMap<K, V> before(
Iterable<K> iterable, Function<? super K, ? extends V> valueFunction) {
return Streams.stream(iterable)
.collect(toImmutableMap(Refaster.anyOf(identity(), k -> k), valueFunction));
Iterable<K> iterable,
Function<? super K, ? extends V> valueFunction,
@Matches(IsIdentityOperation.class) Function<? super K, ? extends K> keyFunction) {
return Streams.stream(iterable).collect(toImmutableMap(keyFunction, valueFunction));
}
@BeforeTemplate
ImmutableMap<K, V> before(
Collection<K> iterable, Function<? super K, ? extends V> valueFunction) {
return iterable.stream()
.collect(toImmutableMap(Refaster.anyOf(identity(), k -> k), valueFunction));
Collection<K> iterable,
Function<? super K, ? extends V> valueFunction,
@Matches(IsIdentityOperation.class) Function<? super K, ? extends K> keyFunction) {
return iterable.stream().collect(toImmutableMap(keyFunction, valueFunction));
}
@BeforeTemplate
@@ -157,25 +160,29 @@ final class ImmutableMapRules {
* Prefer {@link Maps#uniqueIndex(Iterable, com.google.common.base.Function)} over the
* stream-based alternative.
*/
// XXX: Drop the `Refaster.anyOf` if/when we decide to rewrite one to the other.
static final class IndexIterableToImmutableMap<K, V> {
@BeforeTemplate
ImmutableMap<K, V> before(Iterator<V> iterable, Function<? super V, ? extends K> keyFunction) {
return Streams.stream(iterable)
.collect(toImmutableMap(keyFunction, Refaster.anyOf(identity(), v -> v)));
}
@BeforeTemplate
ImmutableMap<K, V> before(Iterable<V> iterable, Function<? super V, ? extends K> keyFunction) {
return Streams.stream(iterable)
.collect(toImmutableMap(keyFunction, Refaster.anyOf(identity(), v -> v)));
ImmutableMap<K, V> before(
Iterator<V> iterable,
Function<? super V, ? extends K> keyFunction,
@Matches(IsIdentityOperation.class) Function<? super V, ? extends V> valueFunction) {
return Streams.stream(iterable).collect(toImmutableMap(keyFunction, valueFunction));
}
@BeforeTemplate
ImmutableMap<K, V> before(
Collection<V> iterable, Function<? super V, ? extends K> keyFunction) {
return iterable.stream()
.collect(toImmutableMap(keyFunction, Refaster.anyOf(identity(), v -> v)));
Iterable<V> iterable,
Function<? super V, ? extends K> keyFunction,
@Matches(IsIdentityOperation.class) Function<? super V, ? extends V> valueFunction) {
return Streams.stream(iterable).collect(toImmutableMap(keyFunction, valueFunction));
}
@BeforeTemplate
ImmutableMap<K, V> before(
Collection<V> iterable,
Function<? super V, ? extends K> keyFunction,
@Matches(IsIdentityOperation.class) Function<? super V, ? extends V> valueFunction) {
return iterable.stream().collect(toImmutableMap(keyFunction, valueFunction));
}
@AfterTemplate

View File

@@ -6,6 +6,7 @@ import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -517,4 +518,82 @@ final class JUnitToAssertJRules {
assertThat(actual).withFailMessage(supplier).isInstanceOf(clazz);
}
}
static final class AssertThatByteIsEqualTo {
@BeforeTemplate
void before(byte actual, byte expected) {
assertEquals(actual, expected);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(byte actual, byte expected) {
assertThat(actual).isEqualTo(expected);
}
}
static final class AssertThatByteWithFailMessageStringIsEqualTo {
@BeforeTemplate
void before(byte actual, byte expected, String message) {
assertEquals(actual, expected, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(byte actual, byte expected, String message) {
assertThat(actual).withFailMessage(message).isEqualTo(expected);
}
}
static final class AssertThatByteWithFailMessageSupplierIsEqualTo {
@BeforeTemplate
void before(byte actual, byte expected, Supplier<String> message) {
assertEquals(actual, expected, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(byte actual, byte expected, Supplier<String> message) {
assertThat(actual).withFailMessage(message).isEqualTo(expected);
}
}
static final class AssertThatCharIsEqualTo {
@BeforeTemplate
void before(char actual, char expected) {
assertEquals(actual, expected);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(char actual, char expected) {
assertThat(actual).isEqualTo(expected);
}
}
static final class AssertThatCharWithFailMessageStringIsEqualTo {
@BeforeTemplate
void before(char actual, char expected, String message) {
assertEquals(actual, expected, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(char actual, char expected, String message) {
assertThat(actual).withFailMessage(message).isEqualTo(expected);
}
}
static final class AssertThatCharWithFailMessageSupplierIsEqualTo {
@BeforeTemplate
void before(char actual, char expected, Supplier<String> message) {
assertEquals(actual, expected, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(char actual, char expected, Supplier<String> message) {
assertThat(actual).withFailMessage(message).isEqualTo(expected);
}
}
}

View File

@@ -8,7 +8,9 @@ import com.google.common.primitives.Floats;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.common.primitives.Shorts;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.AlsoNegation;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
@@ -384,4 +386,60 @@ final class PrimitiveRules {
return Double.isFinite(d);
}
}
/** Prefer an {@link Integer#signum(int)} comparison to 1 over less clear alternatives. */
static final class IntegerSignumIsPositive {
@BeforeTemplate
boolean before(int i) {
return Refaster.anyOf(Integer.signum(i) > 0, Integer.signum(i) >= 1);
}
@AfterTemplate
@AlsoNegation
boolean after(int i) {
return Integer.signum(i) == 1;
}
}
/** Prefer an {@link Integer#signum(int)} comparison to -1 over less clear alternatives. */
static final class IntegerSignumIsNegative {
@BeforeTemplate
boolean before(int i) {
return Refaster.anyOf(Integer.signum(i) < 0, Integer.signum(i) <= -1);
}
@AfterTemplate
@AlsoNegation
boolean after(int i) {
return Integer.signum(i) == -1;
}
}
/** Prefer an {@link Long#signum(long)} comparison to 1 over less clear alternatives. */
static final class LongSignumIsPositive {
@BeforeTemplate
boolean before(long l) {
return Refaster.anyOf(Long.signum(l) > 0, Long.signum(l) >= 1);
}
@AfterTemplate
@AlsoNegation
boolean after(long l) {
return Long.signum(l) == 1;
}
}
/** Prefer an {@link Long#signum(long)} comparison to -1 over less clear alternatives. */
static final class LongSignumIsNegative {
@BeforeTemplate
boolean before(long l) {
return Refaster.anyOf(Long.signum(l) < 0, Long.signum(l) <= -1);
}
@AfterTemplate
@AlsoNegation
boolean after(long l) {
return Long.signum(l) == -1;
}
}
}

View File

@@ -12,12 +12,12 @@ import static reactor.function.TupleUtils.function;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.Matches;
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
import com.google.errorprone.refaster.annotation.NotMatches;
import com.google.errorprone.refaster.annotation.Placeholder;
@@ -26,8 +26,8 @@ import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.function.BiConsumer;
@@ -48,6 +48,8 @@ import reactor.util.function.Tuple2;
import tech.picnic.errorprone.refaster.annotation.Description;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
import tech.picnic.errorprone.refaster.annotation.Severity;
import tech.picnic.errorprone.refaster.matchers.IsEmpty;
import tech.picnic.errorprone.refaster.matchers.IsIdentityOperation;
import tech.picnic.errorprone.refaster.matchers.ThrowsCheckedException;
/** Refaster rules related to Reactor expressions and statements. */
@@ -508,7 +510,8 @@ final class ReactorRules {
}
// XXX: Replace this rule with an extension of the `IdentityConversion` rule, supporting
// `Stream#map`, `Mono#map` and `Flux#map`.
// `Stream#map`, `Mono#map` and `Flux#map`. Alternatively, extend the `IsIdentityOperation`
// matcher and use it to constrain the matched `map` argument.
@BeforeTemplate
Mono<ImmutableList<T>> before3(Mono<ImmutableList<T>> mono) {
return mono.map(ImmutableList::copyOf);
@@ -549,51 +552,59 @@ final class ReactorRules {
}
/** Prefer {@link Flux#concatMap(Function)} over more contrived alternatives. */
static final class FluxConcatMap<T, S> {
static final class FluxConcatMap<T, S, P extends Publisher<? extends S>> {
@BeforeTemplate
@SuppressWarnings("NestedPublishers")
Flux<S> before(Flux<T> flux, Function<? super T, ? extends Publisher<? extends S>> function) {
Flux<S> before(
Flux<T> flux,
Function<? super T, ? extends P> function,
@Matches(IsIdentityOperation.class)
Function<? super P, ? extends Publisher<? extends S>> identityOperation) {
return Refaster.anyOf(
flux.flatMap(function, 1),
flux.flatMapSequential(function, 1),
flux.map(function).concatMap(identity()));
flux.map(function).concatMap(identityOperation));
}
@AfterTemplate
Flux<S> after(Flux<T> flux, Function<? super T, ? extends Publisher<? extends S>> function) {
Flux<S> after(Flux<T> flux, Function<? super T, ? extends P> function) {
return flux.concatMap(function);
}
}
/** Prefer {@link Flux#concatMap(Function, int)} over more contrived alternatives. */
static final class FluxConcatMapWithPrefetch<T, S> {
static final class FluxConcatMapWithPrefetch<T, S, P extends Publisher<? extends S>> {
@BeforeTemplate
@SuppressWarnings("NestedPublishers")
Flux<S> before(
Flux<T> flux,
Function<? super T, ? extends Publisher<? extends S>> function,
int prefetch) {
Function<? super T, ? extends P> function,
int prefetch,
@Matches(IsIdentityOperation.class)
Function<? super P, ? extends Publisher<? extends S>> identityOperation) {
return Refaster.anyOf(
flux.flatMap(function, 1, prefetch),
flux.flatMapSequential(function, 1, prefetch),
flux.map(function).concatMap(identity(), prefetch));
flux.map(function).concatMap(identityOperation, prefetch));
}
@AfterTemplate
Flux<S> after(
Flux<T> flux,
Function<? super T, ? extends Publisher<? extends S>> function,
int prefetch) {
Flux<S> after(Flux<T> flux, Function<? super T, ? extends P> function, int prefetch) {
return flux.concatMap(function, prefetch);
}
}
/** Avoid contrived alternatives to {@link Mono#flatMapIterable(Function)}. */
static final class MonoFlatMapIterable<T, S> {
static final class MonoFlatMapIterable<T, S, I extends Iterable<? extends S>> {
@BeforeTemplate
Flux<S> before(Mono<T> mono, Function<? super T, ? extends Iterable<? extends S>> function) {
Flux<S> before(
Mono<T> mono,
Function<? super T, I> function,
@Matches(IsIdentityOperation.class)
Function<? super I, ? extends Iterable<? extends S>> identityOperation) {
return Refaster.anyOf(
mono.map(function).flatMapIterable(identity()), mono.flux().concatMapIterable(function));
mono.map(function).flatMapIterable(identityOperation),
mono.flux().concatMapIterable(function));
}
@AfterTemplate
@@ -623,11 +634,15 @@ final class ReactorRules {
* Prefer {@link Flux#concatMapIterable(Function)} over alternatives with less clear syntax or
* semantics.
*/
static final class FluxConcatMapIterable<T, S> {
static final class FluxConcatMapIterable<T, S, I extends Iterable<? extends S>> {
@BeforeTemplate
Flux<S> before(Flux<T> flux, Function<? super T, ? extends Iterable<? extends S>> function) {
Flux<S> before(
Flux<T> flux,
Function<? super T, I> function,
@Matches(IsIdentityOperation.class)
Function<? super I, ? extends Iterable<? extends S>> identityOperation) {
return Refaster.anyOf(
flux.flatMapIterable(function), flux.map(function).concatMapIterable(identity()));
flux.flatMapIterable(function), flux.map(function).concatMapIterable(identityOperation));
}
@AfterTemplate
@@ -640,13 +655,17 @@ final class ReactorRules {
* Prefer {@link Flux#concatMapIterable(Function, int)} over alternatives with less clear syntax
* or semantics.
*/
static final class FluxConcatMapIterableWithPrefetch<T, S> {
static final class FluxConcatMapIterableWithPrefetch<T, S, I extends Iterable<? extends S>> {
@BeforeTemplate
Flux<S> before(
Flux<T> flux, Function<? super T, ? extends Iterable<? extends S>> function, int prefetch) {
Flux<T> flux,
Function<? super T, I> function,
int prefetch,
@Matches(IsIdentityOperation.class)
Function<? super I, ? extends Iterable<? extends S>> identityOperation) {
return Refaster.anyOf(
flux.flatMapIterable(function, prefetch),
flux.map(function).concatMapIterable(identity(), prefetch));
flux.map(function).concatMapIterable(identityOperation, prefetch));
}
@AfterTemplate
@@ -1080,31 +1099,37 @@ final class ReactorRules {
}
/** Prefer {@link Mono#flatMap(Function)} over more contrived alternatives. */
static final class MonoFlatMap<S, T> {
static final class MonoFlatMap<S, T, P extends Mono<? extends T>> {
@BeforeTemplate
@SuppressWarnings("NestedPublishers")
Mono<T> before(Mono<S> mono, Function<? super S, ? extends Mono<? extends T>> function) {
return mono.map(function).flatMap(identity());
Mono<T> before(
Mono<S> mono,
Function<? super S, ? extends P> function,
@Matches(IsIdentityOperation.class)
Function<? super P, ? extends Mono<? extends T>> identityOperation) {
return mono.map(function).flatMap(identityOperation);
}
@AfterTemplate
Mono<T> after(Mono<S> mono, Function<? super S, ? extends Mono<? extends T>> function) {
Mono<T> after(Mono<S> mono, Function<? super S, ? extends P> function) {
return mono.flatMap(function);
}
}
/** Prefer {@link Mono#flatMapMany(Function)} over more contrived alternatives. */
static final class MonoFlatMapMany<S, T> {
static final class MonoFlatMapMany<S, T, P extends Publisher<? extends T>> {
@BeforeTemplate
@SuppressWarnings("NestedPublishers")
Flux<T> before(
Mono<S> mono,
Function<? super S, ? extends Publisher<? extends T>> function,
Function<? super S, P> function,
@Matches(IsIdentityOperation.class)
Function<? super P, ? extends Publisher<? extends T>> identityOperation,
boolean delayUntilEnd,
int maxConcurrency,
int prefetch) {
return Refaster.anyOf(
mono.map(function).flatMapMany(identity()),
mono.map(function).flatMapMany(identityOperation),
mono.flux().concatMap(function),
mono.flux().concatMap(function, prefetch),
mono.flux().concatMapDelayError(function),
@@ -1124,7 +1149,7 @@ final class ReactorRules {
}
@AfterTemplate
Flux<T> after(Mono<S> mono, Function<? super S, ? extends Publisher<? extends T>> function) {
Flux<T> after(Mono<S> mono, Function<? super S, ? extends P> function) {
return mono.flatMapMany(function);
}
}
@@ -1555,12 +1580,12 @@ final class ReactorRules {
}
/** Prefer {@link reactor.util.context.Context#empty()}} over more verbose alternatives. */
// XXX: Consider introducing an `IsEmpty` matcher that identifies a wide range of guaranteed-empty
// `Collection` and `Map` expressions.
// XXX: Introduce Refaster rules or a `BugChecker` that maps `(Immutable)Map.of(k, v)` to
// `Context.of(k, v)` and likewise for multi-pair overloads.
static final class ContextEmpty {
@BeforeTemplate
Context before() {
return Context.of(Refaster.anyOf(new HashMap<>(), ImmutableMap.of()));
Context before(@Matches(IsEmpty.class) Map<?, ?> map) {
return Context.of(map);
}
@AfterTemplate
@@ -1609,13 +1634,13 @@ final class ReactorRules {
}
/** Don't unnecessarily have {@link StepVerifier.Step} expect no elements. */
// XXX: Given an `IsEmpty` matcher that identifies a wide range of guaranteed-empty `Iterable`
// expressions, consider also simplifying `step.expectNextSequence(someEmptyIterable)`.
static final class StepVerifierStepIdentity<T> {
@BeforeTemplate
@SuppressWarnings("unchecked")
StepVerifier.Step<T> before(StepVerifier.Step<T> step) {
return Refaster.anyOf(step.expectNext(), step.expectNextCount(0));
StepVerifier.Step<T> before(
StepVerifier.Step<T> step, @Matches(IsEmpty.class) Iterable<? extends T> iterable) {
return Refaster.anyOf(
step.expectNext(), step.expectNextCount(0), step.expectNextSequence(iterable));
}
@AfterTemplate

View File

@@ -3,7 +3,6 @@ package tech.picnic.errorprone.refasterrules;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.reverseOrder;
import static java.util.function.Function.identity;
import static java.util.function.Predicate.not;
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.filtering;
@@ -51,6 +50,7 @@ import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
import tech.picnic.errorprone.refaster.matchers.IsIdentityOperation;
import tech.picnic.errorprone.refaster.matchers.IsLambdaExpressionOrMethodReference;
import tech.picnic.errorprone.refaster.matchers.IsRefasterAsVarargs;
@@ -357,6 +357,8 @@ final class StreamRules {
stream.filter(predicate).findAny().isEmpty());
}
// XXX: Consider extending `@Matches(IsIdentityOperation.class)` such that it can replace this
// template's `Refaster.anyOf` usage.
@BeforeTemplate
boolean before2(
Stream<T> stream,
@@ -395,6 +397,8 @@ final class StreamRules {
!stream.noneMatch(predicate), stream.filter(predicate).findAny().isPresent());
}
// XXX: Consider extending `@Matches(IsIdentityOperation.class)` such that it can replace this
// template's `Refaster.anyOf` usage.
@BeforeTemplate
boolean before2(
Stream<T> stream,
@@ -415,6 +419,8 @@ final class StreamRules {
return stream.noneMatch(Refaster.anyOf(not(predicate), predicate.negate()));
}
// XXX: Consider extending `@Matches(IsIdentityOperation.class)` such that it can replace this
// template's `Refaster.anyOf` usage.
@BeforeTemplate
boolean before2(
Stream<T> stream,
@@ -632,8 +638,11 @@ final class StreamRules {
static final class StreamsConcat<T> {
@BeforeTemplate
Stream<T> before(@Repeated Stream<T> stream) {
return Stream.of(Refaster.asVarargs(stream)).flatMap(Refaster.anyOf(identity(), s -> s));
Stream<T> before(
@Repeated Stream<T> stream,
@Matches(IsIdentityOperation.class)
Function<? super Stream<T>, ? extends Stream<? extends T>> mapper) {
return Stream.of(Refaster.asVarargs(stream)).flatMap(mapper);
}
@AfterTemplate

View File

@@ -0,0 +1,155 @@
package tech.picnic.errorprone.bugpatterns;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class JUnitNullaryParameterizedTestDeclarationTest {
@Test
void identification() {
CompilationTestHelper.newInstance(JUnitNullaryParameterizedTestDeclaration.class, getClass())
.addSourceLines(
"A.java",
"import org.junit.jupiter.api.Test;",
"import org.junit.jupiter.params.ParameterizedTest;",
"import org.junit.jupiter.params.provider.ValueSource;",
"",
"class A {",
" void nonTest() {}",
"",
" @Test",
" void nonParameterizedTest() {}",
"",
" @ParameterizedTest",
" @ValueSource(ints = {0, 1})",
" void goodParameterizedTest(int someInt) {}",
"",
" @ParameterizedTest",
" @ValueSource(ints = {0, 1})",
" // BUG: Diagnostic contains:",
" void nullaryParameterizedTest() {}",
"}")
.doTest();
}
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(
JUnitNullaryParameterizedTestDeclaration.class, getClass())
.addInputLines(
"A.java",
"import org.junit.jupiter.params.ParameterizedTest;",
"import org.junit.jupiter.params.provider.ArgumentsProvider;",
"import org.junit.jupiter.params.provider.ArgumentsSource;",
"import org.junit.jupiter.params.provider.ArgumentsSources;",
"import org.junit.jupiter.params.provider.MethodSource;",
"import org.junit.jupiter.params.provider.ValueSource;",
"",
"class A {",
" @ParameterizedTest",
" void withoutArgumentSource() {}",
"",
" @ParameterizedTest",
" @ArgumentsSource(ArgumentsProvider.class)",
" void withCustomArgumentSource() {}",
"",
" @ParameterizedTest",
" @ArgumentsSources({",
" @ArgumentsSource(ArgumentsProvider.class),",
" @ArgumentsSource(ArgumentsProvider.class)",
" })",
" void withCustomerArgumentSources() {}",
"",
" /** Foo. */",
" @ParameterizedTest",
" @ValueSource(ints = {0, 1})",
" void withValueSourceAndJavadoc() {}",
"",
" @ParameterizedTest",
" @MethodSource(\"nonexistentMethod\")",
" @SuppressWarnings(\"foo\")",
" void withMethodSourceAndUnrelatedAnnotation() {}",
"",
" @org.junit.jupiter.params.ParameterizedTest",
" @ArgumentsSource(ArgumentsProvider.class)",
" @ValueSource(ints = {0, 1})",
" @MethodSource(\"nonexistentMethod\")",
" void withMultipleArgumentSourcesAndFullyQualifiedImport() {}",
"",
" class NestedWithTestAnnotationFirst {",
" @ParameterizedTest",
" @ValueSource(ints = {0, 1})",
" void withValueSource() {}",
" }",
"",
" class NestedWithTestAnnotationSecond {",
" @ValueSource(ints = {0, 1})",
" @ParameterizedTest",
" void withValueSource() {}",
" }",
"}")
.addOutputLines(
"A.java",
"import org.junit.jupiter.api.Test;",
"import org.junit.jupiter.params.ParameterizedTest;",
"import org.junit.jupiter.params.provider.ArgumentsProvider;",
"import org.junit.jupiter.params.provider.ArgumentsSource;",
"import org.junit.jupiter.params.provider.ArgumentsSources;",
"import org.junit.jupiter.params.provider.MethodSource;",
"import org.junit.jupiter.params.provider.ValueSource;",
"",
"class A {",
" @Test",
" void withoutArgumentSource() {}",
"",
" @Test",
" void withCustomArgumentSource() {}",
"",
" @Test",
" void withCustomerArgumentSources() {}",
"",
" /** Foo. */",
" @Test",
" void withValueSourceAndJavadoc() {}",
"",
" @Test",
" @SuppressWarnings(\"foo\")",
" void withMethodSourceAndUnrelatedAnnotation() {}",
"",
" @Test",
" void withMultipleArgumentSourcesAndFullyQualifiedImport() {}",
"",
" class NestedWithTestAnnotationFirst {",
" @Test",
" void withValueSource() {}",
" }",
"",
" class NestedWithTestAnnotationSecond {",
" @Test",
" void withValueSource() {}",
" }",
"}")
.addInputLines(
"B.java",
"import org.junit.jupiter.params.ParameterizedTest;",
"",
"class B {",
" @ParameterizedTest",
" void scopeInWhichIdentifierTestIsAlreadyDeclared() {}",
"",
" class Test {}",
"}")
.addOutputLines(
"B.java",
"import org.junit.jupiter.params.ParameterizedTest;",
"",
"class B {",
" @org.junit.jupiter.api.Test",
" void scopeInWhichIdentifierTestIsAlreadyDeclared() {}",
"",
" class Test {}",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -0,0 +1,196 @@
package tech.picnic.errorprone.bugpatterns;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class NonStaticImportTest {
@Test
void candidateTypesDoNotClash() {
assertThat(NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_TYPES)
.doesNotContainAnyElementsOf(StaticImport.STATIC_IMPORT_CANDIDATE_TYPES);
}
@Test
void candidateMembersAreNotRedundant() {
assertThat(NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_MEMBERS.keySet())
.doesNotContainAnyElementsOf(NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_TYPES);
assertThat(NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_MEMBERS.values())
.doesNotContainAnyElementsOf(NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_IDENTIFIERS);
}
@Test
void candidateMembersDoNotClash() {
assertThat(NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_MEMBERS.entries())
.doesNotContainAnyElementsOf(StaticImport.STATIC_IMPORT_CANDIDATE_MEMBERS.entries());
}
@Test
void candidateIdentifiersDoNotClash() {
assertThat(NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_IDENTIFIERS)
.doesNotContainAnyElementsOf(StaticImport.STATIC_IMPORT_CANDIDATE_MEMBERS.values());
}
@Test
void identification() {
CompilationTestHelper.newInstance(NonStaticImport.class, getClass())
.addSourceLines(
"pkg/A.java",
"package pkg;",
"",
"// BUG: Diagnostic contains:",
"import static com.google.common.base.Strings.nullToEmpty;",
"// BUG: Diagnostic contains:",
"import static com.google.common.collect.ImmutableList.copyOf;",
"// BUG: Diagnostic contains:",
"import static java.lang.Integer.MAX_VALUE;",
"// BUG: Diagnostic contains:",
"import static java.lang.Integer.MIN_VALUE;",
"// BUG: Diagnostic contains:",
"import static java.time.Clock.systemUTC;",
"// BUG: Diagnostic contains:",
"import static java.time.Instant.MIN;",
"// BUG: Diagnostic contains:",
"import static java.time.ZoneOffset.SHORT_IDS;",
"import static java.time.ZoneOffset.UTC;",
"// BUG: Diagnostic contains:",
"import static java.util.Collections.min;",
"import static java.util.Locale.ENGLISH;",
"// BUG: Diagnostic contains:",
"import static java.util.Locale.ROOT;",
"// BUG: Diagnostic contains:",
"import static java.util.Optional.empty;",
"import static pkg.A.WithMethodThatIsSelectivelyFlagged.list;",
"",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import java.time.Instant;",
"import java.time.ZoneOffset;",
"import java.util.Locale;",
"import java.util.Map;",
"import pkg.A.Wrapper.ZERO;",
"",
"class A {",
" private Integer MIN_VALUE = 12;",
"",
" void m() {",
" nullToEmpty(null);",
" copyOf(ImmutableList.of());",
" int max = MAX_VALUE;",
" int min = MIN_VALUE;",
" systemUTC();",
" Instant minInstant = MIN;",
" Map<String, String> shortIds = SHORT_IDS;",
" ZoneOffset utc = UTC;",
" min(ImmutableSet.of());",
" Locale english = ENGLISH;",
" Locale root = ROOT;",
" empty();",
"",
" list();",
" new ZERO();",
" }",
"",
" static final class WithMethodThatIsSelectivelyFlagged {",
" static ImmutableList<String> list() {",
" return ImmutableList.of();",
" }",
" }",
"",
" static final class Wrapper {",
" static final class ZERO {}",
" }",
"}")
.doTest();
}
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(NonStaticImport.class, getClass())
.addInputLines(
"A.java",
"import static com.google.common.collect.ImmutableList.copyOf;",
"import static com.google.common.collect.ImmutableSet.of;",
"import static java.time.Clock.systemUTC;",
"import static java.time.Instant.MAX;",
"import static java.time.Instant.MIN;",
"import static java.util.Collections.min;",
"import static java.util.Locale.ROOT;",
"import static java.util.Optional.empty;",
"",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import java.time.Clock;",
"import java.time.Instant;",
"import java.util.Locale;",
"import java.util.Optional;",
"",
"class A {",
" void m() {",
" systemUTC();",
" Clock.systemUTC();",
"",
" Optional<Integer> o1 = empty();",
" Optional<Integer> o2 = Optional.empty();",
"",
" Object l1 = copyOf(ImmutableList.of());",
" Object l2 = ImmutableList.copyOf(ImmutableList.of());",
"",
" Locale lo1 = ROOT;",
" Locale lo2 = Locale.ROOT;",
"",
" Instant i1 = MIN;",
" Instant i2 = MAX;",
"",
" ImmutableSet.of(min(of()));",
" }",
"",
" private static final class WithCustomConstant {",
" private static final Instant MIN = Instant.EPOCH;",
" private static final Instant OTHER = MIN;",
" private static final Instant OTHER_MAX = MAX;",
" }",
"}")
.addOutputLines(
"A.java",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import java.time.Clock;",
"import java.time.Instant;",
"import java.util.Collections;",
"import java.util.Locale;",
"import java.util.Optional;",
"",
"class A {",
" void m() {",
" Clock.systemUTC();",
" Clock.systemUTC();",
"",
" Optional<Integer> o1 = Optional.empty();",
" Optional<Integer> o2 = Optional.empty();",
"",
" Object l1 = ImmutableList.copyOf(ImmutableList.of());",
" Object l2 = ImmutableList.copyOf(ImmutableList.of());",
"",
" Locale lo1 = Locale.ROOT;",
" Locale lo2 = Locale.ROOT;",
"",
" Instant i1 = Instant.MIN;",
" Instant i2 = Instant.MAX;",
"",
" ImmutableSet.of(Collections.min(ImmutableSet.of()));",
" }",
"",
" private static final class WithCustomConstant {",
" private static final Instant MIN = Instant.EPOCH;",
" private static final Instant OTHER = MIN;",
" private static final Instant OTHER_MAX = Instant.MAX;",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -9,21 +9,24 @@ import org.junit.jupiter.api.Test;
final class StaticImportTest {
@Test
void candidateMethodsAreNotRedundant() {
void candidateTypesDoNotClash() {
assertThat(StaticImport.STATIC_IMPORT_CANDIDATE_TYPES)
.doesNotContainAnyElementsOf(NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_TYPES);
}
@Test
void candidateMembersAreNotRedundant() {
assertThat(StaticImport.STATIC_IMPORT_CANDIDATE_MEMBERS.keySet())
.doesNotContainAnyElementsOf(StaticImport.STATIC_IMPORT_CANDIDATE_TYPES);
}
@Test
void exemptedMembersAreNotVacuous() {
assertThat(StaticImport.STATIC_IMPORT_EXEMPTED_MEMBERS.keySet())
.isSubsetOf(StaticImport.STATIC_IMPORT_CANDIDATE_TYPES);
}
void candidateMembersDoNotClash() {
assertThat(StaticImport.STATIC_IMPORT_CANDIDATE_MEMBERS.entries())
.doesNotContainAnyElementsOf(NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_MEMBERS.entries());
@Test
void exemptedMembersAreNotRedundant() {
assertThat(StaticImport.STATIC_IMPORT_EXEMPTED_MEMBERS.values())
.doesNotContainAnyElementsOf(StaticImport.STATIC_IMPORT_EXEMPTED_IDENTIFIERS);
assertThat(StaticImport.STATIC_IMPORT_CANDIDATE_MEMBERS.values())
.doesNotContainAnyElementsOf(NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_IDENTIFIERS);
}
@Test

View File

@@ -41,34 +41,19 @@ final class AssertJMapRulesTest implements RefasterRuleCollectionTestCase {
void testAbstractMapAssertIsEmpty() {
assertThat(ImmutableMap.of(1, 0)).containsExactlyEntriesOf(ImmutableMap.of());
assertThat(ImmutableMap.of(2, 0)).containsExactlyEntriesOf(ImmutableBiMap.of());
assertThat(ImmutableMap.of(3, 0)).containsExactlyEntriesOf(ImmutableSortedMap.of());
assertThat(ImmutableMap.of(4, 0)).containsExactlyEntriesOf(new HashMap<>());
assertThat(ImmutableMap.of(5, 0)).containsExactlyEntriesOf(new LinkedHashMap<>());
assertThat(ImmutableMap.of(6, 0)).containsExactlyEntriesOf(new TreeMap<>());
assertThat(ImmutableMap.of(7, 0)).hasSameSizeAs(ImmutableMap.of());
assertThat(ImmutableMap.of(8, 0)).hasSameSizeAs(ImmutableBiMap.of());
assertThat(ImmutableMap.of(9, 0)).hasSameSizeAs(ImmutableSortedMap.of());
assertThat(ImmutableMap.of(10, 0)).hasSameSizeAs(new HashMap<>());
assertThat(ImmutableMap.of(11, 0)).hasSameSizeAs(new LinkedHashMap<>());
assertThat(ImmutableMap.of(12, 0)).hasSameSizeAs(new TreeMap<>());
assertThat(ImmutableMap.of(13, 0)).isEqualTo(ImmutableMap.of());
assertThat(ImmutableMap.of(14, 0)).isEqualTo(ImmutableBiMap.of());
assertThat(ImmutableMap.of(15, 0)).isEqualTo(ImmutableSortedMap.of());
assertThat(ImmutableMap.of(16, 0)).isEqualTo(new HashMap<>());
assertThat(ImmutableMap.of(17, 0)).isEqualTo(new LinkedHashMap<>());
assertThat(ImmutableMap.of(18, 0)).isEqualTo(new TreeMap<>());
assertThat(ImmutableMap.of(19, 0)).containsOnlyKeys(ImmutableList.of());
assertThat(ImmutableMap.of(20, 0)).containsOnlyKeys(new ArrayList<>());
assertThat(ImmutableMap.of(21, 0)).containsOnlyKeys(ImmutableSet.of());
assertThat(ImmutableMap.of(22, 0)).containsOnlyKeys(new HashSet<>());
assertThat(ImmutableMap.of(23, 0)).containsOnlyKeys(ImmutableSortedSet.of());
assertThat(ImmutableMap.of(24, 0)).containsOnlyKeys(new TreeSet<>());
assertThat(ImmutableMap.of(25, 0)).containsOnlyKeys(ImmutableMultiset.of());
assertThat(ImmutableMap.of(26, 0)).containsOnlyKeys(ImmutableSortedMultiset.of());
assertThat(ImmutableMap.of(27, 0)).containsExactly();
assertThat(ImmutableMap.of(28, 0)).containsOnly();
assertThat(ImmutableMap.of(29, 0)).containsOnlyKeys();
assertThat(ImmutableMap.of(2, 0)).containsExactlyEntriesOf(ImmutableMap.of(1, 2));
assertThat(ImmutableMap.of(3, 0)).containsExactlyInAnyOrderEntriesOf(ImmutableMap.of());
assertThat(ImmutableMap.of(4, 0))
.containsExactlyInAnyOrderEntriesOf(ImmutableMap.of(1, 2, 3, 4));
assertThat(ImmutableMap.of(5, 0)).hasSameSizeAs(ImmutableMap.of());
assertThat(ImmutableMap.of(6, 0)).hasSameSizeAs(ImmutableMap.of(1, 2));
assertThat(ImmutableMap.of(7, 0)).isEqualTo(ImmutableMap.of());
assertThat(ImmutableMap.of(8, 0)).isEqualTo(ImmutableMap.of("foo", "bar"));
assertThat(ImmutableMap.of(9, 0)).containsOnlyKeys(ImmutableList.of());
assertThat(ImmutableMap.of(10, 0)).containsOnlyKeys(ImmutableList.of(1));
assertThat(ImmutableMap.of(11, 0)).containsExactly();
assertThat(ImmutableMap.of(12, 0)).containsOnly();
assertThat(ImmutableMap.of(13, 0)).containsOnlyKeys();
}
void testAssertThatMapIsEmpty() {
@@ -84,11 +69,7 @@ final class AssertJMapRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<MapAssert<Integer, Integer>> testAbstractMapAssertIsNotEmpty() {
return ImmutableSet.of(
assertThat(ImmutableMap.of(1, 0)).isNotEqualTo(ImmutableMap.of()),
assertThat(ImmutableMap.of(2, 0)).isNotEqualTo(ImmutableBiMap.of()),
assertThat(ImmutableMap.of(3, 0)).isNotEqualTo(ImmutableSortedMap.of()),
assertThat(ImmutableMap.of(4, 0)).isNotEqualTo(new HashMap<>()),
assertThat(ImmutableMap.of(5, 0)).isNotEqualTo(new LinkedHashMap<>()),
assertThat(ImmutableMap.of(6, 0)).isNotEqualTo(new TreeMap<>()));
assertThat(ImmutableMap.of(2, 0)).isNotEqualTo(ImmutableMap.of("foo", "bar")));
}
ImmutableSet<AbstractAssert<?, ?>> testAssertThatMapIsNotEmpty() {

View File

@@ -41,34 +41,19 @@ final class AssertJMapRulesTest implements RefasterRuleCollectionTestCase {
void testAbstractMapAssertIsEmpty() {
assertThat(ImmutableMap.of(1, 0)).isEmpty();
assertThat(ImmutableMap.of(2, 0)).isEmpty();
assertThat(ImmutableMap.of(2, 0)).containsExactlyEntriesOf(ImmutableMap.of(1, 2));
assertThat(ImmutableMap.of(3, 0)).isEmpty();
assertThat(ImmutableMap.of(4, 0)).isEmpty();
assertThat(ImmutableMap.of(4, 0))
.containsExactlyInAnyOrderEntriesOf(ImmutableMap.of(1, 2, 3, 4));
assertThat(ImmutableMap.of(5, 0)).isEmpty();
assertThat(ImmutableMap.of(6, 0)).isEmpty();
assertThat(ImmutableMap.of(6, 0)).hasSameSizeAs(ImmutableMap.of(1, 2));
assertThat(ImmutableMap.of(7, 0)).isEmpty();
assertThat(ImmutableMap.of(8, 0)).isEmpty();
assertThat(ImmutableMap.of(8, 0)).isEqualTo(ImmutableMap.of("foo", "bar"));
assertThat(ImmutableMap.of(9, 0)).isEmpty();
assertThat(ImmutableMap.of(10, 0)).isEmpty();
assertThat(ImmutableMap.of(10, 0)).containsOnlyKeys(ImmutableList.of(1));
assertThat(ImmutableMap.of(11, 0)).isEmpty();
assertThat(ImmutableMap.of(12, 0)).isEmpty();
assertThat(ImmutableMap.of(13, 0)).isEmpty();
assertThat(ImmutableMap.of(14, 0)).isEmpty();
assertThat(ImmutableMap.of(15, 0)).isEmpty();
assertThat(ImmutableMap.of(16, 0)).isEmpty();
assertThat(ImmutableMap.of(17, 0)).isEmpty();
assertThat(ImmutableMap.of(18, 0)).isEmpty();
assertThat(ImmutableMap.of(19, 0)).isEmpty();
assertThat(ImmutableMap.of(20, 0)).isEmpty();
assertThat(ImmutableMap.of(21, 0)).isEmpty();
assertThat(ImmutableMap.of(22, 0)).isEmpty();
assertThat(ImmutableMap.of(23, 0)).isEmpty();
assertThat(ImmutableMap.of(24, 0)).isEmpty();
assertThat(ImmutableMap.of(25, 0)).isEmpty();
assertThat(ImmutableMap.of(26, 0)).isEmpty();
assertThat(ImmutableMap.of(27, 0)).isEmpty();
assertThat(ImmutableMap.of(28, 0)).isEmpty();
assertThat(ImmutableMap.of(29, 0)).isEmpty();
}
void testAssertThatMapIsEmpty() {
@@ -84,11 +69,7 @@ final class AssertJMapRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<MapAssert<Integer, Integer>> testAbstractMapAssertIsNotEmpty() {
return ImmutableSet.of(
assertThat(ImmutableMap.of(1, 0)).isNotEmpty(),
assertThat(ImmutableMap.of(2, 0)).isNotEmpty(),
assertThat(ImmutableMap.of(3, 0)).isNotEmpty(),
assertThat(ImmutableMap.of(4, 0)).isNotEmpty(),
assertThat(ImmutableMap.of(5, 0)).isNotEmpty(),
assertThat(ImmutableMap.of(6, 0)).isNotEmpty());
assertThat(ImmutableMap.of(2, 0)).isNotEqualTo(ImmutableMap.of("foo", "bar")));
}
ImmutableSet<AbstractAssert<?, ?>> testAssertThatMapIsNotEmpty() {

View File

@@ -20,4 +20,36 @@ final class BigDecimalRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<BigDecimal> testBigDecimalValueOf() {
return ImmutableSet.of(new BigDecimal(2), new BigDecimal(2L), new BigDecimal(2.0));
}
ImmutableSet<Boolean> testBigDecimalSignumIsZero() {
return ImmutableSet.of(
BigDecimal.valueOf(1).compareTo(BigDecimal.ZERO) == 0,
BigDecimal.ZERO.compareTo(BigDecimal.valueOf(2)) == 0,
BigDecimal.valueOf(3).compareTo(BigDecimal.ZERO) != 0,
BigDecimal.ZERO.compareTo(BigDecimal.valueOf(4)) != 0);
}
ImmutableSet<Boolean> testBigDecimalSignumIsPositive() {
return ImmutableSet.of(
BigDecimal.valueOf(1).compareTo(BigDecimal.ZERO) > 0,
BigDecimal.ZERO.compareTo(BigDecimal.valueOf(2)) < 0,
BigDecimal.valueOf(3).signum() > 0,
BigDecimal.valueOf(4).signum() >= 1,
BigDecimal.valueOf(5).compareTo(BigDecimal.ZERO) <= 0,
BigDecimal.ZERO.compareTo(BigDecimal.valueOf(6)) >= 0,
BigDecimal.valueOf(7).signum() <= 0,
BigDecimal.valueOf(8).signum() < 1);
}
ImmutableSet<Boolean> testBigDecimalSignumIsNegative() {
return ImmutableSet.of(
BigDecimal.valueOf(1).compareTo(BigDecimal.ZERO) < 0,
BigDecimal.ZERO.compareTo(BigDecimal.valueOf(2)) > 0,
BigDecimal.valueOf(3).signum() < 0,
BigDecimal.valueOf(4).signum() <= -1,
BigDecimal.valueOf(5).compareTo(BigDecimal.ZERO) >= 0,
BigDecimal.ZERO.compareTo(BigDecimal.valueOf(6)) <= 0,
BigDecimal.valueOf(7).signum() >= 0,
BigDecimal.valueOf(8).signum() > -1);
}
}

View File

@@ -20,4 +20,36 @@ final class BigDecimalRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<BigDecimal> testBigDecimalValueOf() {
return ImmutableSet.of(BigDecimal.valueOf(2), BigDecimal.valueOf(2L), BigDecimal.valueOf(2.0));
}
ImmutableSet<Boolean> testBigDecimalSignumIsZero() {
return ImmutableSet.of(
BigDecimal.valueOf(1).signum() == 0,
BigDecimal.valueOf(2).signum() == 0,
BigDecimal.valueOf(3).signum() != 0,
BigDecimal.valueOf(4).signum() != 0);
}
ImmutableSet<Boolean> testBigDecimalSignumIsPositive() {
return ImmutableSet.of(
BigDecimal.valueOf(1).signum() == 1,
BigDecimal.valueOf(2).signum() == 1,
BigDecimal.valueOf(3).signum() == 1,
BigDecimal.valueOf(4).signum() == 1,
BigDecimal.valueOf(5).signum() != 1,
BigDecimal.valueOf(6).signum() != 1,
BigDecimal.valueOf(7).signum() != 1,
BigDecimal.valueOf(8).signum() != 1);
}
ImmutableSet<Boolean> testBigDecimalSignumIsNegative() {
return ImmutableSet.of(
BigDecimal.valueOf(1).signum() == -1,
BigDecimal.valueOf(2).signum() == -1,
BigDecimal.valueOf(3).signum() == -1,
BigDecimal.valueOf(4).signum() == -1,
BigDecimal.valueOf(5).signum() != -1,
BigDecimal.valueOf(6).signum() != -1,
BigDecimal.valueOf(7).signum() != -1,
BigDecimal.valueOf(8).signum() != -1);
}
}

View File

@@ -4,12 +4,13 @@ import com.google.common.collect.ImmutableSet;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
import com.google.errorprone.bugpatterns.BugChecker;
import com.sun.tools.javac.util.Convert;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class BugCheckerRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(FixChoosers.class);
return ImmutableSet.of(Convert.class, FixChoosers.class);
}
ImmutableSet<BugCheckerRefactoringTestHelper> testBugCheckerRefactoringTestHelperIdentity() {
@@ -26,4 +27,8 @@ final class BugCheckerRulesTest implements RefasterRuleCollectionTestCase {
.addInputLines("A.java", "class A {}")
.addOutputLines("A.java", "class A {}");
}
String testConstantsFormat() {
return String.format("\"%s\"", Convert.quote("foo"));
}
}

View File

@@ -4,12 +4,14 @@ import com.google.common.collect.ImmutableSet;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
import com.google.errorprone.bugpatterns.BugChecker;
import com.sun.tools.javac.util.Constants;
import com.sun.tools.javac.util.Convert;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class BugCheckerRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(FixChoosers.class);
return ImmutableSet.of(Convert.class, FixChoosers.class);
}
ImmutableSet<BugCheckerRefactoringTestHelper> testBugCheckerRefactoringTestHelperIdentity() {
@@ -24,4 +26,8 @@ final class BugCheckerRulesTest implements RefasterRuleCollectionTestCase {
.addInputLines("A.java", "class A {}")
.expectUnchanged();
}
String testConstantsFormat() {
return Constants.format("foo");
}
}

View File

@@ -31,6 +31,7 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
String::compareTo,
Comparator.comparing(identity()),
Comparator.comparing(s -> s),
Comparator.comparing(s -> 0),
Collections.<String>reverseOrder(reverseOrder()),
Comparator.<String>reverseOrder().reversed());
}
@@ -45,7 +46,8 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<Comparator<String>> testCustomComparator() {
return ImmutableSet.of(
Comparator.comparing(identity(), Comparator.comparingInt(String::length)),
Comparator.comparing(s -> s, Comparator.comparingInt(String::length)));
Comparator.comparing(s -> s, Comparator.comparingInt(String::length)),
Comparator.comparing(s -> "foo", Comparator.comparingInt(String::length)));
}
Comparator<String> testThenComparing() {
@@ -86,7 +88,8 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<Comparator<String>> testThenComparingNaturalOrder() {
return ImmutableSet.of(
Comparator.<String>naturalOrder().thenComparing(identity()),
Comparator.<String>naturalOrder().thenComparing(s -> s));
Comparator.<String>naturalOrder().thenComparing(s -> s),
Comparator.<String>naturalOrder().thenComparing(s -> 0));
}
ImmutableSet<Integer> testCompareTo() {

View File

@@ -28,7 +28,12 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<Comparator<String>> testNaturalOrder() {
return ImmutableSet.of(
naturalOrder(), naturalOrder(), naturalOrder(), naturalOrder(), naturalOrder());
naturalOrder(),
naturalOrder(),
naturalOrder(),
Comparator.comparing(s -> 0),
naturalOrder(),
naturalOrder());
}
ImmutableSet<Comparator<String>> testReverseOrder() {
@@ -38,7 +43,9 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<Comparator<String>> testCustomComparator() {
return ImmutableSet.of(
Comparator.comparingInt(String::length), Comparator.comparingInt(String::length));
Comparator.comparingInt(String::length),
Comparator.comparingInt(String::length),
Comparator.comparing(s -> "foo", Comparator.comparingInt(String::length)));
}
Comparator<String> testThenComparing() {
@@ -73,7 +80,8 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<Comparator<String>> testThenComparingNaturalOrder() {
return ImmutableSet.of(
Comparator.<String>naturalOrder().thenComparing(naturalOrder()),
Comparator.<String>naturalOrder().thenComparing(naturalOrder()));
Comparator.<String>naturalOrder().thenComparing(naturalOrder()),
Comparator.<String>naturalOrder().thenComparing(s -> 0));
}
ImmutableSet<Integer> testCompareTo() {

View File

@@ -76,11 +76,22 @@ final class ImmutableListMultimapRulesTest implements RefasterRuleCollectionTest
ImmutableSet<ImmutableListMultimap<Integer, Integer>> testIndexIterableToImmutableListMultimap() {
return ImmutableSet.of(
ImmutableList.of(1).stream().collect(toImmutableListMultimap(n -> n * 2, identity())),
Streams.stream(ImmutableList.of(2)::iterator)
.collect(toImmutableListMultimap(Integer::valueOf, n -> n)),
Streams.stream(ImmutableList.of(1).iterator())
.collect(toImmutableListMultimap(n -> n * 2, identity())),
Streams.stream(ImmutableList.of(2).iterator())
.collect(toImmutableListMultimap(n -> n * 2, v -> v)),
Streams.stream(ImmutableList.of(3).iterator())
.collect(toImmutableListMultimap(n -> n.intValue(), identity())));
.collect(toImmutableListMultimap(n -> n * 2, v -> 0)),
Streams.stream(ImmutableList.of(4)::iterator)
.collect(toImmutableListMultimap(Integer::valueOf, identity())),
Streams.stream(ImmutableList.of(5)::iterator)
.collect(toImmutableListMultimap(Integer::valueOf, v -> v)),
Streams.stream(ImmutableList.of(6)::iterator)
.collect(toImmutableListMultimap(Integer::valueOf, v -> 0)),
ImmutableList.of(7).stream()
.collect(toImmutableListMultimap(n -> n.intValue(), identity())),
ImmutableList.of(8).stream().collect(toImmutableListMultimap(n -> n.intValue(), v -> v)),
ImmutableList.of(9).stream().collect(toImmutableListMultimap(n -> n.intValue(), v -> 0)));
}
ImmutableListMultimap<String, Integer> testTransformMultimapValuesToImmutableListMultimap() {

View File

@@ -64,9 +64,17 @@ final class ImmutableListMultimapRulesTest implements RefasterRuleCollectionTest
ImmutableSet<ImmutableListMultimap<Integer, Integer>> testIndexIterableToImmutableListMultimap() {
return ImmutableSet.of(
Multimaps.index(ImmutableList.of(1), n -> n * 2),
Multimaps.index(ImmutableList.of(2)::iterator, Integer::valueOf),
Multimaps.index(ImmutableList.of(3).iterator(), n -> n.intValue()));
Multimaps.index(ImmutableList.of(1).iterator(), n -> n * 2),
Multimaps.index(ImmutableList.of(2).iterator(), n -> n * 2),
Streams.stream(ImmutableList.of(3).iterator())
.collect(toImmutableListMultimap(n -> n * 2, v -> 0)),
Multimaps.index(ImmutableList.of(4)::iterator, Integer::valueOf),
Multimaps.index(ImmutableList.of(5)::iterator, Integer::valueOf),
Streams.stream(ImmutableList.of(6)::iterator)
.collect(toImmutableListMultimap(Integer::valueOf, v -> 0)),
Multimaps.index(ImmutableList.of(7), n -> n.intValue()),
Multimaps.index(ImmutableList.of(8), n -> n.intValue()),
ImmutableList.of(9).stream().collect(toImmutableListMultimap(n -> n.intValue(), v -> 0)));
}
ImmutableListMultimap<String, Integer> testTransformMultimapValuesToImmutableListMultimap() {

View File

@@ -34,11 +34,21 @@ final class ImmutableMapRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<ImmutableMap<Integer, Integer>> testIterableToImmutableMap() {
return ImmutableSet.of(
ImmutableList.of(1).stream().collect(toImmutableMap(identity(), n -> n * 2)),
Streams.stream(ImmutableList.of(2)::iterator)
.collect(toImmutableMap(n -> n, Integer::valueOf)),
Streams.stream(ImmutableList.of(3).iterator())
ImmutableList.of(2).stream().collect(toImmutableMap(k -> k, n -> n * 2)),
ImmutableList.of(3).stream().collect(toImmutableMap(k -> 0, n -> n * 2)),
Streams.stream(ImmutableList.of(4)::iterator)
.collect(toImmutableMap(identity(), Integer::valueOf)),
Streams.stream(ImmutableList.of(5)::iterator)
.collect(toImmutableMap(k -> k, Integer::valueOf)),
Streams.stream(ImmutableList.of(6)::iterator)
.collect(toImmutableMap(k -> 0, Integer::valueOf)),
Streams.stream(ImmutableList.of(7).iterator())
.collect(toImmutableMap(identity(), n -> n.intValue())),
ImmutableMap.copyOf(Maps.asMap(ImmutableSet.of(4), Integer::valueOf)));
Streams.stream(ImmutableList.of(8).iterator())
.collect(toImmutableMap(k -> k, n -> n.intValue())),
Streams.stream(ImmutableList.of(9).iterator())
.collect(toImmutableMap(k -> 0, n -> n.intValue())),
ImmutableMap.copyOf(Maps.asMap(ImmutableSet.of(10), Integer::valueOf)));
}
ImmutableSet<ImmutableMap<String, Integer>> testEntryIterableToImmutableMap() {
@@ -63,10 +73,20 @@ final class ImmutableMapRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<ImmutableMap<Integer, Integer>> testIndexIterableToImmutableMap() {
return ImmutableSet.of(
ImmutableList.of(1).stream().collect(toImmutableMap(n -> n * 2, identity())),
Streams.stream(ImmutableList.of(2)::iterator)
.collect(toImmutableMap(Integer::valueOf, n -> n)),
Streams.stream(ImmutableList.of(3).iterator())
.collect(toImmutableMap(n -> n.intValue(), identity())));
ImmutableList.of(2).stream().collect(toImmutableMap(n -> n * 2, v -> v)),
ImmutableList.of(3).stream().collect(toImmutableMap(n -> n * 2, v -> 0)),
Streams.stream(ImmutableList.of(4)::iterator)
.collect(toImmutableMap(Integer::valueOf, identity())),
Streams.stream(ImmutableList.of(5)::iterator)
.collect(toImmutableMap(Integer::valueOf, v -> v)),
Streams.stream(ImmutableList.of(6)::iterator)
.collect(toImmutableMap(Integer::valueOf, v -> 0)),
Streams.stream(ImmutableList.of(7).iterator())
.collect(toImmutableMap(n -> n.intValue(), identity())),
Streams.stream(ImmutableList.of(8).iterator())
.collect(toImmutableMap(n -> n.intValue(), v -> v)),
Streams.stream(ImmutableList.of(9).iterator())
.collect(toImmutableMap(n -> n.intValue(), v -> 0)));
}
ImmutableSet<ImmutableMap<String, Integer>> testTransformMapValuesToImmutableMap() {

View File

@@ -33,9 +33,17 @@ final class ImmutableMapRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<ImmutableMap<Integer, Integer>> testIterableToImmutableMap() {
return ImmutableSet.of(
Maps.toMap(ImmutableList.of(1), n -> n * 2),
Maps.toMap(ImmutableList.of(2)::iterator, Integer::valueOf),
Maps.toMap(ImmutableList.of(3).iterator(), n -> n.intValue()),
Maps.toMap(ImmutableSet.of(4), Integer::valueOf));
Maps.toMap(ImmutableList.of(2), n -> n * 2),
ImmutableList.of(3).stream().collect(toImmutableMap(k -> 0, n -> n * 2)),
Maps.toMap(ImmutableList.of(4)::iterator, Integer::valueOf),
Maps.toMap(ImmutableList.of(5)::iterator, Integer::valueOf),
Streams.stream(ImmutableList.of(6)::iterator)
.collect(toImmutableMap(k -> 0, Integer::valueOf)),
Maps.toMap(ImmutableList.of(7).iterator(), n -> n.intValue()),
Maps.toMap(ImmutableList.of(8).iterator(), n -> n.intValue()),
Streams.stream(ImmutableList.of(9).iterator())
.collect(toImmutableMap(k -> 0, n -> n.intValue())),
Maps.toMap(ImmutableSet.of(10), Integer::valueOf));
}
ImmutableSet<ImmutableMap<String, Integer>> testEntryIterableToImmutableMap() {
@@ -54,8 +62,16 @@ final class ImmutableMapRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<ImmutableMap<Integer, Integer>> testIndexIterableToImmutableMap() {
return ImmutableSet.of(
Maps.uniqueIndex(ImmutableList.of(1), n -> n * 2),
Maps.uniqueIndex(ImmutableList.of(2)::iterator, Integer::valueOf),
Maps.uniqueIndex(ImmutableList.of(3).iterator(), n -> n.intValue()));
Maps.uniqueIndex(ImmutableList.of(2), n -> n * 2),
ImmutableList.of(3).stream().collect(toImmutableMap(n -> n * 2, v -> 0)),
Maps.uniqueIndex(ImmutableList.of(4)::iterator, Integer::valueOf),
Maps.uniqueIndex(ImmutableList.of(5)::iterator, Integer::valueOf),
Streams.stream(ImmutableList.of(6)::iterator)
.collect(toImmutableMap(Integer::valueOf, v -> 0)),
Maps.uniqueIndex(ImmutableList.of(7).iterator(), n -> n.intValue()),
Maps.uniqueIndex(ImmutableList.of(8).iterator(), n -> n.intValue()),
Streams.stream(ImmutableList.of(9).iterator())
.collect(toImmutableMap(n -> n.intValue(), v -> 0)));
}
ImmutableSet<ImmutableMap<String, Integer>> testTransformMapValuesToImmutableMap() {

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.refasterrules;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -29,7 +30,10 @@ final class JUnitToAssertJRulesTest implements RefasterRuleCollectionTestCase {
(Runnable) () -> assertNotSame(null, null),
(Runnable) () -> assertNull(null),
(Runnable) () -> assertSame(null, null),
(Runnable) () -> assertTrue(true));
(Runnable) () -> assertTrue(true),
(Runnable) () -> assertEquals(0, 0),
(Runnable) () -> assertEquals(0, 0, "foo"),
(Runnable) () -> assertEquals(0, 0, () -> "foo"));
}
void testThrowNewAssertionError() {
@@ -170,4 +174,28 @@ final class JUnitToAssertJRulesTest implements RefasterRuleCollectionTestCase {
void testAssertThatWithFailMessageSupplierIsInstanceOf() {
assertInstanceOf(Object.class, new Object(), () -> "foo");
}
void testAssertThatByteIsEqualTo() {
assertEquals((byte) 0, (byte) 0);
}
void testAssertThatByteWithFailMessageStringIsEqualTo() {
assertEquals((byte) 0, (byte) 0, "foo");
}
void testAssertThatByteWithFailMessageSupplierIsEqualTo() {
assertEquals((byte) 0, (byte) 0, () -> "foo");
}
void testAssertThatCharIsEqualTo() {
assertEquals('a', 'a');
}
void testAssertThatCharWithFailMessageStringIsEqualTo() {
assertEquals('a', 'a', "foo");
}
void testAssertThatCharWithFailMessageSupplierIsEqualTo() {
assertEquals('a', 'a', () -> "foo");
}
}

View File

@@ -5,6 +5,7 @@ import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -33,7 +34,10 @@ final class JUnitToAssertJRulesTest implements RefasterRuleCollectionTestCase {
(Runnable) () -> assertNotSame(null, null),
(Runnable) () -> assertNull(null),
(Runnable) () -> assertSame(null, null),
(Runnable) () -> assertTrue(true));
(Runnable) () -> assertTrue(true),
(Runnable) () -> assertEquals(0, 0),
(Runnable) () -> assertEquals(0, 0, "foo"),
(Runnable) () -> assertEquals(0, 0, () -> "foo"));
}
void testThrowNewAssertionError() {
@@ -180,4 +184,28 @@ final class JUnitToAssertJRulesTest implements RefasterRuleCollectionTestCase {
void testAssertThatWithFailMessageSupplierIsInstanceOf() {
assertThat(new Object()).withFailMessage(() -> "foo").isInstanceOf(Object.class);
}
void testAssertThatByteIsEqualTo() {
assertThat((byte) 0).isEqualTo((byte) 0);
}
void testAssertThatByteWithFailMessageStringIsEqualTo() {
assertThat((byte) 0).withFailMessage("foo").isEqualTo((byte) 0);
}
void testAssertThatByteWithFailMessageSupplierIsEqualTo() {
assertThat((byte) 0).withFailMessage(() -> "foo").isEqualTo((byte) 0);
}
void testAssertThatCharIsEqualTo() {
assertThat('a').isEqualTo('a');
}
void testAssertThatCharWithFailMessageStringIsEqualTo() {
assertThat('a').withFailMessage("foo").isEqualTo('a');
}
void testAssertThatCharWithFailMessageSupplierIsEqualTo() {
assertThat('a').withFailMessage(() -> "foo").isEqualTo('a');
}
}

View File

@@ -164,4 +164,30 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
boolean testDoubleIsFinite() {
return Doubles.isFinite(1);
}
ImmutableSet<Boolean> testIntegerSignumIsPositive() {
return ImmutableSet.of(
Integer.signum(1) > 0,
Integer.signum(2) >= 1,
Integer.signum(3) <= 0,
Integer.signum(4) < 1);
}
ImmutableSet<Boolean> testIntegerSignumIsNegative() {
return ImmutableSet.of(
Integer.signum(1) < 0,
Integer.signum(2) <= -1,
Integer.signum(3) >= 0,
Integer.signum(4) > -1);
}
ImmutableSet<Boolean> testLongSignumIsPositive() {
return ImmutableSet.of(
Long.signum(1L) > 0, Long.signum(2L) >= 1, Long.signum(3L) <= 0, Long.signum(4L) < 1);
}
ImmutableSet<Boolean> testLongSignumIsNegative() {
return ImmutableSet.of(
Long.signum(1L) < 0, Long.signum(2L) <= -1, Long.signum(3L) >= 0, Long.signum(4L) > -1);
}
}

View File

@@ -164,4 +164,30 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
boolean testDoubleIsFinite() {
return Double.isFinite(1);
}
ImmutableSet<Boolean> testIntegerSignumIsPositive() {
return ImmutableSet.of(
Integer.signum(1) == 1,
Integer.signum(2) == 1,
Integer.signum(3) != 1,
Integer.signum(4) != 1);
}
ImmutableSet<Boolean> testIntegerSignumIsNegative() {
return ImmutableSet.of(
Integer.signum(1) == -1,
Integer.signum(2) == -1,
Integer.signum(3) != -1,
Integer.signum(4) != -1);
}
ImmutableSet<Boolean> testLongSignumIsPositive() {
return ImmutableSet.of(
Long.signum(1L) == 1, Long.signum(2L) == 1, Long.signum(3L) != 1, Long.signum(4L) != 1);
}
ImmutableSet<Boolean> testLongSignumIsNegative() {
return ImmutableSet.of(
Long.signum(1L) == -1, Long.signum(2L) == -1, Long.signum(3L) != -1, Long.signum(4L) != -1);
}
}

View File

@@ -202,20 +202,26 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableSet.of(
Flux.just(1).flatMap(Mono::just, 1),
Flux.just(2).flatMapSequential(Mono::just, 1),
Flux.just(3).map(Mono::just).concatMap(identity()));
Flux.just(3).map(Mono::just).concatMap(identity()),
Flux.just(4).map(Mono::just).concatMap(v -> v),
Flux.just(5).map(Mono::just).concatMap(v -> Mono.empty()));
}
ImmutableSet<Flux<Integer>> testFluxConcatMapWithPrefetch() {
return ImmutableSet.of(
Flux.just(1).flatMap(Mono::just, 1, 3),
Flux.just(2).flatMapSequential(Mono::just, 1, 4),
Flux.just(3).map(Mono::just).concatMap(identity(), 5));
Flux.just(3).map(Mono::just).concatMap(identity(), 5),
Flux.just(4).map(Mono::just).concatMap(v -> v, 6),
Flux.just(5).map(Mono::just).concatMap(v -> Mono.empty(), 7));
}
ImmutableSet<Flux<Integer>> testMonoFlatMapIterable() {
return ImmutableSet.of(
Mono.just(1).map(ImmutableSet::of).flatMapIterable(identity()),
Mono.just(2).flux().concatMapIterable(ImmutableSet::of));
Mono.just(2).map(ImmutableSet::of).flatMapIterable(v -> v),
Mono.just(3).map(ImmutableSet::of).flatMapIterable(v -> ImmutableSet.of()),
Mono.just(4).flux().concatMapIterable(ImmutableSet::of));
}
Flux<Integer> testMonoFlatMapIterableIdentity() {
@@ -225,13 +231,17 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<Flux<Integer>> testFluxConcatMapIterable() {
return ImmutableSet.of(
Flux.just(1).flatMapIterable(ImmutableList::of),
Flux.just(2).map(ImmutableList::of).concatMapIterable(identity()));
Flux.just(2).map(ImmutableList::of).concatMapIterable(identity()),
Flux.just(3).map(ImmutableList::of).concatMapIterable(v -> v),
Flux.just(4).map(ImmutableList::of).concatMapIterable(v -> ImmutableSet.of()));
}
ImmutableSet<Flux<Integer>> testFluxConcatMapIterableWithPrefetch() {
return ImmutableSet.of(
Flux.just(1).flatMapIterable(ImmutableList::of, 3),
Flux.just(2).map(ImmutableList::of).concatMapIterable(identity(), 3));
Flux.just(1).flatMapIterable(ImmutableList::of, 5),
Flux.just(2).map(ImmutableList::of).concatMapIterable(identity(), 5),
Flux.just(3).map(ImmutableList::of).concatMapIterable(v -> v, 5),
Flux.just(4).map(ImmutableList::of).concatMapIterable(v -> ImmutableSet.of(), 5));
}
Flux<String> testMonoFlatMapToFlux() {
@@ -372,25 +382,30 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return Flux.just(1).filter(Number.class::isInstance).cast(Number.class);
}
Mono<String> testMonoFlatMap() {
return Mono.just("foo").map(Mono::just).flatMap(identity());
ImmutableSet<Mono<String>> testMonoFlatMap() {
return ImmutableSet.of(
Mono.just("foo").map(Mono::just).flatMap(identity()),
Mono.just("bar").map(Mono::just).flatMap(v -> v),
Mono.just("baz").map(Mono::just).flatMap(v -> Mono.empty()));
}
ImmutableSet<Flux<Integer>> testMonoFlatMapMany() {
return ImmutableSet.of(
Mono.just(1).map(Mono::just).flatMapMany(identity()),
Mono.just(2).flux().concatMap(Mono::just),
Mono.just(3).flux().concatMap(Mono::just, 2),
Mono.just(4).flux().concatMapDelayError(Mono::just),
Mono.just(5).flux().concatMapDelayError(Mono::just, 2),
Mono.just(6).flux().concatMapDelayError(Mono::just, false, 2),
Mono.just(7).flux().flatMap(Mono::just, 2),
Mono.just(8).flux().flatMap(Mono::just, 2, 3),
Mono.just(9).flux().flatMapDelayError(Mono::just, 2, 3),
Mono.just(10).flux().flatMapSequential(Mono::just, 2),
Mono.just(11).flux().flatMapSequential(Mono::just, 2, 3),
Mono.just(12).flux().flatMapSequentialDelayError(Mono::just, 2, 3),
Mono.just(13).flux().switchMap(Mono::just));
Mono.just(2).map(Mono::just).flatMapMany(v -> v),
Mono.just(3).map(Mono::just).flatMapMany(v -> Flux.empty()),
Mono.just(4).flux().concatMap(Mono::just),
Mono.just(5).flux().concatMap(Mono::just, 2),
Mono.just(6).flux().concatMapDelayError(Mono::just),
Mono.just(7).flux().concatMapDelayError(Mono::just, 2),
Mono.just(8).flux().concatMapDelayError(Mono::just, false, 2),
Mono.just(9).flux().flatMap(Mono::just, 2),
Mono.just(10).flux().flatMap(Mono::just, 2, 3),
Mono.just(11).flux().flatMapDelayError(Mono::just, 2, 3),
Mono.just(12).flux().flatMapSequential(Mono::just, 2),
Mono.just(13).flux().flatMapSequential(Mono::just, 2, 3),
Mono.just(14).flux().flatMapSequentialDelayError(Mono::just, 2, 3),
Mono.just(15).flux().switchMap(Mono::just));
}
ImmutableSet<Flux<String>> testConcatMapIterableIdentity() {
@@ -511,7 +526,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
}
ImmutableSet<Context> testContextEmpty() {
return ImmutableSet.of(Context.of(new HashMap<>()), Context.of(ImmutableMap.of()));
return ImmutableSet.of(Context.of(ImmutableMap.of()), Context.of(ImmutableMap.of(1, 2)));
}
ImmutableSet<PublisherProbe<Void>> testPublisherProbeEmpty() {
@@ -528,14 +543,16 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<StepVerifier.Step<Integer>> testStepVerifierStepIdentity() {
return ImmutableSet.of(
StepVerifier.create(Mono.just(1)).expectNext(),
StepVerifier.create(Mono.just(2)).expectNextCount(0L));
Mono.just(1).as(StepVerifier::create).expectNext(),
Mono.just(2).as(StepVerifier::create).expectNextCount(0L),
Mono.just(3).as(StepVerifier::create).expectNextSequence(ImmutableList.of()),
Mono.just(4).as(StepVerifier::create).expectNextSequence(ImmutableList.of(5)));
}
ImmutableSet<StepVerifier.Step<String>> testStepVerifierStepExpectNext() {
return ImmutableSet.of(
StepVerifier.create(Mono.just("foo")).expectNextMatches(s -> s.equals("bar")),
StepVerifier.create(Mono.just("baz")).expectNextMatches("qux"::equals));
Mono.just("foo").as(StepVerifier::create).expectNextMatches(s -> s.equals("bar")),
Mono.just("baz").as(StepVerifier::create).expectNextMatches("qux"::equals));
}
StepVerifier.Step<?> testFluxAsStepVerifierExpectNext() {
@@ -546,37 +563,40 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
}
Duration testStepVerifierLastStepVerifyComplete() {
return StepVerifier.create(Mono.empty()).expectComplete().verify();
return Mono.empty().as(StepVerifier::create).expectComplete().verify();
}
Duration testStepVerifierLastStepVerifyError() {
return StepVerifier.create(Mono.empty()).expectError().verify();
return Mono.empty().as(StepVerifier::create).expectError().verify();
}
ImmutableSet<Duration> testStepVerifierLastStepVerifyErrorClass() {
return ImmutableSet.of(
StepVerifier.create(Mono.empty()).expectError(IllegalArgumentException.class).verify(),
StepVerifier.create(Mono.empty())
Mono.empty().as(StepVerifier::create).expectError(IllegalArgumentException.class).verify(),
Mono.empty()
.as(StepVerifier::create)
.verifyErrorMatches(IllegalStateException.class::isInstance),
StepVerifier.create(Mono.empty())
Mono.empty()
.as(StepVerifier::create)
.verifyErrorSatisfies(t -> assertThat(t).isInstanceOf(AssertionError.class)));
}
Duration testStepVerifierLastStepVerifyErrorMatches() {
return StepVerifier.create(Mono.empty())
return Mono.empty()
.as(StepVerifier::create)
.expectErrorMatches(IllegalArgumentException.class::equals)
.verify();
}
Duration testStepVerifierLastStepVerifyErrorSatisfies() {
return StepVerifier.create(Mono.empty()).expectErrorSatisfies(t -> {}).verify();
return Mono.empty().as(StepVerifier::create).expectErrorSatisfies(t -> {}).verify();
}
Duration testStepVerifierLastStepVerifyErrorMessage() {
return StepVerifier.create(Mono.empty()).expectErrorMessage("foo").verify();
return Mono.empty().as(StepVerifier::create).expectErrorMessage("foo").verify();
}
Duration testStepVerifierLastStepVerifyTimeout() {
return StepVerifier.create(Mono.empty()).expectTimeout(Duration.ZERO).verify();
return Mono.empty().as(StepVerifier::create).expectTimeout(Duration.ZERO).verify();
}
}

View File

@@ -205,20 +205,26 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableSet.of(
Flux.just(1).concatMap(Mono::just),
Flux.just(2).concatMap(Mono::just),
Flux.just(3).concatMap(Mono::just));
Flux.just(3).concatMap(Mono::just),
Flux.just(4).concatMap(Mono::just),
Flux.just(5).map(Mono::just).concatMap(v -> Mono.empty()));
}
ImmutableSet<Flux<Integer>> testFluxConcatMapWithPrefetch() {
return ImmutableSet.of(
Flux.just(1).concatMap(Mono::just, 3),
Flux.just(2).concatMap(Mono::just, 4),
Flux.just(3).concatMap(Mono::just, 5));
Flux.just(3).concatMap(Mono::just, 5),
Flux.just(4).concatMap(Mono::just, 6),
Flux.just(5).map(Mono::just).concatMap(v -> Mono.empty(), 7));
}
ImmutableSet<Flux<Integer>> testMonoFlatMapIterable() {
return ImmutableSet.of(
Mono.just(1).flatMapIterable(ImmutableSet::of),
Mono.just(2).flatMapIterable(ImmutableSet::of));
Mono.just(2).flatMapIterable(ImmutableSet::of),
Mono.just(3).map(ImmutableSet::of).flatMapIterable(v -> ImmutableSet.of()),
Mono.just(4).flatMapIterable(ImmutableSet::of));
}
Flux<Integer> testMonoFlatMapIterableIdentity() {
@@ -228,13 +234,17 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<Flux<Integer>> testFluxConcatMapIterable() {
return ImmutableSet.of(
Flux.just(1).concatMapIterable(ImmutableList::of),
Flux.just(2).concatMapIterable(ImmutableList::of));
Flux.just(2).concatMapIterable(ImmutableList::of),
Flux.just(3).concatMapIterable(ImmutableList::of),
Flux.just(4).map(ImmutableList::of).concatMapIterable(v -> ImmutableSet.of()));
}
ImmutableSet<Flux<Integer>> testFluxConcatMapIterableWithPrefetch() {
return ImmutableSet.of(
Flux.just(1).concatMapIterable(ImmutableList::of, 3),
Flux.just(2).concatMapIterable(ImmutableList::of, 3));
Flux.just(1).concatMapIterable(ImmutableList::of, 5),
Flux.just(2).concatMapIterable(ImmutableList::of, 5),
Flux.just(3).concatMapIterable(ImmutableList::of, 5),
Flux.just(4).map(ImmutableList::of).concatMapIterable(v -> ImmutableSet.of(), 5));
}
Flux<String> testMonoFlatMapToFlux() {
@@ -365,15 +375,18 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return Flux.just(1).ofType(Number.class);
}
Mono<String> testMonoFlatMap() {
return Mono.just("foo").flatMap(Mono::just);
ImmutableSet<Mono<String>> testMonoFlatMap() {
return ImmutableSet.of(
Mono.just("foo").flatMap(Mono::just),
Mono.just("bar").flatMap(Mono::just),
Mono.just("baz").map(Mono::just).flatMap(v -> Mono.empty()));
}
ImmutableSet<Flux<Integer>> testMonoFlatMapMany() {
return ImmutableSet.of(
Mono.just(1).flatMapMany(Mono::just),
Mono.just(2).flatMapMany(Mono::just),
Mono.just(3).flatMapMany(Mono::just),
Mono.just(3).map(Mono::just).flatMapMany(v -> Flux.empty()),
Mono.just(4).flatMapMany(Mono::just),
Mono.just(5).flatMapMany(Mono::just),
Mono.just(6).flatMapMany(Mono::just),
@@ -383,7 +396,9 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
Mono.just(10).flatMapMany(Mono::just),
Mono.just(11).flatMapMany(Mono::just),
Mono.just(12).flatMapMany(Mono::just),
Mono.just(13).flatMapMany(Mono::just));
Mono.just(13).flatMapMany(Mono::just),
Mono.just(14).flatMapMany(Mono::just),
Mono.just(15).flatMapMany(Mono::just));
}
ImmutableSet<Flux<String>> testConcatMapIterableIdentity() {
@@ -500,7 +515,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
}
ImmutableSet<Context> testContextEmpty() {
return ImmutableSet.of(Context.empty(), Context.empty());
return ImmutableSet.of(Context.empty(), Context.of(ImmutableMap.of(1, 2)));
}
ImmutableSet<PublisherProbe<Void>> testPublisherProbeEmpty() {
@@ -516,13 +531,17 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
}
ImmutableSet<StepVerifier.Step<Integer>> testStepVerifierStepIdentity() {
return ImmutableSet.of(StepVerifier.create(Mono.just(1)), StepVerifier.create(Mono.just(2)));
return ImmutableSet.of(
Mono.just(1).as(StepVerifier::create),
Mono.just(2).as(StepVerifier::create),
Mono.just(3).as(StepVerifier::create),
Mono.just(4).as(StepVerifier::create).expectNextSequence(ImmutableList.of(5)));
}
ImmutableSet<StepVerifier.Step<String>> testStepVerifierStepExpectNext() {
return ImmutableSet.of(
StepVerifier.create(Mono.just("foo")).expectNext("bar"),
StepVerifier.create(Mono.just("baz")).expectNext("qux"));
Mono.just("foo").as(StepVerifier::create).expectNext("bar"),
Mono.just("baz").as(StepVerifier::create).expectNext("qux"));
}
StepVerifier.Step<?> testFluxAsStepVerifierExpectNext() {
@@ -530,34 +549,35 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
}
Duration testStepVerifierLastStepVerifyComplete() {
return StepVerifier.create(Mono.empty()).verifyComplete();
return Mono.empty().as(StepVerifier::create).verifyComplete();
}
Duration testStepVerifierLastStepVerifyError() {
return StepVerifier.create(Mono.empty()).verifyError();
return Mono.empty().as(StepVerifier::create).verifyError();
}
ImmutableSet<Duration> testStepVerifierLastStepVerifyErrorClass() {
return ImmutableSet.of(
StepVerifier.create(Mono.empty()).verifyError(IllegalArgumentException.class),
StepVerifier.create(Mono.empty()).verifyError(IllegalStateException.class),
StepVerifier.create(Mono.empty()).verifyError(AssertionError.class));
Mono.empty().as(StepVerifier::create).verifyError(IllegalArgumentException.class),
Mono.empty().as(StepVerifier::create).verifyError(IllegalStateException.class),
Mono.empty().as(StepVerifier::create).verifyError(AssertionError.class));
}
Duration testStepVerifierLastStepVerifyErrorMatches() {
return StepVerifier.create(Mono.empty())
return Mono.empty()
.as(StepVerifier::create)
.verifyErrorMatches(IllegalArgumentException.class::equals);
}
Duration testStepVerifierLastStepVerifyErrorSatisfies() {
return StepVerifier.create(Mono.empty()).verifyErrorSatisfies(t -> {});
return Mono.empty().as(StepVerifier::create).verifyErrorSatisfies(t -> {});
}
Duration testStepVerifierLastStepVerifyErrorMessage() {
return StepVerifier.create(Mono.empty()).verifyErrorMessage("foo");
return Mono.empty().as(StepVerifier::create).verifyErrorMessage("foo");
}
Duration testStepVerifierLastStepVerifyTimeout() {
return StepVerifier.create(Mono.empty()).verifyTimeout(Duration.ZERO);
return Mono.empty().as(StepVerifier::create).verifyTimeout(Duration.ZERO);
}
}

View File

@@ -258,7 +258,8 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<Stream<Integer>> testStreamsConcat() {
return ImmutableSet.of(
Stream.of(Stream.of(1), Stream.of(2)).flatMap(identity()),
Stream.of(Stream.of(3), Stream.of(4)).flatMap(v -> v));
Stream.of(Stream.of(3), Stream.of(4)).flatMap(v -> v),
Stream.of(Stream.of(5), Stream.of(6)).flatMap(v -> Stream.empty()));
}
Stream<Integer> testStreamTakeWhile() {

View File

@@ -258,7 +258,9 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<Stream<Integer>> testStreamsConcat() {
return ImmutableSet.of(
Streams.concat(Stream.of(1), Stream.of(2)), Streams.concat(Stream.of(3), Stream.of(4)));
Streams.concat(Stream.of(1), Stream.of(2)),
Streams.concat(Stream.of(3), Stream.of(4)),
Stream.of(Stream.of(5), Stream.of(6)).flatMap(v -> Stream.empty()));
}
Stream<Integer> testStreamTakeWhile() {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
src/it/java/com/google/checkstyle/test/chapter7javadoc/rule711generalform/InvalidJavadocPositionTest.java:[35,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/it/java/com/google/checkstyle/test/chapter7javadoc/rule734nonrequiredjavadoc/InvalidJavadocPositionTest.java:[35,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/it/java/com/sun/checkstyle/test/chapter5comments/rule52documentationcomments/InvalidJavadocPositionTest.java:[35,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionAbbreviationAsWordInNameTest.java:[117,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `enum` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionAbbreviationAsWordInNameTest.java:[169,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `interface` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionAbbreviationAsWordInNameTest.java:[91,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `class` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionAnnotationLocationTest.java:[104,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `enum` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionAnnotationLocationTest.java:[39,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `class` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionAnnotationLocationTest.java:[71,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `interface` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionAnonInnerLengthTest.java:[39,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionAvoidEscapedUnicodeCharactersTest.java:[40,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionAvoidNoArgumentSuperConstructorCallTest.java:[41,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionEqualsAvoidNullTest.java:[39,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that a method named `equals` is already defined in this class or a supertype)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionLambdaBodyLengthTest.java:[39,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionMissingJavadocTypeTest.java:[39,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `class` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionMissingOverrideTest.java:[39,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `class` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionMissingOverrideTest.java:[67,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `interface` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionNeedBracesTest.java:[40,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `do` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionOuterTypeNumberTest.java:[39,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionUnnecessarySemicolonAfterTypeMemberDeclarationTest.java:[41,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionUnnecessarySemicolonInTryWithResourcesTest.java:[41,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/DetailAstImplTest.java:[597,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that a method named `toString` is already defined in this class or a supertype)
src/test/java/com/puppycrawl/tools/checkstyle/PackageNamesLoaderTest.java:[58,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/api/FilterSetTest.java:[49,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that a method named `toString` is already defined in this class or a supertype)
src/test/java/com/puppycrawl/tools/checkstyle/api/FullIdentTest.java:[41,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that a method named `toString` is already defined in this class or a supertype)
src/test/java/com/puppycrawl/tools/checkstyle/checks/AvoidEscapedUnicodeCharactersCheckTest.java:[77,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/DescendantTokenCheckTest.java:[39,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/OrderedPropertiesCheckTest.java:[49,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/UniquePropertiesCheckTest.java:[65,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/annotation/AnnotationLocationCheckTest.java:[193,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `class` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/annotation/AnnotationLocationCheckTest.java:[206,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `enum` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/annotation/AnnotationLocationCheckTest.java:[217,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `interface` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/annotation/AnnotationLocationCheckTest.java:[228,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `package` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/annotation/AnnotationUseStyleCheckTest.java:[103,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/blocks/EmptyBlockCheckTest.java:[49,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/blocks/EmptyCatchBlockCheckTest.java:[46,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/blocks/LeftCurlyCheckTest.java:[59,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/blocks/RightCurlyCheckTest.java:[50,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/ArrayTrailingCommaCheckTest.java:[36,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/AvoidDoubleBraceInitializationCheckTest.java:[37,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/AvoidInlineConditionalsCheckTest.java:[36,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/AvoidNoArgumentSuperConstructorCallCheckTest.java:[37,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/CovariantEqualsCheckTest.java:[36,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheckTest.java:[44,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/DefaultComesLastCheckTest.java:[55,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/ExplicitInitializationCheckTest.java:[36,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/FallThroughCheckTest.java:[38,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/FinalLocalVariableCheckTest.java:[39,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/HiddenFieldCheckTest.java:[105,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/IllegalCatchCheckTest.java:[36,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/IllegalInstantiationCheckTest.java:[48,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/IllegalThrowsCheckTest.java:[37,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/IllegalTokenCheckTest.java:[57,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `native` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/NestedIfDepthCheckTest.java:[37,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/NestedTryDepthCheckTest.java:[36,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/NoArrayTrailingCommaCheckTest.java:[36,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/NoEnumTrailingCommaCheckTest.java:[36,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/OverloadMethodsDeclarationOrderCheckTest.java:[36,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/ParameterAssignmentCheckTest.java:[45,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/RequireThisCheckTest.java:[290,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `static` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/RequireThisCheckTest.java:[373,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `for` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheckTest.java:[48,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/UnnecessaryParenthesesCheckTest.java:[125,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `return` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/UnnecessaryParenthesesCheckTest.java:[44,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/UnnecessarySemicolonAfterOuterTypeDeclarationCheckTest.java:[40,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/UnnecessarySemicolonAfterTypeMemberDeclarationCheckTest.java:[40,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/UnnecessarySemicolonInEnumerationCheckTest.java:[38,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/UnnecessarySemicolonInTryWithResourcesCheckTest.java:[38,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/design/InterfaceIsTypeCheckTest.java:[37,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/design/MutableExceptionCheckTest.java:[54,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/design/ThrowsCountCheckTest.java:[39,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/imports/ImportControlCheckTest.java:[99,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `null` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheckTest.java:[81,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/InvalidJavadocPositionCheckTest.java:[59,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocContentLocationCheckTest.java:[57,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocContentLocationCheckTest.java:[75,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `package` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocContentLocationCheckTest.java:[83,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `interface` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocNodeImplTest.java:[30,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that a method named `toString` is already defined in this class or a supertype)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTagInfoTest.java:[230,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `throws` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTagInfoTest.java:[294,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `return` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTagTest.java:[58,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that a method named `toString` is already defined in this class or a supertype)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTypeCheckTest.java:[83,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `protected` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTypeCheckTest.java:[89,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `public` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocVariableCheckTest.java:[60,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/MissingJavadocTypeCheckTest.java:[102,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `public` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/MissingJavadocTypeCheckTest.java:[94,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `protected` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/utils/InlineTagUtilTest.java:[38,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that a method named `extractInlineTags` is already defined in this class or a supertype)
src/test/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassDataAbstractionCouplingCheckTest.java:[189,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `new` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassFanOutComplexityCheckTest.java:[188,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `extends` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassFanOutComplexityCheckTest.java:[196,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `implements` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassFanOutComplexityCheckTest.java:[266,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `throws` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/naming/AbbreviationAsWordInNameCheckTest.java:[36,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/naming/AccessModifierOptionTest.java:[45,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `case` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/naming/ConstantNameCheckTest.java:[67,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/naming/LocalFinalVariableNameCheckTest.java:[48,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/naming/LocalVariableNameCheckTest.java:[48,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/naming/MethodNameCheckTest.java:[48,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/naming/PackageNameCheckTest.java:[60,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/naming/ParameterNameCheckTest.java:[48,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `catch` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/naming/ParameterNameCheckTest.java:[78,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/naming/PatternVariableNameCheckTest.java:[47,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/naming/TypeNameCheckTest.java:[44,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/regexp/RegexpSinglelineCheckTest.java:[120,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/regexp/RegexpSinglelineJavaCheckTest.java:[195,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/AnonInnerLengthCheckTest.java:[56,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/LambdaBodyLengthCheckTest.java:[56,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheckTest.java:[94,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `enum` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/MethodLengthCheckTest.java:[93,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `abstract` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/OuterTypeNumberCheckTest.java:[73,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/ParameterNumberCheckTest.java:[58,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/RecordComponentNumberCheckTest.java:[62,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/EmptyForInitializerPadCheckTest.java:[50,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/EmptyForIteratorPadCheckTest.java:[49,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/EmptyLineSeparatorCheckTest.java:[51,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/FileTabCharacterCheckTest.java:[36,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/GenericWhitespaceCheckTest.java:[62,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/MethodParamPadCheckTest.java:[50,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/NoWhitespaceAfterCheckTest.java:[174,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `synchronized` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/NoWhitespaceAfterCheckTest.java:[40,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/NoWhitespaceBeforeCaseDefaultColonCheckTest.java:[38,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/NoWhitespaceBeforeCheckTest.java:[36,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/OperatorWrapCheckTest.java:[39,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/ParenPadCheckTest.java:[44,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/TypecastParenPadCheckTest.java:[41,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/WhitespaceAfterCheckTest.java:[46,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/filters/IntMatchFilterElementTest.java:[45,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that a method named `toString` is already defined in this class or a supertype)
src/test/java/com/puppycrawl/tools/checkstyle/filters/SeverityMatchFilterTest.java:[36,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/filters/SuppressFilterElementTest.java:[185,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that a method named `equals` is already defined in this class or a supertype)
src/test/java/com/puppycrawl/tools/checkstyle/filters/SuppressWarningsFilterTest.java:[83,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/filters/SuppressWithNearbyCommentFilterTest.java:[210,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/filters/SuppressionCommentFilterTest.java:[142,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/filters/SuppressionSingleFilterTest.java:[42,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `default` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/grammar/antlr4/Antlr4AstRegressionTest.java:[34,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `package` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/grammar/comments/CommentsTest.java:[53,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that a method named `toString` is already defined in this class or a supertype)
src/test/java/com/puppycrawl/tools/checkstyle/grammar/java8/DefaultMethodsTest.java:[40,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `switch` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/gui/BaseCellEditorTest.java:[31,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that a method named `toString` is already defined in this class or a supertype)
src/test/java/com/puppycrawl/tools/checkstyle/utils/CheckUtilTest.java:[71,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that a method named `equals` is already defined in this class or a supertype)
src/test/java/com/puppycrawl/tools/checkstyle/xpath/XpathQueryGeneratorTest.java:[205,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `package` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/xpath/XpathQueryGeneratorTest.java:[219,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `import` is not a valid identifier)
src/test/java/com/puppycrawl/tools/checkstyle/xpath/XpathQueryGeneratorTest.java:[265,8] [JUnitMethodDeclaration] This method's name should not redundantly start with `test` (but note that `switch` is not a valid identifier)

View File

@@ -0,0 +1,185 @@
--- a/pom.xml
+++ b/pom.xml
@@ -334,6 +334,12 @@
<version>1.1.3</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>${assertj.version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>nl.jqno.equalsverifier</groupId>
<artifactId>equalsverifier</artifactId>
@@ -2397,8 +2403,10 @@
<arg>-Xpkginfo:always</arg>
<arg>-XDcompilePolicy=simple</arg>
<arg>
- -Xplugin:ErrorProne
+ -Xplugin:ErrorProne ${error-prone.flags}
</arg>
+ <arg>-Xmaxwarns</arg>
+ <arg>1000000</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
@@ -2406,6 +2414,16 @@
<artifactId>error_prone_core</artifactId>
<version>${error-prone.version}</version>
</path>
+ <path>
+ <groupId>tech.picnic.error-prone-support</groupId>
+ <artifactId>error-prone-contrib</artifactId>
+ <version>${error-prone-support.version}</version>
+ </path>
+ <path>
+ <groupId>tech.picnic.error-prone-support</groupId>
+ <artifactId>refaster-runner</artifactId>
+ <version>${error-prone-support.version}</version>
+ </path>
</annotationProcessorPaths>
</configuration>
</execution>
@@ -2449,9 +2467,10 @@
<arg>-Xpkginfo:always</arg>
<arg>-XDcompilePolicy=simple</arg>
<arg>
- -Xplugin:ErrorProne \
- -XepExcludedPaths:.*[\\/]resources[\\/].*
+ -Xplugin:ErrorProne ${error-prone.flags}
</arg>
+ <arg>-Xmaxwarns</arg>
+ <arg>1000000</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
@@ -2459,6 +2478,16 @@
<artifactId>error_prone_core</artifactId>
<version>${error-prone.version}</version>
</path>
+ <path>
+ <groupId>tech.picnic.error-prone-support</groupId>
+ <artifactId>error-prone-contrib</artifactId>
+ <version>${error-prone-support.version}</version>
+ </path>
+ <path>
+ <groupId>tech.picnic.error-prone-support</groupId>
+ <artifactId>refaster-runner</artifactId>
+ <version>${error-prone-support.version}</version>
+ </path>
</annotationProcessorPaths>
</configuration>
</execution>
--- a/src/main/java/com/puppycrawl/tools/checkstyle/DetailNodeTreeStringPrinter.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/DetailNodeTreeStringPrinter.java
@@ -63,6 +63,8 @@ public final class DetailNodeTreeStringPrinter {
* @return DetailNode tree
* @throws IllegalArgumentException if there is an error parsing the Javadoc.
*/
+ // Invoking `getParseErrorMessage` requires that `status.getParseErrorMessage()` is `null`.
+ @SuppressWarnings("CheckArgumentWithMessage")
public static DetailNode parseJavadocAsDetailNode(DetailAST blockComment) {
final JavadocDetailNodeParser parser = new JavadocDetailNodeParser();
final ParseStatus status = parser.parseJavadocAsDetailNode(blockComment);
--- a/src/main/java/com/puppycrawl/tools/checkstyle/Main.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/Main.java
@@ -626,6 +626,8 @@ public final class Main {
+ "reported to standard out in plain format. Checkstyle requires a configuration "
+ "XML file that configures the checks to apply.",
mixinStandardHelpOptions = true)
+ // XXX: Don't reorder arguments to `picocli.CommandLine.Option#names`.
+ @SuppressWarnings("LexicographicalAnnotationAttributeListing")
private static final class CliOptions {
/** Width of CLI help option. */
--- a/src/main/java/com/puppycrawl/tools/checkstyle/SarifLogger.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/SarifLogger.java
@@ -139,6 +139,9 @@ public class SarifLogger extends AbstractAutomaticBean implements AuditListener
@Override
public void auditFinished(AuditEvent event) {
final String version = SarifLogger.class.getPackage().getImplementationVersion();
+ // Here `version` may be `null`, while `String#replace` requires non-`null` arguments.
+ // XXX: Investigate better nullness handling by `IdentityConversion`.
+ @SuppressWarnings("IdentityConversion")
final String rendered = report
.replace(VERSION_PLACEHOLDER, String.valueOf(version))
.replace(RESULTS_PLACEHOLDER, String.join(",\n", results));
--- a/src/main/java/com/puppycrawl/tools/checkstyle/site/SiteUtil.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/site/SiteUtil.java
@@ -419,6 +419,10 @@ public final class SiteUtil {
* @return a set of properties for the given class.
*/
public static Set<String> getPropertiesForDocumentation(Class<?> clss, Object instance) {
+ // XXX: File PR to replace `.collect(toSet())` with `.collect(toCollection(HashSet::new))`.
+ // XXX: Update `CollectorMutability` to recognize cases such as this one, where the created
+ // collection is clearly modified.
+ @SuppressWarnings("CollectorMutability")
final Set<String> properties =
getProperties(clss).stream()
.filter(prop -> {
--- a/src/test/java/com/puppycrawl/tools/checkstyle/CheckerTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/CheckerTest.java
@@ -93,6 +93,8 @@ import de.thetaphi.forbiddenapis.SuppressForbidden;
* @noinspectionreason ClassWithTooManyDependencies - complex tests require a large number
* of imports
*/
+// This class is referenced from another package.
+@SuppressWarnings("JUnitClassModifiers")
public class CheckerTest extends AbstractModuleTestSupport {
@TempDir
--- a/src/test/java/com/puppycrawl/tools/checkstyle/ConfigurationLoaderTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/ConfigurationLoaderTest.java
@@ -46,6 +46,8 @@ import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
/**
* Unit test for ConfigurationLoader.
*/
+// This class is referenced from another package.
+@SuppressWarnings("JUnitClassModifiers")
public class ConfigurationLoaderTest extends AbstractPathTestSupport {
@Override
--- a/src/test/java/com/puppycrawl/tools/checkstyle/PackageObjectFactoryTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/PackageObjectFactoryTest.java
@@ -98,6 +98,8 @@ public class PackageObjectFactoryTest {
public void testCtorNullPackageException1() {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
+ // XXX: Don't suggest `ImmutableSet.of(elem)` for nullable `elem`.
+ @SuppressWarnings("ImmutableSetOf1")
final Object test = new PackageObjectFactory(Collections.singleton(null), classLoader);
assertWithMessage("Exception is expected but got " + test).fail();
}
@@ -126,6 +128,8 @@ public class PackageObjectFactoryTest {
public void testCtorNullPackageException3() {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
+ // XXX: Don't suggest `ImmutableSet.of(elem)` for nullable `elem`.
+ @SuppressWarnings("ImmutableSetOf1")
final Object test = new PackageObjectFactory(Collections.singleton(null), classLoader,
TRY_IN_ALL_REGISTERED_PACKAGES);
assertWithMessage("Exception is expected but got " + test).fail();
--- a/src/test/java/com/puppycrawl/tools/checkstyle/TreeWalkerTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/TreeWalkerTest.java
@@ -79,6 +79,8 @@ import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
* @noinspectionreason ClassWithTooManyDependencies - complex tests require a
* large number of imports
*/
+// This class is referenced from another package.
+@SuppressWarnings("JUnitClassModifiers")
public class TreeWalkerTest extends AbstractModuleTestSupport {
@TempDir
--- a/src/test/java/com/puppycrawl/tools/checkstyle/utils/CheckUtilTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/utils/CheckUtilTest.java
@@ -47,6 +47,8 @@ import com.puppycrawl.tools.checkstyle.checks.coding.NestedIfDepthCheck;
import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocMethodCheck;
import com.puppycrawl.tools.checkstyle.checks.naming.AccessModifierOption;
+// This class is referenced from another package.
+@SuppressWarnings("JUnitClassModifiers")
public class CheckUtilTest extends AbstractModuleTestSupport {
@Override

View File

@@ -0,0 +1,183 @@
#!/usr/bin/env bash
set -e -u -o pipefail
integration_test_root="$(readlink -f "$(dirname "${0}")")"
error_prone_support_root="${integration_test_root}/.."
repos_root="${integration_test_root}/.repos"
test_name="$(basename "${0}" .sh)"
project=checkstyle
repository=https://github.com/checkstyle/checkstyle.git
revision=checkstyle-10.12.4
if [ "${#}" -gt 2 ] || ([ "${#}" = 2 ] && [ "${1:---sync}" != '--sync' ]); then
echo "Usage: ${0} [--sync] [<report_directory>]"
exit 1
fi
do_sync="$([ "${#}" = 0 ] || [ "${1:-}" != '--sync' ] || echo 1)"
report_directory="$([ "${#}" = 0 ] || ([ -z "${do_sync}" ] && echo "${1}") || ([ "${#}" = 1 ] || echo "${2}"))"
if [ -n "${report_directory}" ]; then
mkdir -p "${report_directory}"
else
report_directory="$(mktemp -d)"
trap 'rm -rf -- "${report_directory}"' INT TERM HUP EXIT
fi
# XXX: Configure Renovate to manage the AssertJ version declared here.
shared_build_flags="
-Perror-prone-compile,error-prone-test-compile
-Dassertj.version=3.24.2
-Derror-prone.version=$(
mvn -f "${error_prone_support_root}" help:evaluate -Dexpression=version.error-prone -q -DforceStdout
)
-Derror-prone-support.version=$(
mvn -f "${error_prone_support_root}" help:evaluate -Dexpression=project.version -q -DforceStdout
)
-DadditionalSourceDirectories=\${project.basedir}\${file.separator}src\${file.separator}it\${file.separator}java,\${project.basedir}\${file.separator}src\${file.separator}xdocs-examples\${file.separator}java
"
# XXX: Configure Renovate to manage the fmt-maven-plugin version declared here.
# XXX: Once GitHub actions uses Maven 3.9.2+, we can inline this variable with
# version reference `${fmt.version}`, and `-Dfmt.version=2.21.1` added to
# `shared_build_flags`.
format_goal='com.spotify.fmt:fmt-maven-plugin:2.21.1:format'
error_prone_shared_flags='-XepExcludedPaths:(\Q${project.basedir}${file.separator}src${file.separator}\E(it|test|xdocs-examples)\Q${file.separator}resources\E|\Q${project.build.directory}${file.separator}\E).*'
error_prone_patch_flags="${error_prone_shared_flags} -XepPatchLocation:IN_PLACE -XepPatchChecks:$(
find "${error_prone_support_root}" -path "*/META-INF/services/com.google.errorprone.bugpatterns.BugChecker" -print0 \
| xargs -0 grep -hoP '[^.]+$' \
| paste -s -d ','
)"
error_prone_validation_flags="${error_prone_shared_flags} -XepDisableAllChecks $(
find "${error_prone_support_root}" -path "*/META-INF/services/com.google.errorprone.bugpatterns.BugChecker" -print0 \
| xargs -0 grep -hoP '[^.]+$' \
| sed -r 's,(.*),-Xep:\1:WARN,' \
| paste -s -d ' '
)"
echo "Shared build flags: ${shared_build_flags}"
echo "Error Prone patch flags: ${error_prone_patch_flags}"
echo "Error Prone validation flags: ${error_prone_validation_flags}"
mkdir -p "${repos_root}"
# Make sure that the targeted tag of the project's Git repository is checked
# out.
project_root="${repos_root}/${project}"
if [ ! -d "${project_root}" ]; then
# The repository has not yet been cloned; create a shallow clone.
git clone --branch "${revision}" --depth 1 "${repository}" "${project_root}"
else
# The repository does already appear to exist. Try to check out the requested
# tag if possible, and fetch it otherwise.
#
# Under certain circumstances this does not cause the relevant tag to be
# created, so if necessary we manually create it.
git -C "${project_root}" checkout --force "${revision}" 2>/dev/null \
|| (
git -C "${project_root}" fetch --depth 1 "${repository}" "${revision}" \
&& git -C "${project_root}" checkout --force FETCH_HEAD \
&& (git -C "${project_root}" tag "${revision}" || true)
)
fi
pushd "${project_root}"
# Make sure that Git is sufficiently configured to enable committing to the
# project's Git repository.
git config user.email || git config user.email "integration-test@example.com"
git config user.name || git config user.name "Integration Test"
# Prepare the code for analysis by (a) applying the minimal set of changes
# required to run Error Prone with Error Prone Support and (b) formatting the
# code using the same method by which it will be formatted after each
# compilation round. The initial formatting operation ensures that subsequent
# modifications can be rendered in a clean manner.
git clean -fdx
git apply < "${integration_test_root}/${test_name}-init.patch"
git commit -m 'dependency: Introduce Error Prone Support' .
mvn ${shared_build_flags} "${format_goal}"
git commit -m 'minor: Reformat using Google Java Format' .
diff_base="$(git rev-parse HEAD)"
# Apply Error Prone Support-suggested changes until a fixed point is reached.
function apply_patch() {
local extra_build_args="${1}"
mvn ${shared_build_flags} ${extra_build_args} \
package "${format_goal}" \
-Derror-prone.flags="${error_prone_patch_flags}" \
-DskipTests
if ! git diff --exit-code; then
git commit -m 'minor: Apply patches' .
# Changes were applied, so another compilation round may apply yet more
# changes. For performance reasons we perform incremental compilation,
# enabled using a misleading flag. (See
# https://issues.apache.org/jira/browse/MCOMPILER-209 for details.)
apply_patch '-Dmaven.compiler.useIncrementalCompilation=false'
elif [ "${extra_build_args}" != 'clean' ]; then
# No changes were applied. We'll attempt one more round in which all files
# are recompiled, because there are cases in which violations are missed
# during incremental compilation.
apply_patch 'clean'
fi
}
apply_patch ''
# Run one more full build and log the output.
#
# By also running the tests, we validate that the (majority of) applied changes
# are behavior preserving. Some tests are skipped:
# - The `metadataFilesGenerationAllFiles` test is skipped because it makes line
# number assertions that will fail when the code is formatted or patched.
# - The `allCheckSectionJavaDocs` test is skipped because is validates that
# Javadoc has certain closing tags that are removed by Google Java Format.
validation_build_log="${report_directory}/${test_name}-validation-build-log.txt"
mvn ${shared_build_flags} \
clean package \
-Derror-prone.flags="${error_prone_validation_flags}" \
-Dtest='
!MetadataGeneratorUtilTest#metadataFilesGenerationAllFiles,
!XdocsJavaDocsTest#allCheckSectionJavaDocs' \
| tee "${validation_build_log}" \
|| failure=1
# Collect the applied changes.
expected_changes="${integration_test_root}/${test_name}-expected-changes.patch"
actual_changes="${report_directory}/${test_name}-changes.patch"
(git diff "${diff_base}"..HEAD | grep -vP '^(diff|index)' || true) > "${actual_changes}"
# Collect the warnings reported by Error Prone Support checks.
expected_warnings="${integration_test_root}/${test_name}-expected-warnings.txt"
actual_warnings="${report_directory}/${test_name}-validation-build-warnings.txt"
(grep -oP "(?<=^\\Q[WARNING] ${PWD}/\\E).*" "${validation_build_log}" | grep -P '\] \[' || true) | LC_ALL=C sort > "${actual_warnings}"
# Persist or validate the applied changes and reported warnings.
if [ -n "${do_sync}" ]; then
echo 'Saving changes...'
cp "${actual_changes}" "${expected_changes}"
cp "${actual_warnings}" "${expected_warnings}"
else
echo 'Inspecting changes...'
# XXX: This "diff of diffs" also contains vacuous sections, introduced due to
# line offset differences. Try to omit those from the final output.
if ! diff -u "${expected_changes}" "${actual_changes}"; then
echo 'There are unexpected changes.'
failure=1
fi
echo 'Inspecting emitted warnings...'
if ! diff -u "${expected_warnings}" "${actual_warnings}"; then
echo 'Diagnostics output changed.'
failure=1
fi
fi
if [ -n "${failure:-}" ]; then
exit 1
fi

187
pom.xml
View File

@@ -47,9 +47,9 @@
<module>refaster-test-support</module>
</modules>
<scm>
<scm child.scm.developerConnection.inherit.append.path="false" child.scm.url.inherit.append.path="false">
<developerConnection>scm:git:git@github.com:PicnicSupermarket/error-prone-support.git</developerConnection>
<tag>v0.12.0</tag>
<tag>HEAD</tag>
<url>https://github.com/PicnicSupermarket/error-prone-support</url>
</scm>
<issueManagement>
@@ -100,6 +100,10 @@
<!-- The test JVMs are short-running. By disabling certain
expensive JIT optimizations we actually speed up most tests. -->
-XX:TieredStopAtLevel=1
<!-- The test JVMs run in a non-interactive and generally
multi-core context, where the Parallel Garbage Collector generally
has the highest throughput. -->
-XX:+UseParallelGC
<!-- We cap memory usage. This may be relevant for build agents,
but also prevents excessive memory usage by heavily parallelized
local builds. -->
@@ -141,7 +145,7 @@
<groupId.error-prone>com.google.errorprone</groupId.error-prone>
<!-- The build timestamp is derived from the most recent commit
timestamp in support of reproducible builds. -->
<project.build.outputTimestamp>2023-10-04T14:40:37Z</project.build.outputTimestamp>
<project.build.outputTimestamp>${git.commit.time}</project.build.outputTimestamp>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Glob pattern identifying Refaster rule definition files. These
Java classes don't contain "regular" code, and thus require special
@@ -201,16 +205,16 @@
<version.auto-value>1.10.4</version.auto-value>
<version.error-prone>${version.error-prone-orig}</version.error-prone>
<version.error-prone-fork>v${version.error-prone-orig}-picnic-1</version.error-prone-fork>
<version.error-prone-orig>2.22.0</version.error-prone-orig>
<version.error-prone-slf4j>0.1.20</version.error-prone-slf4j>
<version.error-prone-orig>2.23.0</version.error-prone-orig>
<version.error-prone-slf4j>0.1.21</version.error-prone-slf4j>
<version.guava-beta-checker>1.0</version.guava-beta-checker>
<version.jdk>11</version.jdk>
<version.maven>3.8.7</version.maven>
<version.mockito>5.6.0</version.mockito>
<version.mockito>5.8.0</version.mockito>
<version.nopen-checker>1.0.1</version.nopen-checker>
<version.nullaway>0.10.14</version.nullaway>
<version.pitest-git>1.1.2</version.pitest-git>
<version.surefire>3.1.2</version.surefire>
<version.nullaway>0.10.18</version.nullaway>
<version.pitest-git>1.1.4</version.pitest-git>
<version.surefire>3.2.2</version.surefire>
</properties>
<dependencyManagement>
@@ -268,7 +272,7 @@
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.15.2</version>
<version>2.16.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -307,7 +311,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-bom</artifactId>
<version>32.1.2-jre</version>
<version>32.1.3-jre</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -333,7 +337,7 @@
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-bom</artifactId>
<version>2022.0.11</version>
<version>2023.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -345,12 +349,12 @@
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.11</version>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.2.16</version>
<version>2.2.19</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
@@ -382,7 +386,7 @@
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.14.9</version>
<version>1.14.10</version>
</dependency>
<!-- Specified so that Renovate will file Maven upgrade PRs, which
subsequently will cause `maven-enforcer-plugin` to require that
@@ -407,7 +411,7 @@
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>3.39.0</version>
<version>3.41.0</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
@@ -427,7 +431,7 @@
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.10.0</version>
<version>5.10.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -441,7 +445,7 @@
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-core</artifactId>
<version>4.10.2</version>
<version>4.11.1</version>
<!-- XXX: Drop this exclusion once we forgo enforcement of JDK
11 bytecode version compatibility. -->
<exclusions>
@@ -461,14 +465,14 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>5.3.30</version>
<version>5.3.31</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.7.16</version>
<version>2.7.18</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
@@ -529,6 +533,14 @@
<forkMode>never</forkMode>
<failOnError>false</failOnError>
</configuration>
<executions>
<execution>
<id>check-source-format</id>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>de.thetaphi</groupId>
@@ -580,7 +592,7 @@
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>6.0.0</version>
<version>7.0.0</version>
<configuration>
<injectAllReactorProjects>true</injectAllReactorProjects>
<runOnlyOnce>true</runOnlyOnce>
@@ -599,7 +611,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.0</version>
<version>3.3.1</version>
<configuration>
<checkstyleRules>
<!-- We only enable rules that are not enforced by
@@ -704,6 +716,10 @@
<!-- Instead, please use
`com.google.errorprone.annotations.Immutable`. -->
</property>
<property name="illegalClasses" value="org.jetbrains.annotations.CheckReturnValue">
<!-- Instead, please use
`com.google.errorprone.annotations.CheckReturnValue`. -->
</property>
<property name="illegalClasses" value="org.jetbrains.annotations.VisibleForTesting">
<!-- Instead, please use
`com.google.common.annotations.VisibleForTesting`. -->
@@ -810,6 +826,7 @@
<module name="RedundantModifier" />
<module name="SimplifyBooleanExpression" />
<module name="SimplifyBooleanReturn" />
<module name="SummaryJavadoc" />
<module name="SuppressWarningsHolder">
<property name="aliasList" value="com.puppycrawl.tools.checkstyle.checks.coding.UnusedLocalVariableCheck=unused" />
</module>
@@ -828,28 +845,21 @@
<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck" />
</module>
</checkstyleRules>
<excludeGeneratedSources>true</excludeGeneratedSources>
<failOnViolation>false</failOnViolation>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<!-- The plugin's "excludes" property accepts an Ant
pattern, but does not match against the full path. So
rather than explicitly excluding generated sources, we
have to explicitly include "original" sources. -->
<sourceDirectories>
<sourceDirectory>${project.build.sourceDirectory}</sourceDirectory>
</sourceDirectories>
<testSourceDirectories>
<testSourceDirectory>${project.build.testSourceDirectory}</testSourceDirectory>
<!-- Refaster input and output test files represent
valid Java code, exposed as resources on the test
classpath. -->
<testSourceDirectory>${basedir}/src/test/resources</testSourceDirectory>
</testSourceDirectories>
<resourceIncludes>**/*</resourceIncludes>
<!-- XXX: The generated
`target/generated-sources/license/THIRD-PARTY.txt`
files are not excluded by the `excludeGeneratedSources`
setting. Consider filing an issue for this. -->
<resourceExcludes>THIRD-PARTY.txt</resourceExcludes>
</configuration>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>10.12.4</version>
<version>10.12.5</version>
</dependency>
<dependency>
<groupId>io.spring.nohttp</groupId>
@@ -869,7 +879,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.3.1</version>
<version>3.3.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -950,7 +960,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.6.0</version>
<version>3.6.1</version>
<configuration>
<!-- XXX: Drop `ignoreAllNonTestScoped` once
https://issues.apache.org/jira/browse/MNG-6058 is
@@ -1033,18 +1043,7 @@
</requireMavenVersion>
<requireNoRepositories />
<requirePluginVersions />
<requireUpperBoundDeps>
<excludes>
<!-- XXX:
`com.google.errorprone:error_prone_test_helpers`
pulls in a more recent version of Truth
than
`com.google.testing.compile:compile-testing`,
but the latter is incompatible with said
never version. -->
<exclude>com.google.truth:truth</exclude>
</excludes>
</requireUpperBoundDeps>
<requireUpperBoundDeps />
</rules>
</configuration>
<dependencies>
@@ -1112,7 +1111,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.0</version>
<version>3.6.3</version>
<configuration>
<additionalJOptions>
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</additionalJOption>
@@ -1198,12 +1197,12 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.4.0</version>
<version>3.5.0</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>2.2.0</version>
<version>2.3.0</version>
<configuration>
<includedLicenses>
<!-- The SPDX IDs of licenses of third-party
@@ -1302,11 +1301,19 @@
<groupId>org.codehaus.mojo</groupId>
<artifactId>tidy-maven-plugin</artifactId>
<version>1.2.0</version>
<executions>
<execution>
<id>check-pom</id>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>2.16.1</version>
<version>2.16.2</version>
<configuration>
<updateBuildOutputTimestampPolicy>never</updateBuildOutputTimestampPolicy>
</configuration>
@@ -1345,7 +1352,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.10</version>
<version>0.8.11</version>
<configuration>
<excludes>
<!-- Refaster rules are tested using a custom method
@@ -1357,7 +1364,7 @@
<plugin>
<groupId>org.pitest</groupId>
<artifactId>pitest-maven</artifactId>
<version>1.15.1</version>
<version>1.15.3</version>
<configuration>
<excludedClasses>
<!-- AutoValue generated classes. -->
@@ -1386,7 +1393,7 @@
<dependency>
<groupId>com.groupcdg.arcmutate</groupId>
<artifactId>base</artifactId>
<version>1.2.1</version>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>com.groupcdg.pitest</groupId>
@@ -1396,7 +1403,7 @@
<dependency>
<groupId>org.pitest</groupId>
<artifactId>pitest-junit5-plugin</artifactId>
<version>1.2.0</version>
<version>1.2.1</version>
</dependency>
</dependencies>
<executions>
@@ -1538,7 +1545,7 @@
`BugChecker` test code and the Refaster test files) does not
exhibit anti-patterns other than those associated with the
check/rule under test. Ideally all test cases are realistic. -->
<error-prone.self-check-args>-XepAllSuggestionsAsWarnings -Xep:MethodReferenceUsage:OFF</error-prone.self-check-args>
<error-prone.self-check-args>-Xep:MethodReferenceUsage:OFF</error-prone.self-check-args>
</properties>
<build>
<plugins>
@@ -1563,6 +1570,31 @@
</plugins>
</build>
</profile>
<profile>
<!-- A counterpart to the `disallow-warnings` profile which
explicitly "tones down" plugins enabled by the `build-checks`
profile. Necessary for dealing with plugins that default to failing
the build upon encountering a violation. -->
<id>avoid-errors</id>
<activation>
<property>
<name>verification.warn</name>
</property>
</activation>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tidy-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
<profile>
<!-- By default, we verify various aspects of a module and the
artifact(s) it produces. We define these checks in a profile so
@@ -1583,14 +1615,6 @@
<plugin>
<groupId>com.spotify.fmt</groupId>
<artifactId>fmt-maven-plugin</artifactId>
<executions>
<execution>
<id>check-source-format</id>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>de.thetaphi</groupId>
@@ -1669,6 +1693,10 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tidy-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.gaul</groupId>
<artifactId>modernizer-maven-plugin</artifactId>
@@ -1703,15 +1731,17 @@
failing the build on the first error
encountered. -->
-XepAllErrorsAsWarnings
-XepAllSuggestionsAsWarnings
<!-- We want to enable almost all Error
Prone bug pattern checkers, so we enable
all and then selectively deactivate some. -->
-XepAllDisabledChecksAsWarnings
<!-- Some generated classes violate Error
Prone bug patterns. We cannot in all cases
avoid that, so we simply tell Error Prone
not to warn about generated code. -->
-XepDisableWarningsInGeneratedCode
<!-- We want to enable almost all Error
Prone bug pattern checkers, so we enable
all and then selectively deactivate some. -->
-XepAllDisabledChecksAsWarnings
-XepExcludedPaths:\Q${project.build.directory}${file.separator}\E.*
<!-- We don't target Android. -->
-Xep:AndroidJdkLibsChecker:OFF
<!-- XXX: Enable this once we open-source
@@ -1738,11 +1768,8 @@
-XepOpt:NullAway:CheckOptionalEmptiness=true
-XepOpt:Nullness:Conservative=false
-XepOpt:StatementSwitchToExpressionSwitch:EnableAssignmentSwitchConversion=true
-XepOpt:StatementSwitchToExpressionSwitch:EnableDirectConversion=true
-XepOpt:StatementSwitchToExpressionSwitch:EnableReturnSwitchConversion=true
<!-- XXX: Enable once this check respects
the compilation source version. See
https://github.com/google/error-prone/pull/3646.
-XepOpt:StatementSwitchToExpressionSwitch:EnableDirectConversion=true -->
<!-- Append additional custom arguments. -->
${error-prone.patch-args}
${error-prone.self-check-args}
@@ -1775,10 +1802,10 @@
</properties>
</profile>
<profile>
<!-- The `build-checks` and `error-prone` profiles enable a bunch
of additional compile checks. By default, those warnings break the
build. This profile allows one to collect all build warnings
without failing the build. -->
<!-- The `build-checks` and `error-prone` profiles enable numerous
additional validations. By default, any violations break the build.
This profile allows one to collect all violations without failing
the build. -->
<id>disallow-warnings</id>
<activation>
<property>

View File

@@ -55,6 +55,11 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>

View File

@@ -0,0 +1,151 @@
package tech.picnic.errorprone.refaster.matchers;
import static com.google.common.base.Verify.verify;
import static com.google.errorprone.matchers.FieldMatchers.staticField;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.argumentCount;
import static com.google.errorprone.matchers.Matchers.isPrimitiveType;
import static com.google.errorprone.matchers.Matchers.isSameType;
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
import static com.google.errorprone.matchers.Matchers.staticMethod;
import static com.google.errorprone.matchers.Matchers.toType;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
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.MethodInvocationTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.Tree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
* A matcher of expressions that are guaranteed to yield "empty" instances, as defined by their
* respective types.
*/
// XXX: Also match (effectively) final variables that reference provably-empty objects.
// XXX: Also handle `#copyOf(someEmptyInstance)`, `#sortedCopyOf(someEmptyInstance)`,
// `Sets.immutableEnumSet(emptyIterable)` (and other `Sets` methods), `EnumSet.noneOf(...)`,
// `emptyCollection.stream()`, `emptyStream.collect(...)`, `emptyMap.{keySet,values,entrySet}()`,
// etc.
// XXX: Also recognize null-hostile "container" expression types that can only reference empty
// instances, such as `ImmutableCollection<Void>` and `Flux<Void>`.
// XXX: Also recognize empty instances of `Optional`, `OptionalInt`, `OptionalLong`, and
// `OptionalDouble`.
// XXX: Also recognize empty builders and `emptyBuilder.build()` invocations.
public final class IsEmpty implements Matcher<ExpressionTree> {
private static final long serialVersionUID = 1L;
private static final Pattern EMPTY_INSTANCE_FACTORY_METHOD_PATTERN = Pattern.compile("empty.*");
private static final Matcher<Tree> EMPTY_COLLECTION_CONSTRUCTOR_ARGUMENT =
anyOf(isPrimitiveType(), isSubtypeOf(Comparator.class));
// XXX: Extend this list to include additional JDK collection types with a public constructor.
private static final Matcher<ExpressionTree> MUTABLE_COLLECTION_TYPE =
anyOf(
isSameType(ArrayList.class),
isSameType(HashMap.class),
isSameType(HashSet.class),
isSameType(LinkedHashMap.class),
isSameType(LinkedHashSet.class),
isSameType(LinkedList.class),
isSameType(Stack.class),
isSameType(TreeMap.class),
isSameType(TreeSet.class),
isSameType(Vector.class));
private static final Matcher<ExpressionTree> EMPTY_INSTANCE_FACTORY =
anyOf(
staticField(Collections.class.getName(), "EMPTY_LIST"),
staticField(Collections.class.getName(), "EMPTY_MAP"),
staticField(Collections.class.getName(), "EMPTY_SET"),
toType(
MethodInvocationTree.class,
allOf(
argumentCount(0),
anyOf(
staticMethod()
.onClass(Collections.class.getName())
.withNameMatching(EMPTY_INSTANCE_FACTORY_METHOD_PATTERN),
staticMethod()
.onDescendantOfAny(
ImmutableCollection.class.getName(),
ImmutableMap.class.getName(),
ImmutableMultimap.class.getName(),
List.class.getName(),
Map.class.getName(),
Set.class.getName(),
Stream.class.getName())
.named("of"),
staticMethod()
.onClassAny(
Stream.class.getName(),
"reactor.core.publisher.Flux",
"reactor.core.publisher.Mono",
"reactor.util.context.Context")
.named("empty"),
staticMethod()
.onDescendantOf("reactor.core.publisher.Flux")
.named("just")))));
/** Instantiates a new {@link IsEmpty} instance. */
public IsEmpty() {}
@Override
public boolean matches(ExpressionTree tree, VisitorState state) {
return isEmptyArrayCreation(tree)
|| EMPTY_INSTANCE_FACTORY.matches(tree, state)
|| isEmptyCollectionConstructor(tree, state);
}
private boolean isEmptyCollectionConstructor(ExpressionTree tree, VisitorState state) {
if (!(tree instanceof NewClassTree) || !MUTABLE_COLLECTION_TYPE.matches(tree, state)) {
return false;
}
List<? extends ExpressionTree> arguments = ((NewClassTree) tree).getArguments();
if (arguments.stream().allMatch(a -> EMPTY_COLLECTION_CONSTRUCTOR_ARGUMENT.matches(a, state))) {
/*
* This is a default constructor, or a constructor that creates an empty collection using
* custom (re)size/load factor parameters and/or a custom `Comparator`.
*/
return true;
}
/*
* This looks like a copy constructor, in which case the resultant collection is empty if its
* argument is.
*/
verify(arguments.size() == 1, "Unexpected %s-ary constructor", arguments.size());
return matches(arguments.get(0), state);
}
private static boolean isEmptyArrayCreation(ExpressionTree tree) {
if (!(tree instanceof NewArrayTree)) {
return false;
}
NewArrayTree newArray = (NewArrayTree) tree;
return (!newArray.getDimensions().isEmpty()
&& ASTHelpers.constValue(newArray.getDimensions().get(0), Integer.class) == 0)
|| (newArray.getInitializers() != null && newArray.getInitializers().isEmpty());
}
}

View File

@@ -0,0 +1,54 @@
package tech.picnic.errorprone.refaster.matchers;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.staticMethod;
import static com.google.errorprone.matchers.Matchers.toType;
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.LambdaExpressionTree.BodyKind;
import java.util.function.DoubleUnaryOperator;
import java.util.function.Function;
import java.util.function.IntUnaryOperator;
import java.util.function.LongUnaryOperator;
/** A matcher of expressions that represent identity operations. */
// XXX: In selected contexts many other method invocations can be considered identity operations;
// see the `IdentityConversion` check. Review whether those can/should be captured by this matcher
// as well.
public final class IsIdentityOperation implements Matcher<ExpressionTree> {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> DELEGATE =
anyOf(
staticMethod()
.onDescendantOfAny(
DoubleUnaryOperator.class.getName(),
Function.class.getName(),
IntUnaryOperator.class.getName(),
LongUnaryOperator.class.getName())
.named("identity"),
isIdentityLambdaExpression());
/** Instantiates a new {@link IsIdentityOperation} instance. */
public IsIdentityOperation() {}
@Override
public boolean matches(ExpressionTree tree, VisitorState state) {
return DELEGATE.matches(tree, state);
}
// XXX: Also support selected block expressions, including ones that perform a vacuous parameter
// transformation such as those identified by the `IdentityConversion` check.
private static Matcher<ExpressionTree> isIdentityLambdaExpression() {
return toType(
LambdaExpressionTree.class,
(tree, state) ->
tree.getBodyKind() == BodyKind.EXPRESSION
&& tree.getParameters().size() == 1
&& ASTHelpers.getSymbol(tree.getParameters().get(0))
.equals(ASTHelpers.getSymbol(tree.getBody())));
}
}

View File

@@ -13,7 +13,7 @@ import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreeScanner;
import com.sun.source.util.TreePathScanner;
import org.jspecify.annotations.Nullable;
/**
@@ -34,15 +34,11 @@ abstract class AbstractMatcherTestChecker extends BugChecker implements Compilat
@Override
public Description matchCompilationUnit(CompilationUnitTree compilationUnit, VisitorState state) {
new TreeScanner<@Nullable Void, TreePath>() {
new TreePathScanner<@Nullable Void, @Nullable Void>() {
@Override
public @Nullable Void scan(@Nullable Tree tree, TreePath treePath) {
if (tree == null) {
return null;
}
TreePath path = new TreePath(treePath, tree);
public @Nullable Void scan(@Nullable Tree tree, @Nullable Void unused) {
if (tree instanceof ExpressionTree) {
TreePath path = new TreePath(getCurrentPath(), tree);
ExpressionTree expressionTree = (ExpressionTree) tree;
if (!isMethodSelect(expressionTree, path)
&& delegate.matches(expressionTree, state.withPath(path))) {
@@ -50,11 +46,11 @@ abstract class AbstractMatcherTestChecker extends BugChecker implements Compilat
}
}
return super.scan(tree, path);
return super.scan(tree, null);
}
@Override
public @Nullable Void visitImport(ImportTree tree, TreePath path) {
public @Nullable Void visitImport(ImportTree tree, @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.
@@ -63,24 +59,24 @@ abstract class AbstractMatcherTestChecker extends BugChecker implements Compilat
}
@Override
public @Nullable Void visitMethod(MethodTree tree, TreePath path) {
public @Nullable Void visitMethod(MethodTree tree, @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(tree.getBody(), new TreePath(path, tree));
return scan(tree.getBody(), null);
}
@Override
public @Nullable Void visitTypeCast(TypeCastTree tree, TreePath path) {
public @Nullable Void visitTypeCast(TypeCastTree tree, @Nullable Void unused) {
/*
* We're not interested in matching the parenthesized type subtree that is part of a type
* cast expression. While such trees can be `ExpressionTree`s, they will never be matched by
* Refaster.
*/
return scan(tree.getExpression(), new TreePath(path, tree));
return scan(tree.getExpression(), null);
}
}.scan(compilationUnit, state.getPath());
}.scan(compilationUnit, null);
return Description.NO_MATCH;
}

View File

@@ -0,0 +1,443 @@
package tech.picnic.errorprone.refaster.matchers;
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 IsEmptyTest {
@Test
void matches() {
CompilationTestHelper.newInstance(MatcherTestChecker.class, getClass())
.addSourceLines(
"A.java",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableMap;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.common.collect.ImmutableSetMultimap;",
"import java.util.ArrayList;",
"import java.util.Collections;",
"import java.util.Comparator;",
"import java.util.HashMap;",
"import java.util.HashSet;",
"import java.util.LinkedHashMap;",
"import java.util.LinkedHashSet;",
"import java.util.LinkedList;",
"import java.util.List;",
"import java.util.Map;",
"import java.util.Random;",
"import java.util.Set;",
"import java.util.Stack;",
"import java.util.TreeMap;",
"import java.util.TreeSet;",
"import java.util.Vector;",
"import java.util.stream.Stream;",
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.Mono;",
"import reactor.util.context.Context;",
"",
"class A {",
" int[] negative1() {",
" return new int[1];",
" }",
"",
" int[][] negative2() {",
" return new int[1][0];",
" }",
"",
" int[] negative3() {",
" return new int[] {0};",
" }",
"",
" int[][] negative4() {",
" return new int[][] {{0}};",
" }",
"",
" Random negative5() {",
" return new Random();",
" }",
"",
" List<Integer> negative6() {",
" return new ArrayList<>(ImmutableList.of(1));",
" }",
"",
" Map<Integer, Integer> negative7() {",
" return new HashMap<>(ImmutableMap.of(1, 2));",
" }",
"",
" Set<Integer> negative8() {",
" return new HashSet<>(ImmutableList.of(1));",
" }",
"",
" Map<Integer, Integer> negative9() {",
" return new LinkedHashMap<>(ImmutableMap.of(1, 2));",
" }",
"",
" Set<Integer> negative10() {",
" return new LinkedHashSet<>(ImmutableList.of(1));",
" }",
"",
" List<Integer> negative11() {",
" return new LinkedList<>(ImmutableList.of(1));",
" }",
"",
" Map<Integer, Integer> negative12() {",
" return new HashMap<>(ImmutableMap.of(1, 2));",
" }",
"",
" Set<Integer> negative13() {",
" return new HashSet<>(ImmutableList.of(1));",
" }",
"",
" List<Integer> negative14() {",
" return new Vector<>(ImmutableList.of(1));",
" }",
"",
" ImmutableList<Integer> negative15() {",
" return ImmutableList.of(1);",
" }",
"",
" ImmutableSet<Integer> negative16() {",
" return ImmutableSet.of(1);",
" }",
"",
" ImmutableMap<Integer, Integer> negative17() {",
" return ImmutableMap.of(1, 2);",
" }",
"",
" ImmutableSetMultimap<Integer, Integer> negative18() {",
" return ImmutableSetMultimap.of(1, 2);",
" }",
"",
" List<Integer> negative19() {",
" return List.of(1);",
" }",
"",
" Map<Integer, Integer> negative20() {",
" return Map.of(1, 2);",
" }",
"",
" Set<Integer> negative21() {",
" return Set.of(1);",
" }",
"",
" Stream<Integer> negative22() {",
" return Stream.of(1);",
" }",
"",
" int[] positive1() {",
" // BUG: Diagnostic contains:",
" return new int[0];",
" }",
"",
" int[][] positive2() {",
" // BUG: Diagnostic contains:",
" return new int[0][1];",
" }",
"",
" int[] positive3() {",
" // BUG: Diagnostic contains:",
" return new int[] {};",
" }",
"",
" int[][] positive4() {",
" // BUG: Diagnostic contains:",
" return new int[][] {};",
" }",
"",
" List<Integer> positive5() {",
" // BUG: Diagnostic contains:",
" return new ArrayList<>();",
" }",
"",
" List<String> positive6() {",
" // BUG: Diagnostic contains:",
" return new ArrayList<>(1);",
" }",
"",
" List<String> positive7() {",
" // BUG: Diagnostic contains:",
" return new ArrayList<>(",
" // BUG: Diagnostic contains:",
" ImmutableList.of());",
" }",
"",
" Map<String, String> positive8() {",
" // BUG: Diagnostic contains:",
" return new HashMap<>();",
" }",
"",
" Map<String, String> positive9() {",
" // BUG: Diagnostic contains:",
" return new HashMap<>(1);",
" }",
"",
" Map<String, String> positive10() {",
" // BUG: Diagnostic contains:",
" return new HashMap<>(1, 1.0F);",
" }",
"",
" Map<String, String> positive11() {",
" // BUG: Diagnostic contains:",
" return new HashMap<>(",
" // BUG: Diagnostic contains:",
" ImmutableMap.of());",
" }",
"",
" Set<Integer> positive12() {",
" // BUG: Diagnostic contains:",
" return new HashSet<>();",
" }",
"",
" Set<Integer> positive13() {",
" // BUG: Diagnostic contains:",
" return new HashSet<>(1);",
" }",
"",
" Set<Integer> positive14() {",
" // BUG: Diagnostic contains:",
" return new HashSet<>(1, 1.0F);",
" }",
"",
" Set<Integer> positive15() {",
" // BUG: Diagnostic contains:",
" return new HashSet<>(",
" // BUG: Diagnostic contains:",
" ImmutableList.of());",
" }",
"",
" Map<String, String> positive16() {",
" // BUG: Diagnostic contains:",
" return new LinkedHashMap<>();",
" }",
"",
" Map<String, String> positive17() {",
" // BUG: Diagnostic contains:",
" return new LinkedHashMap<>(1);",
" }",
"",
" Map<String, String> positive18() {",
" // BUG: Diagnostic contains:",
" return new LinkedHashMap<>(1, 1.0F);",
" }",
"",
" Map<String, String> positive19() {",
" // BUG: Diagnostic contains:",
" return new LinkedHashMap<>(1, 1.0F, false);",
" }",
"",
" Map<String, String> positive20() {",
" // BUG: Diagnostic contains:",
" return new LinkedHashMap<>(",
" // BUG: Diagnostic contains:",
" ImmutableMap.of());",
" }",
"",
" Set<Integer> positive21() {",
" // BUG: Diagnostic contains:",
" return new LinkedHashSet<>();",
" }",
"",
" Set<Integer> positive22() {",
" // BUG: Diagnostic contains:",
" return new LinkedHashSet<>(1);",
" }",
"",
" Set<Integer> positive23() {",
" // BUG: Diagnostic contains:",
" return new LinkedHashSet<>(1, 1.0F);",
" }",
"",
" Set<Integer> positive24() {",
" // BUG: Diagnostic contains:",
" return new LinkedHashSet<>(",
" // BUG: Diagnostic contains:",
" ImmutableList.of());",
" }",
"",
" List<Integer> positive25() {",
" // BUG: Diagnostic contains:",
" return new LinkedList<>();",
" }",
"",
" List<String> positive26() {",
" // BUG: Diagnostic contains:",
" return new LinkedList<>(",
" // BUG: Diagnostic contains:",
" ImmutableList.of());",
" }",
"",
" List<Integer> positive27() {",
" // BUG: Diagnostic contains:",
" return new Stack<>();",
" }",
"",
" Map<String, String> positive28() {",
" // BUG: Diagnostic contains:",
" return new TreeMap<>();",
" }",
"",
" Map<String, String> positive29() {",
" // BUG: Diagnostic contains:",
" return new TreeMap<>(Comparator.naturalOrder());",
" }",
"",
" Map<String, String> positive30() {",
" // BUG: Diagnostic contains:",
" return new TreeMap<>(",
" // BUG: Diagnostic contains:",
" ImmutableMap.of());",
" }",
"",
" Set<Integer> positive31() {",
" // BUG: Diagnostic contains:",
" return new TreeSet<>();",
" }",
"",
" Set<Integer> positive32() {",
" // BUG: Diagnostic contains:",
" return new TreeSet<>(Comparator.naturalOrder());",
" }",
"",
" Set<Integer> positive33() {",
" // BUG: Diagnostic contains:",
" return new TreeSet<>(",
" // BUG: Diagnostic contains:",
" ImmutableList.of());",
" }",
"",
" List<Integer> positive34() {",
" // BUG: Diagnostic contains:",
" return new Vector<>();",
" }",
"",
" List<Integer> positive35() {",
" // BUG: Diagnostic contains:",
" return new Vector<>(1);",
" }",
"",
" List<Integer> positive36() {",
" // BUG: Diagnostic contains:",
" return new Vector<>(1, 2);",
" }",
"",
" List<Integer> positive37() {",
" // BUG: Diagnostic contains:",
" return new Vector<>(",
" // BUG: Diagnostic contains:",
" ImmutableList.of());",
" }",
"",
" List<Integer> positive38() {",
" // BUG: Diagnostic contains:",
" return Collections.EMPTY_LIST;",
" }",
"",
" Map<String, String> positive39() {",
" // BUG: Diagnostic contains:",
" return Collections.EMPTY_MAP;",
" }",
"",
" Set<Integer> positive40() {",
" // BUG: Diagnostic contains:",
" return Collections.EMPTY_SET;",
" }",
"",
" List<Integer> positive41() {",
" // BUG: Diagnostic contains:",
" return Collections.emptyList();",
" }",
"",
" Map<String, String> positive42() {",
" // BUG: Diagnostic contains:",
" return Collections.emptyMap();",
" }",
"",
" Set<Integer> positive43() {",
" // BUG: Diagnostic contains:",
" return Collections.emptySet();",
" }",
"",
" ImmutableList<Integer> positive44() {",
" // BUG: Diagnostic contains:",
" return ImmutableList.of();",
" }",
"",
" ImmutableSet<Integer> positive45() {",
" // BUG: Diagnostic contains:",
" return ImmutableSet.of();",
" }",
"",
" ImmutableMap<String, Integer> positive46() {",
" // BUG: Diagnostic contains:",
" return ImmutableMap.of();",
" }",
"",
" ImmutableSetMultimap<String, Integer> positive47() {",
" // BUG: Diagnostic contains:",
" return ImmutableSetMultimap.of();",
" }",
"",
" List<Integer> positive48() {",
" // BUG: Diagnostic contains:",
" return List.of();",
" }",
"",
" Map<String, String> positive49() {",
" // BUG: Diagnostic contains:",
" return Map.of();",
" }",
"",
" Set<Integer> positive50() {",
" // BUG: Diagnostic contains:",
" return Set.of();",
" }",
"",
" Stream<Integer> positive51() {",
" // BUG: Diagnostic contains:",
" return Stream.of();",
" }",
"",
" Stream<Integer> positive52() {",
" // BUG: Diagnostic contains:",
" return Stream.empty();",
" }",
"",
" Flux<Integer> positive53() {",
" // BUG: Diagnostic contains:",
" return Flux.empty();",
" }",
"",
" Mono<Integer> positive54() {",
" // BUG: Diagnostic contains:",
" return Mono.empty();",
" }",
"",
" Context positive55() {",
" // BUG: Diagnostic contains:",
" return Context.empty();",
" }",
"",
" Flux<Integer> positive56() {",
" // BUG: Diagnostic contains:",
" return Flux.just();",
" }",
"}")
.doTest();
}
/** A {@link BugChecker} that simply delegates to {@link IsEmpty}. */
@BugPattern(summary = "Flags expressions matched by `IsEmpty`", 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 IsEmpty());
}
}
}

View File

@@ -0,0 +1,77 @@
package tech.picnic.errorprone.refaster.matchers;
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 IsIdentityOperationTest {
@Test
void matches() {
CompilationTestHelper.newInstance(MatcherTestChecker.class, getClass())
.addSourceLines(
"A.java",
"import java.util.function.BinaryOperator;",
"import java.util.function.DoubleUnaryOperator;",
"import java.util.function.Function;",
"import java.util.function.IntUnaryOperator;",
"import java.util.function.LongUnaryOperator;",
"import java.util.function.UnaryOperator;",
"",
"class A {",
" BinaryOperator<String> negative1() {",
" return (a, b) -> a;",
" }",
"",
" UnaryOperator<String> negative2() {",
" return a -> a + a;",
" }",
"",
" DoubleUnaryOperator positive1() {",
" // BUG: Diagnostic contains:",
" return DoubleUnaryOperator.identity();",
" }",
"",
" Function<Integer, Integer> positive2() {",
" // BUG: Diagnostic contains:",
" return Function.identity();",
" }",
"",
" UnaryOperator<String> positive3() {",
" // BUG: Diagnostic contains:",
" return UnaryOperator.identity();",
" }",
"",
" IntUnaryOperator positive4() {",
" // BUG: Diagnostic contains:",
" return IntUnaryOperator.identity();",
" }",
"",
" LongUnaryOperator positive5() {",
" // BUG: Diagnostic contains:",
" return LongUnaryOperator.identity();",
" }",
"",
" UnaryOperator positive6() {",
" // BUG: Diagnostic contains:",
" return a -> a;",
" }",
"}")
.doTest();
}
/** A {@link BugChecker} that simply delegates to {@link IsIdentityOperation}. */
@BugPattern(summary = "Flags expressions matched by `IsIdentityOperation`", 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 IsIdentityOperation());
}
}
}

View File

@@ -4,12 +4,10 @@ import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableListMultimap.toImmutableListMultimap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
import static com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static java.util.Comparator.naturalOrder;
import static tech.picnic.errorprone.refaster.runner.Refaster.INCLUDED_RULES_PATTERN_FLAG;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableRangeMap;
@@ -19,6 +17,7 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Range;
import com.google.common.collect.Sets;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.BugPattern;
import com.google.errorprone.ErrorProneFlags;
import com.google.errorprone.SubContext;
@@ -123,10 +122,12 @@ public final class RefasterRuleCollection extends BugChecker implements Compilat
String className = clazz.getSimpleName();
BugCheckerRefactoringTestHelper.newInstance(RefasterRuleCollection.class, clazz)
.setArgs(ImmutableList.of("-XepOpt:" + RULE_COLLECTION_FLAG + '=' + className))
.setArgs(
"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
"-XepOpt:" + RULE_COLLECTION_FLAG + '=' + className)
.addInput(className + "TestInput.java")
.addOutput(className + "TestOutput.java")
.doTest(TEXT_MATCH);
.doTest(TestMode.TEXT_MATCH);
}
@Override

View File

@@ -51,11 +51,11 @@ bundle install
# Deployment
The website is regenerated and deployed using the
[`deploy-website.yaml`][error-prone-support-website-deploy-workflow] GitHub
[`deploy-website.yml`][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
[error-prone-support-website-deploy-workflow]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/deploy-website.yml
[jekyll]: https://jekyllrb.com
[jekyll-docs]: https://jekyllrb.com/docs
[jekyll-docs-installation]: https://jekyllrb.com/docs/installation