Compare commits

...

194 Commits

Author SHA1 Message Date
Stephan Schroevers
390d84d3dd WIP 2023-09-02 19:06:34 +02:00
Stephan Schroevers
87f79dae60 Introduce additional equality Refaster rules (#765) 2023-09-01 15:17:33 +02:00
Stephan Schroevers
1f4a68416f Introduce assorted Optional Refaster rules (#764)
While there, move the `OptionalOrElseGet` Refaster rule test code to the
correct location.
2023-09-01 11:42:13 +02:00
Picnic-Bot
c98fe9273d Upgrade Pitest Git plugins 1.1.1 -> 1.1.2 (#772) 2023-09-01 09:11:09 +02:00
Stephan Schroevers
8b5d1803e4 Introduce CollectionContains Refaster rule (#766) 2023-08-31 15:46:43 +02:00
Stephan Schroevers
507a19d10e Introduce FileRules Refaster rule collection (#767) 2023-08-31 08:58:11 +02:00
Picnic-Bot
43fcbb770d Upgrade Checkstyle 10.12.2 -> 10.12.3 (#769)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.12.3
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.12.2...checkstyle-10.12.3
2023-08-30 10:13:01 +02:00
Stephan Schroevers
7779631320 Introduce StreamIterate Refaster rule (#762) 2023-08-29 09:08:43 +02:00
Stephan Schroevers
48e2f57cb6 [maven-release-plugin] prepare for next development iteration 2023-08-28 19:22:11 +03:00
Stephan Schroevers
352829c61f [maven-release-plugin] prepare release v0.13.0 2023-08-28 19:22:11 +03:00
Picnic-Bot
37d95605b0 Upgrade Byte Buddy 1.14.6 -> 1.14.7 (#768)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.14.7
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.6...byte-buddy-1.14.7
2023-08-28 10:06:30 +02:00
Stephan Schroevers
6131599d9d Introduce mutation test script for changed code (#757)
While there, optimize the existing `run-mutation-tests.sh` script.
2023-08-28 09:43:06 +02:00
Picnic-Bot
9c158f11d4 Upgrade maven-enforcer-plugin 3.3.0 -> 3.4.0 (#759)
See:
- https://github.com/apache/maven-enforcer/releases/tag/enforcer-3.4.0
- https://github.com/apache/maven-enforcer/compare/enforcer-3.3.0...enforcer-3.4.0
2023-08-25 22:23:19 +02:00
Picnic-Bot
be818486bd Upgrade Mockito 5.4.0 -> 5.5.0 (#758)
See:
- https://github.com/mockito/mockito/releases/tag/v5.5.0
- https://github.com/mockito/mockito/compare/v5.4.0...v5.5.0
2023-08-25 22:05:51 +02:00
Picnic-Bot
1e80097bee Upgrade Spring Boot 2.7.14 -> 2.7.15 (#760)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v2.7.15
- https://github.com/spring-projects/spring-boot/compare/v2.7.14...v2.7.15
2023-08-25 21:14:26 +02:00
Picnic-Bot
431c3e67ac Upgrade actions/checkout v3.5.3 -> v3.6.0 (#761)
See:
- https://github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v360
2023-08-25 09:34:35 +02:00
Picnic-Bot
e612631cb8 Upgrade AspectJ 1.9.19 -> 1.9.20 (#755)
See:
- https://github.com/eclipse-aspectj/aspectj/releases/tag/V1_9_20
- https://github.com/eclipse/org.aspectj/compare/V1_9_19...V1_9_20
2023-08-17 21:34:51 +02:00
Picnic-Bot
a6bce960cb Upgrade AutoValue 1.10.2 -> 1.10.3 (#752)
See:
- https://github.com/google/auto/releases/tag/auto-value-1.10.3
- https://github.com/google/auto/compare/auto-value-1.10.2...auto-value-1.10.3
2023-08-17 17:12:33 +02:00
Picnic-Bot
e19c2de99d Upgrade pitest-maven-plugin 1.14.3 -> 1.14.4 (#756)
See:
- https://github.com/hcoles/pitest/releases/tag/1.14.4
- https://github.com/hcoles/pitest/compare/1.14.3...1.14.4
2023-08-17 10:36:59 +02:00
Picnic-Bot
7b193f2fd5 Upgrade Byte Buddy 1.14.5 -> 1.14.6 (#753)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.14.6
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.5...byte-buddy-1.14.6
2023-08-16 09:24:45 +02:00
Picnic-Bot
852442b55b Upgrade Project Reactor 2022.0.9 -> 2022.0.10 (#754)
See:
- https://github.com/reactor/reactor/releases/tag/2022.0.10
- https://github.com/reactor/reactor/compare/2022.0.9...2022.0.10
2023-08-16 09:02:16 +02:00
Rick Ossendrijver
d758fab524 Have Renovate file GitHub CodeQL upgrade PRs once every four weeks (#745)
While there, match on the `depName` of `ruby/setup-ruby`.
2023-08-15 14:08:52 +02:00
Picnic-Bot
88ed3ea9a9 Upgrade CodeQL v2.21.3 -> v2.21.4 (#751)
See:
- https://github.com/github/codeql-action/blob/main/CHANGELOG.md
- https://github.com/github/codeql-action/compare/v2.21.3...v2.21.4
2023-08-15 11:58:32 +02:00
Giovanni Zotta
7fb0e551d4 Introduce VerifyOnlyElementInFlux Refaster rule (#617) 2023-08-14 14:05:14 +02:00
Mohamed Sameh
4af7b21bf5 Introduce NestedPublishers check (#642)
And introduce a few utility methods that simplify `Tree`- and `Type`-based
matching, allowing the existing (and similar) `NestedOptionals` check to be
simplified as well.
2023-08-14 10:30:45 +02:00
Picnic-Bot
fc5f3bd4cc Upgrade Pitest Git plugins 1.1.0 -> 1.1.1 (#750) 2023-08-14 08:52:14 +02:00
Mohamed Sameh
35a5944b81 Introduce {Flux,{Double,Int,Long,}Stream}TakeWhile Refaster rules (#727) 2023-08-12 15:36:43 +02:00
Picnic-Bot
17cc8e0c31 Upgrade CodeQL v2.21.2 -> v2.21.3 (#746)
See:
- https://github.com/github/codeql-action/blob/main/CHANGELOG.md
- https://github.com/github/codeql-action/compare/v2.21.2...v2.21.3
2023-08-12 14:39:33 +02:00
Picnic-Bot
b5ba4fa208 Upgrade actions/deploy-pages v2.0.3 -> v2.0.4 (#747)
See:
- https://github.com/actions/deploy-pages/releases/tag/v2.0.4
2023-08-12 14:28:35 +02:00
Picnic-Bot
26b941bbc5 Upgrade Guava 32.1.1-jre -> 32.1.2-jre (#735)
See:
- https://github.com/google/guava/releases/tag/v32.1.2
- https://github.com/google/guava/compare/v32.1.1...v32.1.2
2023-08-12 14:18:05 +02:00
Picnic-Bot
eb3b540537 Upgrade pitest-maven-plugin 1.14.2 -> 1.14.3 (#748)
See:
- https://github.com/hcoles/pitest/releases/tag/1.14.3
- https://github.com/hcoles/pitest/compare/1.14.2...1.14.3
2023-08-12 14:05:50 +02:00
Picnic-Bot
6a11b59517 Upgrade CodeQL v2.20.3 -> v2.21.2 (#722)
See:
- https://github.com/github/codeql-action/blob/main/CHANGELOG.md
- https://github.com/github/codeql-action/compare/v2.20.3...v2.21.2
2023-08-07 11:47:08 +02:00
Picnic-Bot
62abd51f4d Upgrade errorprone-slf4j 0.1.18 -> 0.1.20 (#741)
See:
- https://github.com/KengoTODA/errorprone-slf4j/releases/tag/v0.1.19
- https://github.com/KengoTODA/errorprone-slf4j/releases/tag/v0.1.20
- https://github.com/KengoTODA/errorprone-slf4j/compare/v0.1.18...v0.1.20
2023-08-07 07:24:39 +02:00
Rick Ossendrijver
67a128a9b6 Fix typos in CanonicalSyntaxAnnotation source (#738) 2023-08-06 16:18:29 +02:00
Picnic-Bot
526227e8ed Upgrade Error Prone 2.21.0 -> 2.21.1 (#743)
See:
- https://github.com/google/error-prone/releases/tag/v2.21.1
- https://github.com/google/error-prone/compare/v2.21.0...v2.21.1
- https://github.com/PicnicSupermarket/error-prone/compare/v2.21.0-picnic-1...v2.21.1-picnic-1
2023-08-06 16:06:25 +02:00
Picnic-Bot
c2e837819e Upgrade Pitest Git plugins 1.0.12 -> 1.1.0 (#742) 2023-08-06 11:15:07 +02:00
Picnic-Bot
fd70be081e Upgrade Arcmutate 1.1.2 -> 1.1.3 (#739) 2023-08-05 20:13:27 +02:00
Picnic-Bot
2c47244571 Upgrade NullAway 0.10.11 -> 0.10.12 (#740)
See:
- https://github.com/uber/NullAway/blob/master/CHANGELOG.md
- https://github.com/uber/NullAway/releases/tag/v0.10.12
- https://github.com/uber/NullAway/compare/v0.10.11...v0.10.12
2023-08-05 13:27:26 +02:00
Picnic-Bot
8375f1650f Upgrade Error Prone 2.20.0 -> 2.21.0 (#737)
See:
- https://github.com/google/error-prone/releases/tag/v2.21.0
- https://github.com/google/error-prone/compare/v2.20.0...v2.21.0
- https://github.com/PicnicSupermarket/error-prone/compare/v2.20.0-picnic-1...v2.21.0-picnic-1
2023-08-03 11:06:33 +02:00
Picnic-Bot
c4daa04ce1 Upgrade Checker Framework Annotations 3.36.0 -> 3.37.0 (#736)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.37.0
- https://github.com/typetools/checker-framework/compare/checker-framework-3.36.0...checker-framework-3.37.0
2023-08-03 10:36:41 +02:00
Picnic-Bot
08f0e52104 Upgrade sortpom-maven-plugin 3.2.1 -> 3.3.0 (#731)
See:
- https://github.com/Ekryd/sortpom/wiki/Versions
- https://github.com/Ekryd/sortpom/releases/tag/sortpom-parent-3.3.0
- https://github.com/Ekryd/sortpom/compare/sortpom-parent-3.2.1...sortpom-parent-3.3.0
2023-08-02 14:39:18 +02:00
Picnic-Bot
10070bd4c8 Upgrade JUnit 5 5.9.3 -> 5.10.0 (#729)
See:
- https://junit.org/junit5/docs/current/release-notes/
- https://github.com/junit-team/junit5/releases/tag/r5.10.0-M1
- https://github.com/junit-team/junit5/releases/tag/r5.10.0-RC1
- https://github.com/junit-team/junit5/releases/tag/r5.10.0-RC2
- https://github.com/junit-team/junit5/releases/tag/r5.10.0
- https://github.com/junit-team/junit5/compare/r5.9.3...r5.10.0
2023-08-01 15:54:26 +02:00
Picnic-Bot
3c38fd3495 Upgrade actions/setup-java v3.11.0 -> v3.12.0 (#730)
See https://github.com/actions/setup-java/releases/tag/v3.12.0
2023-08-01 09:54:17 +02:00
Picnic-Bot
d54ec83844 Upgrade New Relic Java Agent 8.4.0 -> 8.5.0 (#732)
See:
- https://github.com/newrelic/newrelic-java-agent/releases/tag/v8.5.0
- https://github.com/newrelic/newrelic-java-agent/compare/v8.4.0...v8.5.0
2023-08-01 08:12:06 +02:00
Philip Leonard
e03d0de366 Introduce MongoDBTextFilterUsage check (#649) 2023-07-31 17:19:39 +02:00
Picnic-Bot
b40c5d61b9 Upgrade Checkstyle 10.12.1 -> 10.12.2 (#734)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.12.2
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.12.1...checkstyle-10.12.2
2023-07-31 12:54:47 +02:00
Picnic-Bot
6d27e46baf Upgrade Spring 5.3.28 -> 5.3.29 (#723)
See:
- https://github.com/spring-projects/spring-framework/releases/tag/v5.3.29
- https://github.com/spring-projects/spring-framework/compare/v5.3.28...v5.3.29
2023-07-24 08:15:30 +02:00
Picnic-Bot
8f48497322 Upgrade Spring Boot 2.7.13 -> 2.7.14 (#726)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v2.7.14
- https://github.com/spring-projects/spring-boot/compare/v2.7.13...v2.7.14
2023-07-23 12:41:36 +02:00
Stephan Schroevers
38554be3d1 Upgrade Error Prone fork v2.20.0-picnic-1 -> v2.20.0-picnic-2 (#725)
This new release is a workaround for jitpack/jitpack.io#5741.

See https://github.com/PicnicSupermarket/error-prone/compare/v2.20.0-picnic-1...v2.20.0-picnic-2
2023-07-19 09:16:40 +02:00
Picnic-Bot
28cdb91913 Upgrade Project Reactor 2022.0.8 -> 2022.0.9 (#721)
See:
- https://github.com/reactor/reactor/releases/tag/2022.0.9
- https://github.com/reactor/reactor/compare/2022.0.8...2022.0.9
2023-07-13 11:19:52 +02:00
Picnic-Bot
bd6693d23e Upgrade actions/deploy-pages v2.0.2 -> v2.0.3 (#718)
See:
- https://github.com/actions/deploy-pages/releases/tag/v2.0.3
2023-07-11 12:51:40 +02:00
Picnic-Bot
a3ec76845a Upgrade actions/upload-pages-artifact v1.0.9 -> v2.0.0 (#720)
See:
- https://github.com/actions/upload-pages-artifact/releases/tag/v2.0.0
- https://github.com/actions/upload-pages-artifact/releases/tag/v1.0.10
2023-07-11 08:55:31 +02:00
Picnic-Bot
e3b310761d Upgrade Swagger 2.2.14 -> 2.2.15 (#717)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.15
- https://github.com/swagger-api/swagger-core/compare/v2.2.14...v2.2.15
2023-07-10 08:55:50 +02:00
Picnic-Bot
774052f13a Upgrade CodeQL v2.20.2 -> v2.20.3 (#715)
See:
- https://github.com/github/codeql-action/blob/main/CHANGELOG.md
- https://github.com/github/codeql-action/compare/v2.20.2...v2.20.3
2023-07-07 15:11:45 +02:00
Picnic-Bot
bad7684639 Upgrade Arcmutate 1.1.1 -> 1.1.2 (#714) 2023-07-07 10:40:15 +02:00
Picnic-Bot
dd72b5a08a Upgrade Checker Framework Annotations 3.35.0 -> 3.36.0 (#711)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.36.0
- https://github.com/typetools/checker-framework/compare/checker-framework-3.35.0...checker-framework-3.36.0
2023-07-06 11:15:26 +02:00
Picnic-Bot
3eb30c5f93 Upgrade Guava 32.0.1-jre -> 32.1.1-jre (#705)
See:
- https://github.com/google/guava/releases/tag/v32.1.0
- https://github.com/google/guava/releases/tag/v32.1.1
- https://github.com/google/guava/compare/v32.0.1...v32.1.1
2023-07-06 10:22:56 +02:00
Picnic-Bot
2cc0539f8f Upgrade CodeQL v2.20.1 -> v2.20.2 (#707)
See:
- https://github.com/github/codeql-action/blob/main/CHANGELOG.md
- https://github.com/github/codeql-action/compare/v2.20.1...v2.20.2
2023-07-06 09:38:52 +02:00
Picnic-Bot
9210c30a0a Upgrade Pitest JUnit 5 Accelerator 1.0.5 -> 1.0.6 (#709) 2023-07-06 08:48:05 +02:00
Picnic-Bot
ba362ed460 Upgrade license-maven-plugin 2.1.0 -> 2.2.0 (#712)
See:
- https://github.com/mojohaus/license-maven-plugin/releases/tag/2.2.0
- https://github.com/mojohaus/license-maven-plugin/compare/2.1.0...2.2.0
2023-07-05 17:59:46 +02:00
Picnic-Bot
ab33adfab8 Upgrade Arcmutate 1.1.0 -> 1.1.1 (#708) 2023-07-05 17:48:22 +02:00
Rick Ossendrijver
006665ee6d Introduce MonoSingle Refaster rule (#703) 2023-07-05 13:54:49 +02:00
Picnic-Bot
08c49b9a58 Upgrade Pitest Git plugins 1.0.11 -> 1.0.12 (#710) 2023-07-05 13:42:54 +02:00
Picnic-Bot
777aa970f1 Upgrade ruby/setup-ruby v1.126.0 -> v1.152.0 (#706)
See:
- https://github.com/ruby/setup-ruby/releases/tag/v1.152.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.151.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.150.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.149.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.148.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.147.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.146.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.145.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.144.2
- https://github.com/ruby/setup-ruby/releases/tag/v1.144.1
- https://github.com/ruby/setup-ruby/releases/tag/v1.144.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.143.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.142.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.141.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.140.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.139.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.138.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.137.2
- https://github.com/ruby/setup-ruby/releases/tag/v1.137.1
- https://github.com/ruby/setup-ruby/releases/tag/v1.137.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.136.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.135.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.134.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.133.2
- https://github.com/ruby/setup-ruby/releases/tag/v1.133.1
- https://github.com/ruby/setup-ruby/releases/tag/v1.133.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.132.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.131.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.130.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.129.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.128.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.127.0
2023-07-05 12:18:29 +02:00
Picnic-Bot
a040310870 Upgrade AutoValue 1.10.1 -> 1.10.2 (#704)
See:
- https://github.com/google/auto/releases/tag/auto-value-1.10.2
- https://github.com/google/auto/compare/auto-value-1.10.1...auto-value-1.10.2
2023-06-30 12:59:26 +02:00
Picnic-Bot
d6ab366f31 Upgrade CodeQL v2.2.11 -> v2.20.1 (#670)
See:
- https://github.com/github/codeql-action/blob/main/CHANGELOG.md
- https://github.com/github/codeql-action/compare/v2.20.0...v2.20.1
- https://github.com/github/codeql-action/compare/v2.3.6...v2.20.0
- https://github.com/github/codeql-action/compare/v2.3.5...v2.3.6
- https://github.com/github/codeql-action/compare/v2.3.4...v2.3.5
- https://github.com/github/codeql-action/compare/v2.3.3...v2.3.4
- https://github.com/github/codeql-action/compare/v2.3.2...v2.3.3
- https://github.com/github/codeql-action/compare/v2.3.1...v2.3.2
- https://github.com/github/codeql-action/compare/v2.3.0...v2.3.1
- https://github.com/github/codeql-action/compare/v2.2.12...v2.3.0
- https://github.com/github/codeql-action/compare/v2.2.11...v2.2.12
2023-06-28 13:20:53 +02:00
Picnic-Bot
d9fefa6824 Upgrade Swagger 2.2.13 -> 2.2.14 (#702)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.14
- https://github.com/swagger-api/swagger-core/compare/v2.2.13...v2.2.14
2023-06-27 08:28:08 +02:00
Picnic-Bot
c8b3a6df82 Upgrade pitest-maven-plugin 1.14.1 -> 1.14.2 (#696)
See:
- https://github.com/hcoles/pitest/releases/tag/1.14.2
- https://github.com/hcoles/pitest/compare/1.14.1...1.14.2
2023-06-26 12:07:21 +02:00
Picnic-Bot
782a790def Upgrade NullAway 0.10.10 -> 0.10.11 (#695)
See:
- https://github.com/uber/NullAway/blob/master/CHANGELOG.md
- https://github.com/uber/NullAway/compare/v0.10.10...v0.10.11
2023-06-26 11:31:05 +02:00
Picnic-Bot
706b5f401f Upgrade Checkstyle 10.12.0 -> 10.12.1 (#699)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.12.1
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.12.0...checkstyle-10.12.1
2023-06-26 11:20:21 +02:00
Picnic-Bot
9f500fd5bb Upgrade ossf/scorecard-action v2.1.3 -> v2.2.0 (#697)
And link to the new scorecard report page.

See:
- https://github.com/ossf/scorecard-action/releases/tag/v2.2.0
- https://github.com/ossf/scorecard-action/compare/v2.1.3...v2.2.0
2023-06-26 09:50:25 +02:00
Picnic-Bot
f541cd2e52 Upgrade Swagger 2.2.12 -> 2.2.13 (#698)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.13
- https://github.com/swagger-api/swagger-core/compare/v2.2.12...v2.2.13
2023-06-26 09:20:25 +02:00
Picnic-Bot
fc862aad94 Upgrade maven-clean-plugin 3.2.0 -> 3.3.1 (#693)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MCLEAN%20AND%20fixVersion%20%3E%203.2.0%20AND%20fixVersion%20%3C%3D%203.3.1
- https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.2.0...maven-clean-plugin-3.3.1
2023-06-23 17:47:37 +02:00
Picnic-Bot
42810bc00c Upgrade Spring Boot 2.7.12 -> 2.7.13 (#694)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v2.7.13
- https://github.com/spring-projects/spring-boot/compare/v2.7.12...v2.7.13
2023-06-23 11:11:10 +02:00
Picnic-Bot
f17d736356 Upgrade AutoCommon 1.2.1 -> 1.2.2 (#681)
See:
- https://github.com/google/auto/releases/tag/auto-common-1.2.2
- https://github.com/google/auto/compare/auto-common-1.2.1...auto-common-1.2.2
2023-06-23 10:32:40 +02:00
Picnic-Bot
705acc9164 Upgrade Spring 5.3.27 -> 5.3.28 (#687)
See:
- https://github.com/spring-projects/spring-framework/releases/tag/v5.3.28
- https://github.com/spring-projects/spring-framework/compare/v5.3.27...v5.3.28
2023-06-23 10:19:13 +02:00
Picnic-Bot
bb969a8b80 Upgrade Mockito 5.3.1 -> 5.4.0 (#689)
See:
- https://github.com/mockito/mockito/releases/tag/v5.4.0
- https://github.com/mockito/mockito/compare/v5.3.1...v5.4.0
2023-06-23 10:09:55 +02:00
Picnic-Bot
c5a700c555 Upgrade Truth 1.1.4 -> 1.1.5 (#688)
See:
- https://github.com/google/truth/releases/tag/v1.1.5
- https://github.com/google/truth/compare/v1.1.4...v1.1.5
2023-06-23 09:56:42 +02:00
Picnic-Bot
2837a04433 Upgrade New Relic Java Agent 8.3.0 -> 8.4.0 (#690)
See:
- https://github.com/newrelic/newrelic-java-agent/releases/tag/v8.4.0
- https://github.com/newrelic/newrelic-java-agent/compare/v8.3.0...v8.4.0
2023-06-22 11:55:19 +02:00
Picnic-Bot
74222e8fa5 Upgrade Arcmutate 1.0.5 -> 1.1.0 (#692) 2023-06-22 09:25:43 +02:00
Stephan Schroevers
dc36ff2c0b [maven-release-plugin] prepare for next development iteration 2023-06-21 21:15:38 +02:00
Stephan Schroevers
09208aa49a [maven-release-plugin] prepare release v0.12.0 2023-06-21 21:15:38 +02:00
Stephan Schroevers
b81ec973a1 Upgrade Error Prone 2.19.1 -> 2.20.0 (#685)
Summary of key changes:
- The `MissingRefasterAnnotation` check was contributed to Error Prone,
  and so is deleted here (multiple checks with the same name are not
  supported).
- Similarly, Error Prone now supports the `-XepAllSuggestionsAsWarnings`
  flag out of the box. So the `ErrorProneFork` class is deleted, as it
  has currently no further use.

While there, include a tweak to `run-mutation-tests.sh`.

Fixes #686.

See:
- https://github.com/google/error-prone/releases/tag/v2.20.0
- https://github.com/google/error-prone/compare/v2.19.1...v2.20.0
- https://github.com/PicnicSupermarket/error-prone/compare/v2.19.1-picnic-1...v2.20.0-picnic-1
2023-06-20 15:52:26 +02:00
Picnic-Bot
8fb57b5bab Upgrade actions/upload-artifact v3.1.1 -> v3.1.2 (#664)
See:
- https://github.com/actions/upload-artifact/releases/tag/v3.1.2
2023-06-20 09:01:33 +02:00
Picnic-Bot
f4aaa5852c Upgrade dawidd6/action-download-artifact v2.24.2 -> v2.27.0 (#669)
See:
- https://github.com/dawidd6/action-download-artifact/compare/v2.26.1...v2.27.0
- https://github.com/dawidd6/action-download-artifact/compare/v2.26.0...v2.26.1
- https://github.com/dawidd6/action-download-artifact/compare/v2.25.0...v2.26.0
- https://github.com/dawidd6/action-download-artifact/compare/v2.24.4...v2.25.0
- https://github.com/dawidd6/action-download-artifact/compare/v2.24.3...v2.24.4
- https://github.com/dawidd6/action-download-artifact/compare/v2.24.2...v2.24.3
2023-06-19 11:54:34 +02:00
Picnic-Bot
2148b7ede4 Upgrade actions/upload-pages-artifact v1.0.5 -> v1.0.9 (#665)
See:
- https://github.com/actions/upload-pages-artifact/releases/tag/v1.0.9
- https://github.com/actions/upload-pages-artifact/releases/tag/v1.0.8
- https://github.com/actions/upload-pages-artifact/releases/tag/v1.0.7
- https://github.com/actions/upload-pages-artifact/releases/tag/v1.0.6
2023-06-19 11:30:48 +02:00
Picnic-Bot
b2320779e7 Upgrade actions/configure-pages v2.1.3 -> v3.0.6 (#673)
See:
- https://github.com/actions/configure-pages/releases/tag/v3.0.6
- https://github.com/actions/configure-pages/releases/tag/v3.0.5
- https://github.com/actions/configure-pages/releases/tag/v3.0.4
- https://github.com/actions/configure-pages/releases/tag/v3.0.3
- https://github.com/actions/configure-pages/releases/tag/v3.0.2
- https://github.com/actions/configure-pages/releases/tag/v3.0.1
- https://github.com/actions/configure-pages/releases/tag/v3.0.0
2023-06-19 11:13:20 +02:00
Picnic-Bot
ef0d65d360 Upgrade actions/deploy-pages v1.2.8 -> v2.0.2 (#674)
See:
- https://github.com/actions/deploy-pages/releases/tag/v2.0.2
- https://github.com/actions/deploy-pages/releases/tag/v2.0.1
- https://github.com/actions/deploy-pages/releases/tag/v2.0.0
2023-06-19 11:01:54 +02:00
Picnic-Bot
d29fde8856 Upgrade actions/checkout v3.1.0 -> v3.5.3 (#667)
See:
- https://github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v353
- https://github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v352
- https://github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v351
- https://github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v350
- https://github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v340
- https://github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v330
- https://github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v320
2023-06-19 09:18:01 +02:00
Picnic-Bot
524c7efb48 Upgrade actions/deploy-pages v1.2.3 -> v1.2.8 (#663)
See:
- https://github.com/actions/deploy-pages/releases/tag/v1.2.8
- https://github.com/actions/deploy-pages/releases/tag/v1.2.7
- https://github.com/actions/deploy-pages/releases/tag/v1.2.6
- https://github.com/actions/deploy-pages/releases/tag/v1.2.5
- https://github.com/actions/deploy-pages/releases/tag/v1.2.4
2023-06-19 08:56:31 +02:00
Picnic-Bot
a62acfd7b5 Upgrade Project Reactor 2022.0.7 -> 2022.0.8 (#683)
See:
- https://github.com/reactor/reactor/releases/tag/2022.0.8
- https://github.com/reactor/reactor/compare/2022.0.7...2022.0.8
2023-06-16 16:16:06 +02:00
Picnic-Bot
c40e1d6691 Upgrade actions/setup-java v3.8.0 -> v3.11.0 (#668)
See:
- https://github.com/actions/setup-java/releases/tag/v3.9.0
- https://github.com/actions/setup-java/releases/tag/v3.10.0
- https://github.com/actions/setup-java/releases/tag/v3.11.0
2023-06-16 15:35:04 +02:00
Picnic-Bot
57a22bf9de Upgrade Swagger 2.2.11 -> 2.2.12 (#684)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.12
- https://github.com/swagger-api/swagger-core/compare/v2.2.11...v2.2.12
2023-06-16 15:12:15 +02:00
Picnic-Bot
ec982fe011 Upgrade AutoService 1.1.0 -> 1.1.1 (#682)
See:
- https://github.com/google/auto/releases/tag/auto-service-1.1.1
- https://github.com/google/auto/compare/auto-service-1.1.0...auto-service-1.1.1
2023-06-14 12:35:41 +02:00
Picnic-Bot
1860e24e65 Upgrade Guava 32.0.0-jre -> 32.0.1-jre (#677)
See:
- https://github.com/google/guava/releases/tag/v32.0.1
- https://github.com/google/guava/compare/v32.0.0...v32.0.1
2023-06-12 19:04:31 +02:00
Picnic-Bot
cce248c306 Upgrade Surefire 3.1.0 -> 3.1.2 (#666)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20SUREFIRE%20AND%20fixVersion%20%3E%203.1.0%20AND%20fixVersion%20%3C%3D%203.1.2
- https://github.com/apache/maven-surefire/releases/tag/surefire-3.1.2
- https://github.com/apache/maven-surefire/compare/surefire-3.1.0...surefire-3.1.2
2023-06-12 11:56:20 +02:00
Picnic-Bot
96aca8ea2b Upgrade versions-maven-plugin 2.15.0 -> 2.16.0 (#678)
See:
- https://github.com/mojohaus/versions/releases/tag/2.16.0
- https://github.com/mojohaus/versions-maven-plugin/compare/2.15.0...2.16.0
2023-06-10 14:21:16 +02:00
Picnic-Bot
70d2bf9016 Upgrade license-maven-plugin 2.0.1 -> 2.1.0 (#671)
See:
- https://github.com/mojohaus/license-maven-plugin/releases/tag/2.1.0
- https://github.com/mojohaus/license-maven-plugin/compare/2.0.1...2.1.0
2023-06-08 14:25:34 +02:00
Rick Ossendrijver
cee3c58d07 Configure Renovate to pin GitHub Action digests (#675) 2023-06-08 10:23:05 +02:00
Picnic-Bot
c141ebe05d Upgrade swagger-annotations 2.2.10 -> 2.2.11 (#657)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.11
- https://github.com/swagger-api/swagger-core/compare/v2.2.10...v2.2.11
2023-06-07 11:40:15 +02:00
Picnic-Bot
f5a8c412af Upgrade Byte Buddy 1.14.4 -> 1.14.5 (#658)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.14.5
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.4...byte-buddy-1.14.5
2023-06-07 10:48:54 +02:00
Picnic-Bot
93440826ed Upgrade Arcmutate 1.0.4 -> 1.0.5 (#656) 2023-06-07 08:02:44 +02:00
Picnic-Bot
80dcae319e Upgrade Jackson 2.15.1 -> 2.15.2 (#652)
See:
- https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.15.2
- https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.15.1...jackson-bom-2.15.2
2023-06-06 16:30:09 +02:00
Picnic-Bot
7371d03db8 Upgrade Truth 1.1.3 -> 1.1.4 (#653)
See:
- https://github.com/google/truth/releases/tag/v1.1.4
- https://github.com/google/truth/compare/release_1_1_3...v1.1.4
2023-06-06 09:47:09 +02:00
Picnic-Bot
c6b98e61ff Upgrade maven-release-plugin 3.0.0 -> 3.0.1 (#662)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MRELEASE%20AND%20fixVersion%20%3E%203.0.0%20AND%20fixVersion%20%3C%3D%203.0.1
- https://github.com/apache/maven-release/releases/tag/maven-release-3.0.1
- https://github.com/apache/maven-release/compare/maven-release-3.0.0...maven-release-3.0.1
2023-06-06 08:34:35 +02:00
Picnic-Bot
ce8f9f60c8 Upgrade New Relic Java Agent 8.2.0 -> 8.3.0 (#659)
See:
- https://github.com/newrelic/newrelic-java-agent/releases/tag/v8.3.0
- https://github.com/newrelic/newrelic-java-agent/compare/v8.2.0...v8.3.0
2023-06-06 07:50:01 +02:00
Picnic-Bot
cdf27acd9c Upgrade Checker Framework Annotations 3.34.0 -> 3.35.0 (#660)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.35.0
- https://github.com/typetools/checker-framework/compare/checker-framework-3.34.0...checker-framework-3.35.0
2023-06-06 07:40:05 +02:00
Picnic-Bot
49e5fd1273 Upgrade extra-enforcer-rules 1.6.2 -> 1.7.0 (#661)
See:
- https://github.com/mojohaus/extra-enforcer-rules/releases/tag/1.7.0
- https://github.com/mojohaus/extra-enforcer-rules/compare/1.6.2...1.7.0
2023-06-06 07:20:46 +02:00
Picnic-Bot
5085db25c0 Upgrade Guava 31.1-jre -> 32.0.0-jre (#650)
See:
- https://guava.dev/releases/32.0.0-jre/api/diffs/
- https://github.com/google/guava/releases/tag/v32.0.0
- https://github.com/google/guava/compare/v31.1...v32.0.0
2023-05-30 16:49:11 +02:00
Picnic-Bot
125d24bc13 Upgrade Checkstyle 10.11.0 -> 10.12.0 (#651)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.12.0
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.11.0...checkstyle-10.12.0
2023-05-30 08:44:15 +02:00
Picnic-Bot
ea02144bff Upgrade pitest-maven-plugin 1.14.0 -> 1.14.1 (#648)
See:
- https://github.com/hcoles/pitest/releases/tag/1.14.1
- https://github.com/hcoles/pitest/compare/1.14.0...1.14.1
2023-05-26 10:13:47 +02:00
Luke Prananta
cc2c49edc3 Introduce OptionalOrElseGet Refaster rule (#527)
While there, introduce `IsLikelyTrivialComputation` Matcher.
2023-05-26 09:19:51 +02:00
Stephan Schroevers
8bc878a05c Improve AbstractMatcherTestChecker (#599)
These changes enable testing of a wider range of `Matcher` implementations. In
a nutshell:
- The `Matcher` under test is now passed `VisitorState` instances with an
  accurate `TreePath`.
- The `Matcher` under test is no longer consulted for method select and cast
  type expressions, mirroring Refaster behavior.
2023-05-26 08:29:26 +02:00
Picnic-Bot
b399ef8910 Upgrade Spring Boot 2.7.11 -> 2.7.12 (#637)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v2.7.12
- https://github.com/spring-projects/spring-boot/compare/v2.7.11...v2.7.12
2023-05-24 19:03:38 +02:00
Picnic-Bot
7dba641a79 Upgrade pitest-junit5-plugin 1.1.2 -> 1.2.0 (#638)
See https://github.com/pitest/pitest-junit5-plugin/compare/1.1.2...1.2.0
2023-05-24 17:20:57 +02:00
Picnic-Bot
da1528129f Upgrade maven-source-plugin 3.2.1 -> 3.3.0 (#643)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MSOURCES%20AND%20fixVersion%20%3E%203.2.1%20AND%20fixVersion%20%3C%3D%203.3.0
- https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.2.1...maven-source-plugin-3.3.0
2023-05-24 17:08:20 +02:00
Picnic-Bot
d0bbc5c14b Upgrade pitest-maven-plugin 1.13.1 -> 1.14.0 (#633)
See:
- https://github.com/hcoles/pitest/releases/tag/1.13.2
- https://github.com/hcoles/pitest/releases/tag/1.14.0
- https://github.com/hcoles/pitest/compare/1.13.1...1.14.0
2023-05-24 16:30:06 +02:00
Picnic-Bot
da532c79c7 Upgrade Pitest Git plugins 1.0.10 -> 1.0.11 (#634) 2023-05-24 16:10:58 +02:00
Picnic-Bot
04e2900a48 Upgrade git-commit-id-maven-plugin 5.0.0 -> 6.0.0 (#635)
See:
- https://github.com/git-commit-id/git-commit-id-maven-plugin/releases/tag/v6.0.0
- https://github.com/git-commit-id/git-commit-id-maven-plugin/compare/v5.0.0...v6.0.0
2023-05-24 16:00:11 +02:00
Picnic-Bot
f097095398 Upgrade AutoService 1.0.1 -> 1.1.0 (#647)
See:
- https://github.com/google/auto/releases/tag/auto-service-1.0.2
- https://github.com/google/auto/releases/tag/auto-service-1.1.0
- https://github.com/google/auto/compare/auto-service-1.0.1...auto-service-1.1.0
2023-05-24 13:25:40 +02:00
Philip Leonard
c53a3f64b6 Qualify non-static TestMode imports across BugChecker test classes (#630)
Prefer non-static imports and qualification of
`BugCheckerRefactoringTestHelper#TestMode` members across bug checker test
classes.
2023-05-23 09:31:59 +02:00
Picnic-Bot
cdfcecc204 Upgrade maven-dependency-plugin 3.5.0 -> 3.6.0 (#646)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MDEP%20AND%20fixVersion%20%3E%203.5.0%20AND%20fixVersion%20%3C%3D%203.6.0
- https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.5.0...maven-dependency-plugin-3.6.0
2023-05-23 08:43:31 +02:00
Picnic-Bot
4f4b3fb865 Upgrade maven-checkstyle-plugin 3.2.2 -> 3.3.0 (#645)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MCHECKSTYLE%20AND%20fixVersion%20%3E%203.2.2%20AND%20fixVersion%20%3C%3D%203.3.0
- https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.2.2...maven-checkstyle-plugin-3.3.0
2023-05-23 07:32:41 +02:00
Picnic-Bot
cce36d24df Upgrade TestNG 7.7.1 -> 7.8.0 (#639)
See:
- https://github.com/testng-team/testng/releases/tag/7.8.0
- https://github.com/testng-team/testng/compare/7.7.1...7.8.0
2023-05-22 08:39:36 +02:00
Picnic-Bot
3bbae43da8 Upgrade swagger-annotations 1.6.10 -> 1.6.11 (#629)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v1.6.11
- https://github.com/swagger-api/swagger-core/compare/v1.6.10...v1.6.11
2023-05-17 13:18:26 +02:00
Picnic-Bot
3217a6974d Upgrade swagger-annotations 2.2.9 -> 2.2.10 (#628)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.10
- https://github.com/swagger-api/swagger-core/compare/v2.2.9...v2.2.10
2023-05-17 12:19:30 +02:00
Picnic-Bot
7b71e4ea3e Upgrade Jackson 2.15.0 -> 2.15.1 (#631)
See:
- https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.15.1
- https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.15.0...jackson-bom-2.15.1
2023-05-17 11:28:55 +02:00
Stephan Schroevers
3df6dc957d [maven-release-plugin] prepare for next development iteration 2023-05-16 17:38:46 +02:00
Stephan Schroevers
a1ecd816ff [maven-release-plugin] prepare release v0.11.1 2023-05-16 17:38:46 +02:00
Stephan Schroevers
03af05889a Make ThirdPartyLibrary compatible with -source 8 (#627)
When targeting Java 8, `unnamedModule` is not properly initialized,
causing an NPE when trying to load a class from it. In that context
`noModule` should be used instead.

Fixes #626.
2023-05-16 09:53:52 +02:00
Stephan Schroevers
f52a93cc4e [maven-release-plugin] prepare for next development iteration 2023-05-14 17:09:09 +02:00
Stephan Schroevers
610085393c [maven-release-plugin] prepare release v0.11.0 2023-05-14 17:09:08 +02:00
Picnic-Bot
08e55fdfb6 Upgrade Error Prone 2.18.0 -> 2.19.1 (#621)
Resolves #622.

See:
- https://github.com/google/error-prone/releases/tag/v2.19.0
- https://github.com/google/error-prone/releases/tag/v2.19.1
- https://github.com/google/error-prone/compare/v2.18.0...v2.19.1
- https://github.com/PicnicSupermarket/error-prone/compare/v2.18.0-picnic-1...v2.19.1-picnic-1
2023-05-14 17:01:46 +02:00
Luke Prananta
137ec4c573 Introduce StreamsConcat Refaster rule (#619) 2023-05-14 16:48:35 +02:00
Luke Prananta
7cf569cca3 Introduce IsRefasterAsVarargs matcher for use by Refaster templates (#623) 2023-05-14 16:33:32 +02:00
Picnic-Bot
d53db2981c Upgrade build-helper-maven-plugin 3.3.0 -> 3.4.0 (#625)
See:
- https://github.com/mojohaus/build-helper-maven-plugin/releases/tag/3.4.0
- https://github.com/mojohaus/build-helper-maven-plugin/compare/build-helper-maven-plugin-3.3.0...3.4.0
2023-05-13 17:23:50 +02:00
Picnic-Bot
454e8662b1 Upgrade Servlet API 4.0.4 -> 6.0.0 (#119)
See https://github.com/eclipse-ee4j/servlet-api/compare/4.0.4-RELEASE...6.0.0
2023-05-13 17:11:29 +02:00
Picnic-Bot
c2f217f055 Upgrade Project Reactor 2022.0.6 -> 2022.0.7 (#620)
See:
- https://github.com/reactor/reactor/releases/tag/2022.0.7
- https://github.com/reactor/reactor/compare/2022.0.6...2022.0.7
2023-05-12 07:44:33 +02:00
Picnic-Bot
45ced8b9d8 Upgrade Checkstyle 10.10.0 -> 10.11.0 (#624)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.11.0
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.10.0...checkstyle-10.11.0
2023-05-11 21:50:41 +02:00
Picnic-Bot
666fe0d49c Upgrade license-maven-plugin 2.0.0 -> 2.0.1 (#615)
See:
- https://github.com/mojohaus/license-maven-plugin/releases/tag/2.0.1
- https://github.com/mojohaus/license-maven-plugin/compare/license-maven-plugin-2.0.0...2.0.1
2023-05-08 13:29:07 +02:00
Picnic-Bot
e50a7e1795 Upgrade Surefire 3.0.0 -> 3.1.0 (#618)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20SUREFIRE%20AND%20fixVersion%20%3E%203.0.0%20AND%20fixVersion%20%3C%3D%203.1.0
- https://github.com/apache/maven-surefire/compare/surefire-3.0.0...surefire-3.1.0
2023-05-08 09:15:24 +02:00
Picnic-Bot
f403b988d5 Upgrade maven-gpg-plugin 3.0.1 -> 3.1.0 (#616)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MGPG%20AND%20fixVersion%20%3E%203.0.1%20AND%20fixVersion%20%3C%3D%203.1.0
- https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.0.1...maven-gpg-plugin-3.1.0
2023-05-08 07:24:31 +02:00
Stephan Schroevers
3ee28f2c05 [maven-release-plugin] prepare for next development iteration 2023-05-04 12:47:28 +02:00
Stephan Schroevers
bb122388f5 [maven-release-plugin] prepare release v0.10.0 2023-05-04 10:02:03 +02:00
Picnic-Bot
760b1ddf31 Upgrade Google Java Format 1.16.0 -> 1.17.0 (#611)
See:
- https://github.com/google/google-java-format/releases/tag/v1.17.0
- https://github.com/google/google-java-format/compare/v1.16.0...v1.17.0
2023-05-04 09:53:57 +02:00
Stephan Schroevers
6229fa9245 Extend StreamRules Refaster rule collection (#605)
All changes suggested by SonarCloud's `java:s4034` rule, as well as the
examples mentioned in openrewrite/rewrite#2984 are now covered. (In a
number of cases through composition of more generic rules.)

See https://rules.sonarsource.com/java/RSPEC-4034
2023-05-04 08:08:27 +02:00
Mohamed Sameh
7d728e956e Introduce Sets{Difference,Intersection}{,Map,Multimap} and SetsUnion Refaster rules (#607) 2023-05-03 16:56:02 +02:00
Stephan Schroevers
52245c6310 Extend PrimitiveRules Refaster rule collection (#608)
Assorted methods and constants exposed by Guava's
`com.google.common.primitives.*` types are now replaced with their JDK
equivalents.

While there, also update the parameter types of some existing `@AfterTemplate`
methods, resolving an inconsistency flagged by @knutwannheden.
2023-05-03 14:39:03 +02:00
Rick Ossendrijver
4b69fe9de9 Extend AssertJThrowingCallableRules Refaster rule collection (#609) 2023-05-03 10:22:33 +02:00
Picnic-Bot
3405962703 Upgrade New Relic Java Agent 8.1.0 -> 8.2.0 (#612)
See:
- https://github.com/newrelic/newrelic-java-agent/releases/tag/v8.2.0
- https://github.com/newrelic/newrelic-java-agent/compare/v8.1.0...v8.2.0
2023-05-03 10:10:33 +02:00
Picnic-Bot
7d3d6a3cf8 Upgrade fmt-maven-plugin 2.19 -> 2.20 (#613)
See:
- https://github.com/spotify/fmt-maven-plugin/releases/tag/fmt-maven-plugin-2.20
- https://github.com/spotify/fmt-maven-plugin/compare/2.19.0...fmt-maven-plugin-2.20
2023-05-03 09:38:45 +02:00
Picnic-Bot
97fa90b64e Upgrade Checker Framework Annotations 3.33.0 -> 3.34.0 (#614)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.34.0
- https://github.com/typetools/checker-framework/compare/checker-framework-3.33.0...checker-framework-3.34.0
2023-05-03 09:11:31 +02:00
Picnic-Bot
ced1ce625d Upgrade pitest-maven-plugin 1.13.0 -> 1.13.1 (#610)
See:
- https://github.com/hcoles/pitest/releases/tag/1.13.1
- https://github.com/hcoles/pitest/compare/1.13.0...1.13.1
2023-05-03 08:43:27 +02:00
Stephan Schroevers
de224deffa Upgrade JDKs used by GitHub Actions builds (#604)
Summary of changes:
- Use JDK 11.0.19 instead of 11.0.18.
- Use JDK 17.0.7 instead of 17.0.6.
- Use JDK 20.0.1 instead of 19.0.2.
- Drop the early access build, as Error Prone is currently not compatible with JDK 21-ea.

See:
- https://www.oracle.com/java/technologies/javase/11-0-19-relnotes.html
- https://www.oracle.com/java/technologies/javase/17-0-7-relnotes.html
- https://www.oracle.com/java/technologies/javase/20-relnote-issues.html
- https://www.oracle.com/java/technologies/javase/20-0-1-relnotes.html
2023-05-02 08:51:39 +02:00
Picnic-Bot
deebd21d34 Upgrade maven-enforcer-plugin 3.2.1 -> 3.3.0 (#566)
See:
- https://github.com/apache/maven-enforcer/releases/tag/enforcer-3.3.0
- https://github.com/apache/maven-enforcer/compare/enforcer-3.2.1...enforcer-3.3.0
2023-05-01 13:43:43 +02:00
Stephan Schroevers
ab84ef4c12 Fix Gradle installation guide link in README (#606) 2023-05-01 08:13:10 +02:00
Picnic-Bot
f675cf0139 Upgrade Checkstyle 10.9.3 -> 10.10.0 (#602)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.10.0
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.9.3...checkstyle-10.10.0
2023-04-29 11:46:59 +02:00
Picnic-Bot
e7e35c7571 Upgrade JUnit 5 5.9.2 -> 5.9.3 (#600)
See:
- https://junit.org/junit5/docs/current/release-notes/
- https://github.com/junit-team/junit5/releases/tag/r5.9.3
- https://github.com/junit-team/junit5/compare/r5.9.2...r5.9.3
2023-04-28 17:06:45 +02:00
Picnic-Bot
f3c5aee7f5 Upgrade Pitest Git plugins 1.0.8 -> 1.0.10 (#601) 2023-04-28 16:48:48 +02:00
Picnic-Bot
b344d4640c Upgrade jacoco-maven-plugin 0.8.9 -> 0.8.10 (#597)
See:
- https://github.com/jacoco/jacoco/releases/tag/v0.8.10
- https://github.com/jacoco/jacoco/compare/v0.8.9...v0.8.10
2023-04-26 10:28:55 +02:00
Picnic-Bot
393aebca9f Upgrade Arcmutate 1.0.3 -> 1.0.4 (#596) 2023-04-26 09:20:42 +02:00
Mohamed Sameh
32d50ab6fe Extend StreamRules Refaster rule collection (#593)
All changes suggested by SonarCloud's java:S4266 rule are now covered.

See https://sonarcloud.io/organizations/picnic-technologies/rules?open=java%3AS4266&rule_key=java%3AS4266

Fixes #578.
2023-04-26 09:07:50 +02:00
Picnic-Bot
c807568b9c Upgrade pitest-maven-plugin 1.12.0 -> 1.13.0 (#591)
See:
- https://github.com/hcoles/pitest/releases/tag/1.13.0
- https://github.com/hcoles/pitest/compare/1.12.0...1.13.0
2023-04-26 07:59:38 +02:00
Picnic-Bot
4dd2aa12cc Upgrade Jackson 2.14.2 -> 2.15.0 (#594)
See:
- https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.15
- https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.14.2...jackson-bom-2.15.0
2023-04-25 09:52:52 +02:00
Stephan Schroevers
2f2e7e7a35 Add additional quality badges to README (#584) 2023-04-25 08:54:31 +02:00
Stephan Schroevers
e0c795d248 Introduce SonarCloud integration and resolve assorted violations (#575) 2023-04-25 08:19:11 +02:00
Picnic-Bot
554a3e634c Upgrade Mockito 5.3.0 -> 5.3.1 (#590)
See:
- https://github.com/mockito/mockito/releases/tag/v5.3.1
- https://github.com/mockito/mockito/compare/v5.3.0...v5.3.1
2023-04-22 14:39:55 +02:00
Picnic-Bot
7a3ae7c646 Upgrade Pitest Git plugins 1.0.7 -> 1.0.8 (#589) 2023-04-21 13:13:47 +02:00
Picnic-Bot
6d24540d01 Upgrade Arcmutate 1.0.2 -> 1.0.3 (#585) 2023-04-21 10:30:30 +02:00
Picnic-Bot
7637ffee24 Upgrade Pitest JUnit 5 Accelerator 1.0.4 -> 1.0.5 (#586) 2023-04-21 10:16:18 +02:00
Picnic-Bot
12d2b52e38 Upgrade Spring Boot 2.7.10 -> 2.7.11 (#588)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v2.7.11
- https://github.com/spring-projects/spring-boot/compare/v2.7.10...v2.7.11
2023-04-21 09:17:56 +02:00
Picnic-Bot
53daabe5df Upgrade maven-checkstyle-plugin 3.2.1 -> 3.2.2 (#587)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MCHECKSTYLE%20AND%20fixVersion%20%3E%203.2.1%20AND%20fixVersion%20%3C%3D%203.2.2
- https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.2.1...maven-checkstyle-plugin-3.2.2
2023-04-21 08:21:49 +02:00
Stephan Schroevers
ee0884e65f Introduce AssociativeMethodInvocation check (#560) 2023-04-19 08:55:14 +02:00
Mohamed Sameh
3af81d8b10 Introduce StringIs{,Not}EmptyPredicate Refaster rules (#577) 2023-04-18 09:04:03 +02:00
Mohamed Sameh
ebd64c1077 Introduce AssertThatMapContainsOnlyKeys Refaster rule (#576) 2023-04-18 08:09:17 +02:00
Stephan Schroevers
929f1dd1c7 Introduce OpenSSF Scorecard GitHub action (#574)
And resolve some of the issues it identified.

See https://securityscorecards.dev
2023-04-16 09:56:14 +02:00
Stephan Schroevers
9ddd91a50e Introduce CodeQL security vulnerability analysis (#573)
See https://codeql.github.com
2023-04-15 19:23:25 +02:00
Picnic-Bot
a1227ca710 Upgrade New Relic Java Agent 8.0.1 -> 8.1.0 (#583)
See:
- https://github.com/newrelic/newrelic-java-agent/releases/tag/v8.1.0
- https://github.com/newrelic/newrelic-java-agent/compare/v8.0.1...v8.1.0
2023-04-15 16:57:45 +02:00
Picnic-Bot
44e0904357 Upgrade Spring 5.3.26 -> 5.3.27 (#582)
See:
- https://github.com/spring-projects/spring-framework/releases/tag/v5.3.27
- https://github.com/spring-projects/spring-framework/compare/v5.3.26...v5.3.27
2023-04-15 13:20:18 +02:00
Stephan Schroevers
9b54c73dc0 Have DirectReturn check consider finally blocks (#568) 2023-04-14 12:56:22 +02:00
Stephan Schroevers
8ace5b7e9a Fix and enable SuggestedFixRules tests (#581) 2023-04-13 14:20:58 +02:00
Stephan Schroevers
94ffc5d495 Apply assorted test cleanup (#562)
Summary of changes:
- Inline more `CompilationTestHelper` fields.
- Move inner class to the bottom of the outer class.
- Improve test parameter name.
2023-04-13 12:54:51 +02:00
Stephan Schroevers
977019c5bf Improve contribution documentation (#572)
- Introduce a `./run-full-build.sh` script.
- Explicitly mention that users should run this script before opening a pull
  request.
- Emphasize that many build warnings can be resolved automatically.
- Introduce a `SECURITY.md` file as suggested by GitHub.
2023-04-13 09:10:56 +02:00
Luke Prananta
6514236514 Introduce FluxCollectToImmutableList Refaster rule (#570)
And extend `MonoIdentity` to simplify `mono.map(ImmutableList::copyOf)`
expressions where possible, as the new rule may introduce such cases.
2023-04-13 08:31:45 +02:00
Picnic-Bot
6d23fbdd35 Upgrade Project Reactor 2022.0.5 -> 2022.0.6 (#579)
See:
- https://github.com/reactor/reactor/releases/tag/2022.0.6
- https://github.com/reactor/reactor/compare/2022.0.5...2022.0.6
2023-04-12 14:03:31 +02:00
Picnic-Bot
b6f14c073a Upgrade Mockito 5.2.0 -> 5.3.0 (#580)
See:
- https://github.com/mockito/mockito/releases/tag/v5.3.0
- https://github.com/mockito/mockito/compare/v5.2.0...v5.3.0
2023-04-12 08:45:52 +02:00
Luke Prananta
ae22e0ec5e Introduce FluxCollectToImmutableSet Refaster rule (#571) 2023-04-08 00:41:50 +02:00
Stephan Schroevers
6e6f8d9f7b Introduce SourceCode#unwrapMethodInvocation utility method (#561) 2023-04-07 15:05:11 +02:00
Picnic-Bot
68d0bed36c Upgrade Byte Buddy 1.14.3 -> 1.14.4 (#569)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.14.4
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.3...byte-buddy-1.14.4
2023-04-07 08:43:11 +02:00
Picnic-Bot
2edbc85c28 Upgrade pitest-maven-plugin 1.11.7 -> 1.12.0 (#567)
See:
- https://github.com/hcoles/pitest/releases/tag/1.12.0
- https://github.com/hcoles/pitest/compare/1.11.7...1.12.0
2023-04-06 10:55:01 +02:00
Mohamed Sameh
0fefb6985e Introduce MonoJustOrEmptyOptional Refaster rule (#563)
While there, rename two other rules.
2023-04-05 18:12:33 +02:00
Stephan Schroevers
64f9d6b7a2 Introduce SuggestedFixRules Refaster rule collection (#559) 2023-04-05 11:25:03 +02:00
Picnic-Bot
b0f99e7c0b Upgrade jacoco-maven-plugin 0.8.8 -> 0.8.9 (#565)
See:
- https://github.com/jacoco/jacoco/releases/tag/v0.8.9
- https://github.com/jacoco/jacoco/compare/v0.8.8...v0.8.9
2023-04-05 10:43:22 +02:00
Picnic-Bot
320c4175c9 Upgrade Checker Framework Annotations 3.32.0 -> 3.33.0 (#564)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.33.0
- https://github.com/typetools/checker-framework/compare/checker-framework-3.32.0...checker-framework-3.33.0
2023-04-04 08:09:57 +02:00
216 changed files with 4797 additions and 1091 deletions

View File

@@ -42,9 +42,9 @@ Please replace this sentence with log output, if applicable.
<!-- Please complete the following information: -->
- Operating system (e.g. MacOS Monterey).
- Java version (i.e. `java --version`, e.g. `17.0.6`).
- Java version (i.e. `java --version`, e.g. `17.0.7`).
- Error Prone version (e.g. `2.18.0`).
- Error Prone Support version (e.g. `0.8.0`).
- Error Prone Support version (e.g. `0.9.0`).
### Additional context

View File

@@ -10,34 +10,32 @@ jobs:
strategy:
matrix:
os: [ ubuntu-22.04 ]
jdk: [ 11.0.18, 17.0.6, 19.0.2 ]
jdk: [ 11.0.19, 17.0.7, 20.0.1 ]
distribution: [ temurin ]
experimental: [ false ]
include:
- os: macos-12
jdk: 17.0.6
jdk: 17.0.7
distribution: temurin
experimental: false
- os: windows-2022
jdk: 17.0.6
jdk: 17.0.7
distribution: temurin
experimental: false
- os: ubuntu-22.04
jdk: 20-ea
distribution: zulu
experimental: true
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
steps:
# We run the build twice for each supported JDK: once against the
# original Error Prone release, using only Error Prone checks available
# on Maven Central, and once against the Picnic Error Prone fork,
# additionally enabling all checks defined in this project and any
# Error Prone checks available only from other artifact repositories.
# We run the build twice for each supported JDK: once against the
# original Error Prone release, using only Error Prone checks available
# on Maven Central, and once against the Picnic Error Prone fork,
# 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@v3.1.0
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with:
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@v3.8.0
uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3.12.0
with:
java-version: ${{ matrix.jdk }}
distribution: ${{ matrix.distribution }}
@@ -52,4 +50,3 @@ jobs:
run: mvn build-helper:remove-project-artifact
# XXX: Enable Codecov once we "go public".
# XXX: Enable SonarCloud once we "go public".

44
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
# Analyzes the code using GitHub's default CodeQL query database.
# Identified issues are registered with GitHub's code scanning dashboard. When
# a pull request is analyzed, any offending lines are annotated. See
# https://codeql.github.com for details.
name: CodeQL analysis
on:
pull_request:
push:
branches: [ master ]
schedule:
- cron: '0 4 * * 1'
permissions:
contents: read
jobs:
analyze:
strategy:
matrix:
language: [ java, ruby ]
permissions:
contents: read
security-events: write
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with:
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3.12.0
with:
java-version: 17.0.7
distribution: temurin
cache: maven
- name: Initialize CodeQL
uses: github/codeql-action/init@a09933a12a80f87b87005513f0abb1494c27a716 # v2.21.4
with:
languages: ${{ matrix.language }}
- name: Perform minimal build
if: matrix.language == 'java'
run: mvn -T1C clean install -DskipTests -Dverification.skip
- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@a09933a12a80f87b87005513f0abb1494c27a716 # v2.21.4
with:
category: /language:${{ matrix.language }}

View File

@@ -3,22 +3,24 @@ on:
pull_request:
push:
branches: [ master, website ]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
build:
permissions:
contents: read
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@v3.1.0
- uses: ruby/setup-ruby@v1.126.0
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with:
persist-credentials: false
- uses: ruby/setup-ruby@250fcd6a742febb1123a77a841497ccaa8b9e939 # v1.152.0
with:
working-directory: ./website
bundler-cache: true
- name: Configure Github Pages
uses: actions/configure-pages@v2.1.3
uses: actions/configure-pages@f156874f8191504dae5b037505266ed5dda6c382 # v3.0.6
- name: Generate documentation
run: ./generate-docs.sh
- name: Build website with Jekyll
@@ -30,7 +32,7 @@ jobs:
# "Refaster rules" terminology on our website and in the code.
run: bundle exec htmlproofer --disable_external true --check-external-hash false ./_site
- name: Upload website as artifact
uses: actions/upload-pages-artifact@v1.0.5
uses: actions/upload-pages-artifact@a753861a5debcf57bf8b404356158c8e1e33150c # v2.0.0
with:
path: ./website/_site
deploy:
@@ -46,4 +48,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1.2.3
uses: actions/deploy-pages@9dbe3824824f8a1377b8e298bafde1a50ede43e5 # v2.0.4

36
.github/workflows/openssf-scorecard.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
# Analyzes the code base and GitHub project configuration for adherence to
# security best practices for open source software. Identified issues are
# registered with GitHub's code scanning dashboard. When a pull request is
# analyzed, any offending lines are annotated. See
# https://securityscorecards.dev for details.
name: OpenSSF Scorecard update
on:
pull_request:
push:
branches: [ master ]
schedule:
- cron: '0 4 * * 1'
permissions:
contents: read
jobs:
analyze:
permissions:
contents: read
security-events: write
id-token: write
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with:
persist-credentials: false
- name: Run OpenSSF Scorecard analysis
uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0
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@a09933a12a80f87b87005513f0abb1494c27a716 # v2.21.4
with:
sarif_file: results.sarif

View File

@@ -12,13 +12,14 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@v3.1.0
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with:
fetch-depth: 2
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@v3.8.0
uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3.12.0
with:
java-version: 17.0.6
java-version: 17.0.7
distribution: temurin
cache: maven
- name: Run Pitest
@@ -31,7 +32,7 @@ jobs:
- name: Aggregate Pitest reports
run: mvn pitest-git:aggregate -DkilledEmoji=":tada:" -DmutantEmoji=":zombie:" -DtrailingText="Mutation testing report by [Pitest](https://pitest.org/). Review any surviving mutants by inspecting the line comments under [_Files changed_](${{ github.event.number }}/files)."
- name: Upload Pitest reports as artifact
uses: actions/upload-artifact@v3.1.1
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: pitest-reports
path: ./target/pit-reports-ci

View File

@@ -9,24 +9,28 @@ on:
- completed
permissions:
actions: read
checks: write
contents: read
pull-requests: write
jobs:
update-pr:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
permissions:
actions: read
checks: write
contents: read
pull-requests: write
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@v3.1.0
- name: Set up JDK
uses: actions/setup-java@v3.8.0
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with:
java-version: 17.0.6
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3.12.0
with:
java-version: 17.0.7
distribution: temurin
cache: maven
- name: Download Pitest analysis artifact
uses: dawidd6/action-download-artifact@v2.24.2
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
name: pitest-reports

36
.github/workflows/sonarcloud.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
# Analyzes the code base using SonarCloud. See
# https://sonarcloud.io/project/overview?id=PicnicSupermarket_error-prone-support.
name: SonarCloud analysis
on:
pull_request:
push:
branches: [ master ]
schedule:
- cron: '0 4 * * 1'
permissions:
contents: read
jobs:
analyze:
permissions:
contents: read
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with:
fetch-depth: 0
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3.12.0
with:
java-version: 17.0.7
distribution: temurin
cache: maven
- name: Create missing `test` directory
# XXX: Drop this step in favour of actually having a test.
run: mkdir refaster-compiler/src/test
- name: Perform SonarCloud analysis
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: mvn -T1C jacoco:prepare-agent verify jacoco:report sonar:sonar -Dverification.skip -Dsonar.projectKey=PicnicSupermarket_error-prone-support

View File

@@ -1,5 +1,8 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"helpers:pinGitHubActionDigests"
],
"packageRules": [
{
"matchPackagePatterns": [
@@ -9,10 +12,11 @@
"separateMinorPatch": true
},
{
"matchPackagePatterns": [
"^ruby\\/setup-ruby$"
"matchDepNames": [
"github/codeql-action",
"ruby/setup-ruby"
],
"schedule": "* * 1 * *"
"schedule": "every 4 weeks on Monday"
}
],
"reviewers": [

View File

@@ -48,6 +48,17 @@ be accepted. When in doubt, make sure to first raise an
To the extent possible, the pull request process guards our coding guidelines.
Some pointers:
- Try to make sure that the
[`./run-full-build.sh`][error-prone-support-full-build] script completes
successfully, ideally before opening a pull request. See the [development
instructions][error-prone-support-developing] for details on how to
efficiently resolve many of the errors and warnings that may be reported. (In
particular, make sure to run `mvn fmt:format` and
[`./apply-error-prone-suggestions.sh`][error-prone-support-patch].) That
said, if you feel that the build fails for invalid or debatable reasons, or
if you're unsure how to best resolve an issue, don't let that discourage you
from opening a PR with a failing build; we can have a look at the issue
together!
- Checks should be _topical_: ideally they address a single concern.
- Where possible checks should provide _fixes_, and ideally these are
completely behavior-preserving. In order for a check to be adopted by users
@@ -66,6 +77,9 @@ Some pointers:
sneak in unrelated changes; instead just open more than one pull request 😉.
[error-prone-criteria]: https://errorprone.info/docs/criteria
[error-prone-support-developing]: https://github.com/PicnicSupermarket/error-prone-support/tree/master#-developing-error-prone-support
[error-prone-support-full-build]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/run-full-build.sh
[error-prone-support-issues]: https://github.com/PicnicSupermarket/error-prone-support/issues
[error-prone-support-mutation-tests]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/run-mutation-tests.sh
[error-prone-support-mutation-tests]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/run-branch-mutation-tests.sh
[error-prone-support-patch]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/apply-error-prone-suggestions.sh
[error-prone-support-pulls]: https://github.com/PicnicSupermarket/error-prone-support/pulls

View File

@@ -21,7 +21,18 @@ code_][picnic-blog-ep-post].
[![Maven Central][maven-central-badge]][maven-central-search]
[![Reproducible Builds][reproducible-builds-badge]][reproducible-builds-report]
[![OpenSSF Best Practices][openssf-best-practices-badge]][openssf-best-practices-checklist]
[![OpenSSF Scorecard][openssf-scorecard-badge]][openssf-scorecard-report]
[![CodeQL Analysis][codeql-badge]][codeql-master]
[![GitHub Actions][github-actions-build-badge]][github-actions-build-master]
[![Mutation tested with PIT][pitest-badge]][pitest]
[![Quality Gate Status][sonarcloud-quality-badge]][sonarcloud-quality-master]
[![Maintainability Rating][sonarcloud-maintainability-badge]][sonarcloud-maintainability-master]
[![Reliability Rating][sonarcloud-reliability-badge]][sonarcloud-reliability-master]
[![Security Rating][sonarcloud-security-badge]][sonarcloud-security-master]
[![Coverage][sonarcloud-coverage-badge]][sonarcloud-coverage-master]
[![Duplicated Lines (%)][sonarcloud-duplication-badge]][sonarcloud-duplication-master]
[![Technical Debt][sonarcloud-technical-debt-badge]][sonarcloud-technical-debt-master]
[![License][license-badge]][license]
[![PRs Welcome][pr-badge]][contributing]
@@ -98,8 +109,9 @@ definition. -->
#### Gradle
1. First, follow the [installation guide]
[error-prone-gradle-installation-guide] of the `gradle-errorprone-plugin`.
1. First, follow the [installation
guide][error-prone-gradle-installation-guide] of the
`gradle-errorprone-plugin`.
2. Next, edit your `build.gradle` file to add one or more Error Prone Support
modules:
@@ -171,8 +183,15 @@ rules][refaster-rules].
## 👷 Developing Error Prone Support
This is a [Maven][maven] project, so running `mvn clean install` performs a
full clean build and installs the library to your local Maven repository. Some
relevant flags:
full clean build and installs the library to your local Maven repository.
Once you've made changes, the build may fail due to a warning or error emitted
by static code analysis. The flags and commands listed below allow you to
suppress or (in a large subset of cases) automatically fix such cases. Make
sure to carefully check the available options, as this can save you significant
amounts of development time!
Relevant Maven build parameters:
- `-Dverification.warn` makes the warnings and errors emitted by various
plugins and the Java compiler non-fatal, where possible.
@@ -189,19 +208,28 @@ relevant flags:
Pending a release of [google/error-prone#3301][error-prone-pull-3301], this
flag must currently be used in combination with `-Perror-prone-fork`.
Some other commands one may find relevant:
Other highly relevant commands:
- `mvn fmt:format` formats the code using
[`google-java-format`][google-java-format].
- `./run-mutation-tests.sh` runs mutation tests using [Pitest][pitest]. The
results can be reviewed by opening the respective
`target/pit-reports/index.html` files. For more information check the [PIT
Maven plugin][pitest-maven].
- `./apply-error-prone-suggestions.sh` applies Error Prone and Error Prone
Support code suggestions to this project. Before running this command, make
sure to have installed the project (`mvn clean install`) and make sure that
the current working directory does not contain unstaged or uncommited
changes.
- [`./run-full-build.sh`][script-run-full-build] builds the project twice,
where the second pass validates compatbility with Picnic's [Error Prone
fork][error-prone-fork-repo] and compliance of the code with any rules
defined within this project. (Consider running this before [opening a pull
request][contributing-pull-request], as the PR checks also perform this
validation.)
- [`./apply-error-prone-suggestions.sh`][script-apply-error-prone-suggestions]
applies Error Prone and Error Prone Support code suggestions to this project.
Before running this command, make sure to have installed the project (`mvn
clean install`) and make sure that the current working directory does not
contain unstaged or uncommited changes.
- [`./run-branch-mutation-tests.sh`][script-run-branch-mutation-tests] uses
[Pitest][pitest] to run mutation tests against code that is modified relative
to the upstream default branch. The results can be reviewed by opening the
respective `target/pit-reports/index.html` files. One can use
[`./run-mutation-tests.sh`][script-run-mutation-tests] to run mutation tests
against _all_ code in the current working directory. For more information
check the [PIT Maven plugin][pitest-maven].
When running the project's tests in IntelliJ IDEA, you might see the following
error:
@@ -228,9 +256,17 @@ Want to report or fix a bug, suggest or add a new feature, or improve the
documentation? That's awesome! Please read our [contribution
guidelines][contributing].
### Security
If you want to report a security vulnerability, please do so through a private
channel; please see our [security policy][security] for details.
[bug-checks]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/
[bug-checks-identity-conversion]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IdentityConversion.java
[codeql-badge]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/codeql.yml/badge.svg?branch=master&event=push
[codeql-master]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/codeql.yml?query=branch:master+event:push
[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
[error-prone-fork-jitpack]: https://jitpack.io/#PicnicSupermarket/error-prone
[error-prone-fork-repo]: https://github.com/PicnicSupermarket/error-prone
@@ -239,7 +275,7 @@ guidelines][contributing].
[error-prone-orig-repo]: https://github.com/google/error-prone
[error-prone-pull-3301]: https://github.com/google/error-prone/pull/3301
[github-actions-build-badge]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/build.yaml/badge.svg
[github-actions-build-master]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/build.yaml?query=branch%3Amaster
[github-actions-build-master]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/build.yaml?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
@@ -247,8 +283,13 @@ guidelines][contributing].
[maven-central-badge]: https://img.shields.io/maven-central/v/tech.picnic.error-prone-support/error-prone-support?color=blue
[maven-central-search]: https://search.maven.org/artifact/tech.picnic.error-prone-support/error-prone-support
[maven]: https://maven.apache.org
[picnic-blog]: https://blog.picnic.nl
[openssf-best-practices-badge]: https://bestpractices.coreinfrastructure.org/projects/7199/badge
[openssf-best-practices-checklist]: https://bestpractices.coreinfrastructure.org/projects/7199
[openssf-scorecard-badge]: https://img.shields.io/ossf-scorecard/github.com/PicnicSupermarket/error-prone-support?label=openssf%20scorecard
[openssf-scorecard-report]: https://securityscorecards.dev/viewer/?uri=github.com/PicnicSupermarket/error-prone-support
[picnic-blog-ep-post]: https://blog.picnic.nl/picnic-loves-error-prone-producing-high-quality-and-consistent-java-code-b8a566be6886
[picnic-blog]: https://blog.picnic.nl
[pitest-badge]: https://img.shields.io/badge/-Mutation%20tested%20with%20PIT-blue.svg
[pitest]: https://pitest.org
[pitest-maven]: https://pitest.org/quickstart/maven
[pr-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
@@ -257,3 +298,22 @@ guidelines][contributing].
[refaster-rules]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/
[reproducible-builds-badge]: https://img.shields.io/badge/Reproducible_Builds-ok-success?labelColor=1e5b96
[reproducible-builds-report]: https://github.com/jvm-repo-rebuild/reproducible-central/blob/master/content/tech/picnic/error-prone-support/error-prone-support/README.md
[script-apply-error-prone-suggestions]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/apply-error-prone-suggestions.sh
[script-run-branch-mutation-tests]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/run-branch-mutation-tests.sh
[script-run-full-build]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/run-full-build.sh
[script-run-mutation-tests]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/run-mutation-tests.sh
[security]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/SECURITY.md
[sonarcloud-coverage-badge]: https://sonarcloud.io/api/project_badges/measure?project=PicnicSupermarket_error-prone-support&metric=coverage
[sonarcloud-coverage-master]: https://sonarcloud.io/component_measures?id=PicnicSupermarket_error-prone-support&metric=coverage
[sonarcloud-duplication-badge]: https://sonarcloud.io/api/project_badges/measure?project=PicnicSupermarket_error-prone-support&metric=duplicated_lines_density
[sonarcloud-duplication-master]: https://sonarcloud.io/component_measures?id=PicnicSupermarket_error-prone-support&metric=duplicated_lines_density
[sonarcloud-maintainability-badge]: https://sonarcloud.io/api/project_badges/measure?project=PicnicSupermarket_error-prone-support&metric=sqale_rating
[sonarcloud-maintainability-master]: https://sonarcloud.io/component_measures?id=PicnicSupermarket_error-prone-support&metric=sqale_rating
[sonarcloud-quality-badge]: https://sonarcloud.io/api/project_badges/measure?project=PicnicSupermarket_error-prone-support&metric=alert_status
[sonarcloud-quality-master]: https://sonarcloud.io/summary/new_code?id=PicnicSupermarket_error-prone-support
[sonarcloud-reliability-badge]: https://sonarcloud.io/api/project_badges/measure?project=PicnicSupermarket_error-prone-support&metric=reliability_rating
[sonarcloud-reliability-master]: https://sonarcloud.io/component_measures?id=PicnicSupermarket_error-prone-support&metric=reliability_rating
[sonarcloud-security-badge]: https://sonarcloud.io/api/project_badges/measure?project=PicnicSupermarket_error-prone-support&metric=security_rating
[sonarcloud-security-master]: https://sonarcloud.io/component_measures?id=PicnicSupermarket_error-prone-support&metric=security_rating
[sonarcloud-technical-debt-badge]: https://sonarcloud.io/api/project_badges/measure?project=PicnicSupermarket_error-prone-support&metric=sqale_index
[sonarcloud-technical-debt-master]: https://sonarcloud.io/component_measures?id=PicnicSupermarket_error-prone-support&metric=sqale_index

23
SECURITY.md Normal file
View File

@@ -0,0 +1,23 @@
# Security policy
We take security seriously. We are mindful of Error Prone Support's place in
the software supply chain, and the risks and responsibilities that come with
this.
## Supported versions
This project uses [semantic versioning][semantic-versioning]. In general, only
the latest version of this software is supported. That said, if users have a
compelling reason to ask for patch release of an older major release, then we
will seriously consider such a request. We do urge users to stay up-to-date and
use the latest release where feasible.
## Reporting a vulnerability
To report a vulnerability, please visit the [security
advisories][security-advisories] page and click _Report a vulnerability_. We
will take such reports seriously and work with you to resolve the issue in a
timely manner.
[security-advisories]: https://github.com/PicnicSupermarket/error-prone-support/security/advisories
[semantic-versioning]: https://semver.org

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.9.1-SNAPSHOT</version>
<version>0.13.1-SNAPSHOT</version>
</parent>
<artifactId>documentation-support</artifactId>

View File

@@ -25,6 +25,7 @@ enum ExtractorType {
return identifier;
}
@SuppressWarnings("java:S1452" /* The extractor returns data of an unspecified type. */)
Extractor<?> getExtractor() {
return extractor;
}

View File

@@ -67,10 +67,11 @@ final class DocumentationGeneratorTaskListenerTest {
@Test
void excessArguments(@TempDir Path outputDirectory) {
String actualOutputDirectory = outputDirectory.toAbsolutePath() + " extra-arg";
assertThatThrownBy(
() ->
Compilation.compileWithDocumentationGenerator(
outputDirectory.toAbsolutePath() + " extra-arg", "A.java", "package pkg;"))
actualOutputDirectory, "A.java", "package pkg;"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Precisely one path must be provided");
}

View File

@@ -24,7 +24,6 @@ project:
- Document how to apply patches.
- Document each of the checks.
- Add [SonarQube][sonarcloud] and [Codecov][codecov] integrations.
- Add non-Java file formatting support, like we have internally at Picnic.
(I.e., somehow open-source that stuff.)
- Auto-generate a website listing each of the checks, just like the Error Prone
@@ -273,7 +272,6 @@ Refaster's expressiveness:
[autorefactor]: https://autorefactor.org
[bettercodehub]: https://bettercodehub.com
[checkstyle-external-project-tests]: https://github.com/checkstyle/checkstyle/blob/master/wercker.yml
[codecov]: https://codecov.io
[error-prone-bug-patterns]: https://errorprone.info/bugpatterns
[error-prone]: https://errorprone.info
[error-prone-repo]: https://github.com/google/error-prone
@@ -283,4 +281,3 @@ Refaster's expressiveness:
[main-contributing]: ../CONTRIBUTING.md
[main-readme]: ../README.md
[modernizer-maven-plugin]: https://github.com/gaul/modernizer-maven-plugin
[sonarcloud]: https://sonarcloud.io

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.9.1-SNAPSHOT</version>
<version>0.13.1-SNAPSHOT</version>
</parent>
<artifactId>error-prone-contrib</artifactId>
@@ -126,6 +126,11 @@
<artifactId>jakarta.servlet-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
@@ -171,6 +176,11 @@
<artifactId>mockito-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams</artifactId>

View File

@@ -0,0 +1,103 @@
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.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.not;
import static com.google.errorprone.matchers.Matchers.staticMethod;
import static com.google.errorprone.matchers.Matchers.toType;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.fixes.SuggestedFixes;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.suppliers.Supplier;
import com.google.errorprone.suppliers.Suppliers;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.code.Type;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
/**
* A {@link BugChecker} that flags unnecessarily nested usage of methods that implement an
* associative operation.
*
* <p>The arguments to such methods can be flattened without affecting semantics, while making the
* code more readable.
*/
@AutoService(BugChecker.class)
@BugPattern(
summary =
"This method implements an associative operation, so the list of operands can be flattened",
link = BUG_PATTERNS_BASE_URL + "AssociativeMethodInvocation",
linkType = CUSTOM,
severity = SUGGESTION,
tags = SIMPLIFICATION)
public final class AssociativeMethodInvocation extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Supplier<Type> ITERABLE = Suppliers.typeFromClass(Iterable.class);
private static final ImmutableSet<Matcher<ExpressionTree>> ASSOCIATIVE_OPERATIONS =
ImmutableSet.of(
allOf(
staticMethod().onClass(Suppliers.typeFromClass(Matchers.class)).named("allOf"),
toType(MethodInvocationTree.class, not(hasArgumentOfType(ITERABLE)))),
allOf(
staticMethod().onClass(Suppliers.typeFromClass(Matchers.class)).named("anyOf"),
toType(MethodInvocationTree.class, not(hasArgumentOfType(ITERABLE)))),
staticMethod().onClass(Suppliers.typeFromClass(Refaster.class)).named("anyOf"));
/** Instantiates a new {@link AssociativeMethodInvocation} instance. */
public AssociativeMethodInvocation() {}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (tree.getArguments().isEmpty()) {
/* Absent any arguments, there is nothing to simplify. */
return Description.NO_MATCH;
}
for (Matcher<ExpressionTree> matcher : ASSOCIATIVE_OPERATIONS) {
if (matcher.matches(tree, state)) {
SuggestedFix fix = processMatchingArguments(tree, matcher, state);
return fix.isEmpty() ? Description.NO_MATCH : describeMatch(tree, fix);
}
}
return Description.NO_MATCH;
}
private static SuggestedFix processMatchingArguments(
MethodInvocationTree tree, Matcher<ExpressionTree> matcher, VisitorState state) {
SuggestedFix.Builder fix = SuggestedFix.builder();
for (ExpressionTree arg : tree.getArguments()) {
if (matcher.matches(arg, state)) {
MethodInvocationTree invocation = (MethodInvocationTree) arg;
fix.merge(
invocation.getArguments().isEmpty()
? SuggestedFixes.removeElement(invocation, tree.getArguments(), state)
: SourceCode.unwrapMethodInvocation(invocation, state));
}
}
return fix.build();
}
private static Matcher<MethodInvocationTree> hasArgumentOfType(Supplier<Type> type) {
return (tree, state) ->
tree.getArguments().stream()
.anyMatch(arg -> ASTHelpers.isSubtype(ASTHelpers.getType(arg), type.get(state), state));
}
}

View File

@@ -58,8 +58,8 @@ public final class AutowiredConstructor extends BugChecker implements ClassTreeM
/*
* This is the only `@Autowired` constructor: suggest that it be removed. Note that this likely
* 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.
* 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);
return describeMatch(annotation, SourceCode.deleteWithTrailingWhitespace(annotation, state));

View File

@@ -91,8 +91,8 @@ public final class CanonicalAnnotationSyntax extends BugChecker implements Annot
ExpressionTree arg = args.get(0);
if (state.getSourceForNode(arg) == null) {
/*
* The annotation argument isn't doesn't have a source representation, e.g. because `value`
* isn't assigned to explicitly.
* The annotation argument doesn't have a source representation, e.g. because `value` isn't
* assigned explicitly.
*/
return Optional.empty();
}

View File

@@ -30,7 +30,7 @@ import tech.picnic.errorprone.bugpatterns.util.ThirdPartyLibrary;
@AutoService(BugChecker.class)
@BugPattern(
summary =
"Avoid `Collectors.to{List,Map,Set}` in favour of alternatives that emphasize (im)mutability",
"Avoid `Collectors.to{List,Map,Set}` in favor of collectors that emphasize (im)mutability",
link = BUG_PATTERNS_BASE_URL + "CollectorMutability",
linkType = CUSTOM,
severity = WARNING,

View File

@@ -14,6 +14,7 @@ import static com.google.errorprone.matchers.Matchers.toType;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.common.collect.Streams;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
@@ -26,14 +27,18 @@ import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.ExpressionStatementTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.ReturnTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TryTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreeScanner;
import com.sun.tools.javac.code.Symbol;
import java.util.List;
import java.util.Optional;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.bugpatterns.util.MoreASTHelpers;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
@@ -75,7 +80,10 @@ public final class DirectReturn extends BugChecker implements BlockTreeMatcher {
StatementTree precedingStatement = statements.get(statements.size() - 2);
return tryMatchAssignment(variableSymbol, precedingStatement)
.filter(resultExpr -> canInlineToReturnStatement(resultExpr, state))
.filter(
resultExpr ->
canInlineToReturnStatement(resultExpr, state)
&& !isIdentifierSymbolReferencedInAssociatedFinallyBlock(variableSymbol, state))
.map(
resultExpr ->
describeMatch(
@@ -113,13 +121,13 @@ public final class DirectReturn extends BugChecker implements BlockTreeMatcher {
}
/**
* Tells whether inlining the given expression to the associated return statement can be done
* safely.
* Tells whether inlining the given expression to the associated return statement can likely be
* done without changing the expression's return type.
*
* <p>Inlining is generally safe, but in rare cases the operation may have a functional impact.
* The sole case considered here is the inlining of a Mockito mock or spy construction without an
* explicit type. In such a case the type created depends on context, such as the method's return
* type.
* <p>Inlining an expression generally does not change its return type, but in rare cases the
* operation may have a functional impact. The sole case considered here is the inlining of a
* Mockito mock or spy construction without an explicit type. In such a case the type created
* depends on context, such as the method's return type.
*/
private static boolean canInlineToReturnStatement(
ExpressionTree expressionTree, VisitorState state) {
@@ -128,4 +136,40 @@ public final class DirectReturn extends BugChecker implements BlockTreeMatcher {
.filter(m -> MoreASTHelpers.areSameType(expressionTree, m.getReturnType(), state))
.isPresent();
}
/**
* Tells whether the given identifier {@link Symbol} is referenced in a {@code finally} block that
* is executed <em>after</em> control flow returns from the {@link VisitorState#getPath() current
* location}.
*/
private static boolean isIdentifierSymbolReferencedInAssociatedFinallyBlock(
Symbol symbol, VisitorState state) {
return Streams.zip(
Streams.stream(state.getPath()).skip(1),
Streams.stream(state.getPath()),
(tree, child) -> {
if (!(tree instanceof TryTree)) {
return null;
}
BlockTree finallyBlock = ((TryTree) tree).getFinallyBlock();
return !child.equals(finallyBlock) ? finallyBlock : null;
})
.anyMatch(finallyBlock -> referencesIdentifierSymbol(symbol, finallyBlock));
}
private static boolean referencesIdentifierSymbol(Symbol symbol, @Nullable BlockTree tree) {
return Boolean.TRUE.equals(
new TreeScanner<Boolean, @Nullable Void>() {
@Override
public Boolean visitIdentifier(IdentifierTree node, @Nullable Void unused) {
return symbol.equals(ASTHelpers.getSymbol(node));
}
@Override
public Boolean reduce(Boolean r1, Boolean r2) {
return Boolean.TRUE.equals(r1) || Boolean.TRUE.equals(r2);
}
}.scan(tree, null));
}
}

View File

@@ -42,6 +42,7 @@ public final class EmptyMethod extends BugChecker implements MethodTreeMatcher {
public EmptyMethod() {}
@Override
@SuppressWarnings("java:S1067" /* Chaining disjunctions like this does not impact readability. */)
public Description matchMethod(MethodTree tree, VisitorState state) {
if (tree.getBody() == null
|| !tree.getBody().getStatements().isEmpty()

View File

@@ -104,7 +104,9 @@ public final class ErrorProneTestHelperSourceFormat extends BugChecker
String formatted;
try {
formatted = formatSourceCode(source, retainUnusedImports).trim();
} catch (FormatterException e) {
} catch (
@SuppressWarnings("java:S1166" /* Stack trace not relevant. */)
FormatterException e) {
return buildDescription(methodInvocation)
.setMessage(String.format("Source code is malformed: %s", e.getMessage()))
.build();
@@ -131,7 +133,7 @@ public final class ErrorProneTestHelperSourceFormat extends BugChecker
SuggestedFix.replace(
startPos,
endPos,
Splitter.on('\n')
Splitter.on(System.lineSeparator())
.splitToStream(formatted)
.map(state::getConstantExpression)
.collect(joining(", "))));
@@ -157,7 +159,7 @@ public final class ErrorProneTestHelperSourceFormat extends BugChecker
return Optional.empty();
}
source.append(value).append('\n');
source.append(value).append(System.lineSeparator());
}
return Optional.of(source.toString());

View File

@@ -82,10 +82,8 @@ public final class FluxFlatMapUsage extends BugChecker
SuggestedFix serializationFix = SuggestedFixes.renameMethodInvocation(tree, "concatMap", state);
SuggestedFix concurrencyCapFix =
SuggestedFix.builder()
.postfixWith(
Iterables.getOnlyElement(tree.getArguments()), ", " + MAX_CONCURRENCY_ARG_NAME)
.build();
SuggestedFix.postfixWith(
Iterables.getOnlyElement(tree.getArguments()), ", " + MAX_CONCURRENCY_ARG_NAME);
Description.Builder description = buildDescription(tree);

View File

@@ -35,6 +35,8 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
// XXX: Consider detecting cases where a flagged expression is passed to a method, and where removal
// of the identity conversion would cause a different method overload to be selected. Depending on
// 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.
@AutoService(BugChecker.class)
@BugPattern(
summary = "Avoid or clarify identity conversions",

View File

@@ -40,6 +40,7 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import javax.inject.Inject;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.bugpatterns.util.AnnotationAttributeMatcher;
import tech.picnic.errorprone.bugpatterns.util.Flags;
@@ -58,6 +59,7 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
linkType = CUSTOM,
severity = SUGGESTION,
tags = STYLE)
@SuppressWarnings("java:S2160" /* Super class equality definition suffices. */)
public final class LexicographicalAnnotationAttributeListing extends BugChecker
implements AnnotationTreeMatcher {
private static final long serialVersionUID = 1L;
@@ -93,7 +95,8 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
*
* @param flags Any provided command line flags.
*/
public LexicographicalAnnotationAttributeListing(ErrorProneFlags flags) {
@Inject
LexicographicalAnnotationAttributeListing(ErrorProneFlags flags) {
matcher = createAnnotationAttributeMatcher(flags);
}

View File

@@ -46,9 +46,19 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
public final class LexicographicalAnnotationListing extends BugChecker
implements MethodTreeMatcher {
private static final long serialVersionUID = 1L;
/**
* A comparator that minimally reorders {@link AnnotationType}s, such that declaration annotations
* are placed before type annotations.
*/
@SuppressWarnings({
"java:S1067",
"java:S3358"
} /* Avoiding the nested ternary operator hurts readability. */)
private static final Comparator<@Nullable AnnotationType> BY_ANNOTATION_TYPE =
(a, b) ->
(a == null || a == DECLARATION) && b == TYPE ? -1 : a == TYPE && b == DECLARATION ? 1 : 0;
(a == null || a == DECLARATION) && b == TYPE
? -1
: (a == TYPE && b == DECLARATION ? 1 : 0);
/** Instantiates a new {@link LexicographicalAnnotationListing} instance. */
public LexicographicalAnnotationListing() {}

View File

@@ -118,6 +118,8 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
.flatMap(expectedInstance -> constructMethodRef(lambdaExpr, subTree, expectedInstance));
}
@SuppressWarnings(
"java:S1151" /* Extracting `IDENTIFIER` case block to separate method does not improve readability. */)
private static Optional<SuggestedFix.Builder> constructMethodRef(
LambdaExpressionTree lambdaExpr,
MethodInvocationTree subTree,
@@ -130,10 +132,9 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
return Optional.empty();
}
Symbol sym = ASTHelpers.getSymbol(methodSelect);
if (!ASTHelpers.isStatic(sym)) {
return constructFix(lambdaExpr, "this", methodSelect);
}
return constructFix(lambdaExpr, sym.owner, methodSelect);
return ASTHelpers.isStatic(sym)
? constructFix(lambdaExpr, sym.owner, methodSelect)
: constructFix(lambdaExpr, "this", methodSelect);
case MEMBER_SELECT:
return constructMethodRef(lambdaExpr, (MemberSelectTree) methodSelect, expectedInstance);
default:

View File

@@ -1,59 +0,0 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.BugPattern.StandardTags.LIKELY_ERROR;
import static com.google.errorprone.matchers.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
import static com.google.errorprone.matchers.Matchers.annotations;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.isType;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
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.google.errorprone.matchers.MultiMatcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
/** A {@link BugChecker} that flags likely missing Refaster annotations. */
@AutoService(BugChecker.class)
@BugPattern(
summary = "The Refaster rule contains a method without any Refaster annotations",
link = BUG_PATTERNS_BASE_URL + "MissingRefasterAnnotation",
linkType = CUSTOM,
severity = WARNING,
tags = LIKELY_ERROR)
public final class MissingRefasterAnnotation extends BugChecker implements ClassTreeMatcher {
private static final long serialVersionUID = 1L;
private static final MultiMatcher<Tree, AnnotationTree> REFASTER_ANNOTATION =
annotations(
AT_LEAST_ONE,
anyOf(
isType("com.google.errorprone.refaster.annotation.Placeholder"),
isType("com.google.errorprone.refaster.annotation.BeforeTemplate"),
isType("com.google.errorprone.refaster.annotation.AfterTemplate")));
/** Instantiates a new {@link MissingRefasterAnnotation} instance. */
public MissingRefasterAnnotation() {}
@Override
public Description matchClass(ClassTree tree, VisitorState state) {
long methodTypes =
tree.getMembers().stream()
.filter(member -> member.getKind() == Tree.Kind.METHOD)
.map(MethodTree.class::cast)
.filter(method -> !ASTHelpers.isGeneratedConstructor(method))
.map(method -> REFASTER_ANNOTATION.matches(method, state))
.distinct()
.count();
return methodTypes < 2 ? Description.NO_MATCH : buildDescription(tree).build();
}
}

View File

@@ -0,0 +1,47 @@
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.PERFORMANCE;
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
/**
* A {@link BugChecker} that flags usages of MongoDB {@code $text} filter usages.
*
* @see <a href="https://www.mongodb.com/docs/manual/text-search/">MongoDB Text Search</a>
*/
@AutoService(BugChecker.class)
@BugPattern(
summary =
"Avoid MongoDB's `$text` filter operator, as it can trigger heavy queries and even cause the server to run out of memory",
link = BUG_PATTERNS_BASE_URL + "MongoDBTextFilterUsage",
linkType = CUSTOM,
severity = SUGGESTION,
tags = PERFORMANCE)
public final class MongoDBTextFilterUsage extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> MONGO_FILTERS_TEXT_METHOD =
staticMethod().onClass("com.mongodb.client.model.Filters").named("text");
/** Instantiates a new {@link MongoDBTextFilterUsage} instance. */
public MongoDBTextFilterUsage() {}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
return MONGO_FILTERS_TEXT_METHOD.matches(tree, state)
? describeMatch(tree)
: Description.NO_MATCH;
}
}

View File

@@ -4,6 +4,7 @@ import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.BugPattern.StandardTags.FRAGILE_CODE;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import static tech.picnic.errorprone.bugpatterns.util.MoreMatchers.isSubTypeOf;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.generic;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.raw;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.subOf;
@@ -14,14 +15,19 @@ import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.suppliers.Supplier;
import com.google.errorprone.suppliers.Suppliers;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Type;
import java.util.Optional;
/** A {@link BugChecker} that flags nesting of {@link Optional Optionals}. */
// XXX: Extend this checker to also flag method return types and variable/field types.
// XXX: Consider generalizing this checker and `NestedPublishers` to a single `NestedMonad` check,
// which e.g. also flags nested `Stream`s. Alternatively, combine these and other checkers into an
// even more generic `ConfusingType` checker.
@AutoService(BugChecker.class)
@BugPattern(
summary =
@@ -33,19 +39,16 @@ import java.util.Optional;
public final class NestedOptionals extends BugChecker implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Supplier<Type> OPTIONAL = Suppliers.typeFromClass(Optional.class);
private static final Supplier<Type> OPTIONAL_OF_OPTIONAL =
VisitorState.memoize(generic(OPTIONAL, subOf(raw(OPTIONAL))));
private static final Matcher<Tree> IS_OPTIONAL_OF_OPTIONAL =
isSubTypeOf(VisitorState.memoize(generic(OPTIONAL, subOf(raw(OPTIONAL)))));
/** Instantiates a new {@link NestedOptionals} instance. */
public NestedOptionals() {}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
Type type = OPTIONAL_OF_OPTIONAL.get(state);
if (type == null || !state.getTypes().isSubtype(ASTHelpers.getType(tree), type)) {
return Description.NO_MATCH;
}
return describeMatch(tree);
return IS_OPTIONAL_OF_OPTIONAL.matches(tree, state)
? describeMatch(tree)
: Description.NO_MATCH;
}
}

View File

@@ -0,0 +1,63 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.BugPattern.StandardTags.FRAGILE_CODE;
import static com.google.errorprone.matchers.Matchers.typePredicateMatcher;
import static com.google.errorprone.predicates.TypePredicates.allOf;
import static com.google.errorprone.predicates.TypePredicates.not;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypePredicates.hasTypeParameter;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypePredicates.isSubTypeOf;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.generic;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.raw;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.subOf;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.type;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.suppliers.Supplier;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.code.Type;
/**
* A {@link BugChecker} that flags {@code Publisher<? extends Publisher>} instances, unless the
* nested {@link org.reactivestreams.Publisher} is a {@link reactor.core.publisher.GroupedFlux}.
*/
// XXX: See the `NestedOptionals` check for some ideas on how to generalize this kind of checker.
@AutoService(BugChecker.class)
@BugPattern(
summary =
"Avoid `Publisher`s that emit other `Publishers`s; "
+ "the resultant code is hard to reason about",
link = BUG_PATTERNS_BASE_URL + "NestedPublishers",
linkType = CUSTOM,
severity = WARNING,
tags = FRAGILE_CODE)
public final class NestedPublishers extends BugChecker implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Supplier<Type> PUBLISHER = type("org.reactivestreams.Publisher");
private static final Matcher<ExpressionTree> IS_NON_GROUPED_PUBLISHER_OF_PUBLISHERS =
typePredicateMatcher(
allOf(
isSubTypeOf(generic(PUBLISHER, subOf(raw(PUBLISHER)))),
not(
hasTypeParameter(
0, isSubTypeOf(raw(type("reactor.core.publisher.GroupedFlux")))))));
/** Instantiates a new {@link NestedPublishers} instance. */
public NestedPublishers() {}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
return IS_NON_GROUPED_PUBLISHER_OF_PUBLISHERS.matches(tree, state)
? describeMatch(tree)
: Description.NO_MATCH;
}
}

View File

@@ -43,6 +43,7 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
// emitting a value (e.g. `Mono.empty()`, `someFlux.then()`, ...), or known not to complete normally
// (`Mono.never()`, `someFlux.repeat()`, `Mono.error(...)`, ...). The latter category could
// potentially be split out further.
@SuppressWarnings("java:S1192" /* Factoring out repeated method names impacts readability. */)
public final class NonEmptyMono extends BugChecker implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> MONO_SIZE_CHECK =

View File

@@ -49,6 +49,7 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
linkType = CUSTOM,
severity = WARNING,
tags = PERFORMANCE)
@SuppressWarnings("java:S1192" /* Factoring out repeated method names impacts readability. */)
public final class PrimitiveComparison extends BugChecker implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> STATIC_COMPARISON_METHOD =

View File

@@ -50,6 +50,7 @@ import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import javax.inject.Inject;
import tech.picnic.errorprone.bugpatterns.util.Flags;
import tech.picnic.errorprone.bugpatterns.util.MethodMatcherFactory;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
@@ -62,6 +63,11 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
linkType = CUSTOM,
severity = SUGGESTION,
tags = SIMPLIFICATION)
@SuppressWarnings({
"java:S1192" /* Factoring out repeated method names impacts readability. */,
"java:S2160" /* Super class equality definition suffices. */,
"key-to-resolve-AnnotationUseStyle-and-TrailingComment-check-conflict"
})
public final class RedundantStringConversion extends BugChecker
implements BinaryTreeMatcher, CompoundAssignmentTreeMatcher, MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
@@ -150,7 +156,8 @@ public final class RedundantStringConversion extends BugChecker
*
* @param flags Any provided command line flags.
*/
public RedundantStringConversion(ErrorProneFlags flags) {
@Inject
RedundantStringConversion(ErrorProneFlags flags) {
conversionMethodMatcher = createConversionMethodMatcher(flags);
}
@@ -163,7 +170,7 @@ public final class RedundantStringConversion extends BugChecker
ExpressionTree lhs = tree.getLeftOperand();
ExpressionTree rhs = tree.getRightOperand();
if (!STRING.matches(lhs, state)) {
return finalize(tree, tryFix(rhs, state, STRING));
return createDescription(tree, tryFix(rhs, state, STRING));
}
List<SuggestedFix.Builder> fixes = new ArrayList<>();
@@ -177,7 +184,7 @@ public final class RedundantStringConversion extends BugChecker
}
tryFix(rhs, state, ANY_EXPR).ifPresent(fixes::add);
return finalize(tree, fixes.stream().reduce(SuggestedFix.Builder::merge));
return createDescription(tree, fixes.stream().reduce(SuggestedFix.Builder::merge));
}
@Override
@@ -186,36 +193,36 @@ public final class RedundantStringConversion extends BugChecker
return Description.NO_MATCH;
}
return finalize(tree, tryFix(tree.getExpression(), state, ANY_EXPR));
return createDescription(tree, tryFix(tree.getExpression(), state, ANY_EXPR));
}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (STRINGBUILDER_APPEND_INVOCATION.matches(tree, state)) {
return finalize(tree, tryFixPositionalConverter(tree.getArguments(), state, 0));
return createDescription(tree, tryFixPositionalConverter(tree.getArguments(), state, 0));
}
if (STRINGBUILDER_INSERT_INVOCATION.matches(tree, state)) {
return finalize(tree, tryFixPositionalConverter(tree.getArguments(), state, 1));
return createDescription(tree, tryFixPositionalConverter(tree.getArguments(), state, 1));
}
if (FORMATTER_INVOCATION.matches(tree, state)) {
return finalize(tree, tryFixFormatter(tree.getArguments(), state));
return createDescription(tree, tryFixFormatter(tree.getArguments(), state));
}
if (GUAVA_GUARD_INVOCATION.matches(tree, state)) {
return finalize(tree, tryFixGuavaGuard(tree.getArguments(), state));
return createDescription(tree, tryFixGuavaGuard(tree.getArguments(), state));
}
if (SLF4J_LOGGER_INVOCATION.matches(tree, state)) {
return finalize(tree, tryFixSlf4jLogger(tree.getArguments(), state));
return createDescription(tree, tryFixSlf4jLogger(tree.getArguments(), state));
}
if (instanceMethod().matches(tree, state)) {
return finalize(tree, tryFix(tree, state, STRING));
return createDescription(tree, tryFix(tree, state, STRING));
}
return finalize(tree, tryFix(tree, state, NON_NULL_STRING));
return createDescription(tree, tryFix(tree, state, NON_NULL_STRING));
}
private Optional<SuggestedFix.Builder> tryFixPositionalConverter(
@@ -298,7 +305,7 @@ public final class RedundantStringConversion extends BugChecker
/* Simplify the values to be plugged into the format pattern, if possible. */
return arguments.stream()
.skip(patternIndex + 1)
.skip(patternIndex + 1L)
.map(arg -> tryFix(arg, state, remainingArgFilter))
.flatMap(Optional::stream)
.reduce(SuggestedFix.Builder::merge);
@@ -362,7 +369,7 @@ public final class RedundantStringConversion extends BugChecker
return Optional.of(Iterables.getOnlyElement(methodInvocation.getArguments()));
}
private Description finalize(Tree tree, Optional<SuggestedFix.Builder> fixes) {
private Description createDescription(Tree tree, Optional<SuggestedFix.Builder> fixes) {
return fixes
.map(SuggestedFix.Builder::build)
.map(fix -> describeMatch(tree, fix))

View File

@@ -0,0 +1,163 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.BugPattern.StandardTags.LIKELY_ERROR;
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ReturnTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.TreeScanner;
import com.sun.tools.javac.code.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.jspecify.annotations.Nullable;
/**
* A {@link BugChecker} that flags Refaster expression templates for which the
* {@code @AfterTemplate} return type is not a subtype of the `@BeforeTemplate` return type.
*/
@AutoService(BugChecker.class)
@BugPattern(
summary = "Refaster templates should have invariant return types",
link = BUG_PATTERNS_BASE_URL + "RefasterExpressionTypeInvariance",
linkType = CUSTOM,
severity = WARNING,
tags = LIKELY_ERROR)
public final class RefasterExpressionTypeInvariance extends BugChecker implements ClassTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<Tree> BEFORE_TEMPLATE_METHOD = hasAnnotation(BeforeTemplate.class);
private static final Matcher<Tree> AFTER_TEMPLATE_METHOD = hasAnnotation(AfterTemplate.class);
private static final Matcher<ExpressionTree> REFASTER_ANY_OF =
staticMethod().onClass(Refaster.class.getName()).named("anyOf");
// XXX: False positive in `OptionalMap`. First write a rule to simplify such case.
@Override
public Description matchClass(ClassTree tree, VisitorState state) {
Optional<Type> afterTemplateResultType = getAfterTemplateResultType(tree, state);
if (afterTemplateResultType.isEmpty()) {
return Description.NO_MATCH;
}
ImmutableSet<Type> beforeTemplateResultTypes = getBeforeTemplateResultTypes(tree, state);
if (beforeTemplateResultTypes.isEmpty()) {
return Description.NO_MATCH;
}
Type replacementType = afterTemplateResultType.orElseThrow();
if (areAllSuperTypeOf(replacementType, beforeTemplateResultTypes, state)) {
return Description.NO_MATCH;
}
Type alternativeReplacementType =
(replacementType.isPrimitive()
? state.getTypes().boxedTypeOrType(replacementType)
: state.getTypes().unboxedType(replacementType));
boolean requiresBoxingOrUnboxing =
areAllSuperTypeOf(alternativeReplacementType, beforeTemplateResultTypes, state);
// @Considerations
// enum ConsiderationType:
// MAY_CAUSE_BOXING
// MAY_CAUSE_UNBOXING
// MAY_CAUSE_COMPILATION_ERROR\
if (requiresBoxingOrUnboxing) {
if (replacementType.isPrimitive()) {
// MAY_CAUSE_UNBOXING
// XXX: Flag this using a custom annotation.
return Description.NO_MATCH;
}
// MAY_CAUSE_
// XXX: Flag this using a custom annotation.
return Description.NO_MATCH;
}
// MAY_CAUSE_COMPILATION_ERROR
// XXX: Flag this using a custom annotation.
// XXX: This case contains false positives; maybe leave out for now?
return describeMatch(tree);
}
private static boolean areAllSuperTypeOf(
Type subType, ImmutableSet<Type> superTypes, VisitorState state) {
return superTypes.stream().allMatch(type -> state.getTypes().isSuperType(type, subType));
}
private static ImmutableSet<Type> getBeforeTemplateResultTypes(
ClassTree tree, VisitorState state) {
return tree.getMembers().stream()
.filter(m -> BEFORE_TEMPLATE_METHOD.matches(m, state))
.map(m -> getResultType((MethodTree) m, state))
.collect(toImmutableSet());
}
private static Optional<Type> getAfterTemplateResultType(ClassTree tree, VisitorState state) {
return tree.getMembers().stream()
.filter(m -> AFTER_TEMPLATE_METHOD.matches(m, state))
.map(m -> getResultType((MethodTree) m, state))
.reduce(state.getTypes()::lub);
}
// XXX: A variant of this pattern also occurs a few times inside Error Prone; contribute upstream.
// XXX: Copied from `JUnitValueSource`.
private static Type getResultType(MethodTree methodTree, VisitorState state) {
List<Type> resultTypes = new ArrayList<>();
new TreeScanner<@Nullable Void, @Nullable Void>() {
@Override
public @Nullable Void visitClass(ClassTree node, @Nullable Void unused) {
/* Ignore `return` statements inside anonymous/local classes. */
return null;
}
@Override
public @Nullable Void visitReturn(ReturnTree node, @Nullable Void unused) {
if (REFASTER_ANY_OF.matches(node.getExpression(), state)) {
for (ExpressionTree arg : ((MethodInvocationTree) node.getExpression()).getArguments()) {
resultTypes.add(ASTHelpers.getType(arg));
}
} else if (ASTHelpers.stripParentheses(node.getExpression()).getKind()
!= Tree.Kind.NULL_LITERAL) {
resultTypes.add(ASTHelpers.getType(node.getExpression()));
}
return super.visitReturn(node, unused);
}
@Override
public @Nullable Void visitLambdaExpression(
LambdaExpressionTree node, @Nullable Void unused) {
/* Ignore `return` statements inside lambda expressions. */
return null;
}
}.scan(methodTree, null);
return resultTypes.stream()
.reduce(state.getTypes()::lub)
.orElseGet(() -> state.getSymtab().botType);
}
}

View File

@@ -0,0 +1,78 @@
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 com.google.errorprone.matchers.Matchers.hasAnnotation;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.code.Type;
// XXX: The plan towards canonical Refaster templates:
// 1. Replace wildcard types.
// 2. Enforce rule naming conventions.
// 3. Split rules with incompatible parameter types.
// 4. (Un)annotate templates with incompatible result expressions.
//
// Also:
// 0. Canonicalize template method order (@PlaceHolder, @BeforeTemplate, @AfterTemplate).
// ^ There may be logic by which to break ties; TBD.
// 1. Canonicalize template method parameter order.
// ^ See https://github.com/PicnicSupermarket/error-prone-support/pull/775.
// 2. Canonicalize template type parameter order following a similar approach.
// 3. Canonicalize template method parameter names by deriving them from the corresponding parameter
// names of the code invoked in the `@AfterTemplate` method.
// 4. Canonicalize type parameter names following a similar approach.
/**
* A {@link BugChecker} that flags Refaster expression templates parameters with wildcard types, and
* suggests simplifying these, possibly by declaring a corresponding class type parameter.
*/
// XXX: Given `? extends T`/`? super T`, suggest just dropping the wildcard if `T` is not referenced
// elsewhere in the same `@BeforeTemplate` signature.
// XXX: The type parameters can also be defined at the method level. Such cases should be moved to
// the class level.
// ^ Relatedly, if not already disallowed, constrain all Refaster method modifiers. Should not be
// `static`, etc.
@AutoService(BugChecker.class)
@BugPattern(
summary = "Refaster template parameter types should not contain wildcards",
link = BUG_PATTERNS_BASE_URL + "RefasterParameterTypeGenerics",
linkType = CUSTOM,
severity = SUGGESTION,
tags = {SIMPLIFICATION, STYLE})
public final class RefasterParameterTypeGenerics extends BugChecker implements ClassTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<Tree> BEFORE_TEMPLATE_METHOD = hasAnnotation(BeforeTemplate.class);
@Override
public Description matchClass(ClassTree tree, VisitorState state) {
for (Tree member : tree.getMembers()) {
if (BEFORE_TEMPLATE_METHOD.matches(member, state)) {
MethodTree method = (MethodTree) member;
for (VariableTree parameter : method.getParameters()) {
Type type = ASTHelpers.getType(parameter);
if (type != null && type.isParameterized()) {
return describeMatch(parameter);
}
}
}
}
return Description.NO_MATCH;
}
}

View File

@@ -99,8 +99,9 @@ public final class RequestMappingAnnotation extends BugChecker implements Method
&& LACKS_PARAMETER_ANNOTATION.matches(tree, state)
? buildDescription(tree)
.setMessage(
"Not all parameters of this request mapping method are annotated; this may be a mistake. "
+ "If the unannotated parameters represent query string parameters, annotate them with `@RequestParam`.")
"Not all parameters of this request mapping method are annotated; this may be a "
+ "mistake. If the unannotated parameters represent query string parameters, "
+ "annotate them with `@RequestParam`.")
.build()
: Description.NO_MATCH;
}

View File

@@ -27,6 +27,7 @@ import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.suppliers.Suppliers;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import javax.inject.Inject;
import tech.picnic.errorprone.bugpatterns.util.Flags;
/** A {@link BugChecker} that flags {@code @RequestParam} parameters with an unsupported type. */
@@ -38,6 +39,7 @@ import tech.picnic.errorprone.bugpatterns.util.Flags;
linkType = CUSTOM,
severity = ERROR,
tags = LIKELY_ERROR)
@SuppressWarnings("java:S2160" /* Super class equality definition suffices. */)
public final class RequestParamType extends BugChecker implements VariableTreeMatcher {
private static final long serialVersionUID = 1L;
private static final String SUPPORTED_CUSTOM_TYPES_FLAG = "RequestParamType:SupportedCustomTypes";
@@ -54,7 +56,8 @@ public final class RequestParamType extends BugChecker implements VariableTreeMa
*
* @param flags Any provided command line flags.
*/
public RequestParamType(ErrorProneFlags flags) {
@Inject
RequestParamType(ErrorProneFlags flags) {
hasUnsupportedRequestParamType = hasUnsupportedRequestParamType(flags);
}

View File

@@ -1,89 +0,0 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.BugPattern.StandardTags.FRAGILE_CODE;
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
import static com.sun.tools.javac.parser.Tokens.TokenKind.RPAREN;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.common.collect.Streams;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.fixes.Fix;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.util.ASTHelpers;
import com.google.errorprone.util.ErrorProneTokens;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.util.Position;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
/**
* A {@link BugChecker} that flags calls to {@link String#toLowerCase()} and {@link
* String#toUpperCase()}, as these methods implicitly rely on the environment's default locale.
*/
// XXX: Also flag `String::toLowerCase` and `String::toUpperCase` method references. For these cases
// the suggested fix should introduce a lambda expression with a parameter of which the name does
// not coincide with the name of an existing variable name. Such functionality should likely be
// introduced in a utility class.
@AutoService(BugChecker.class)
@BugPattern(
summary = "Specify a `Locale` when calling `String#to{Lower,Upper}Case`",
link = BUG_PATTERNS_BASE_URL + "StringCaseLocaleUsage",
linkType = CUSTOM,
severity = WARNING,
tags = FRAGILE_CODE)
public final class StringCaseLocaleUsage extends BugChecker implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> DEFAULT_LOCALE_CASE_CONVERSION =
instanceMethod()
.onExactClass(String.class.getName())
.namedAnyOf("toLowerCase", "toUpperCase")
.withNoParameters();
/** Instantiates a new {@link StringCaseLocaleUsage} instance. */
public StringCaseLocaleUsage() {}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (!DEFAULT_LOCALE_CASE_CONVERSION.matches(tree, state)) {
return Description.NO_MATCH;
}
int closingParenPosition = getClosingParenPosition(tree, state);
if (closingParenPosition == Position.NOPOS) {
return describeMatch(tree);
}
return buildDescription(tree)
.addFix(suggestLocale(closingParenPosition, "Locale.ROOT"))
.addFix(suggestLocale(closingParenPosition, "Locale.getDefault()"))
.build();
}
private static Fix suggestLocale(int insertPosition, String locale) {
return SuggestedFix.builder()
.addImport("java.util.Locale")
.replace(insertPosition, insertPosition, locale)
.build();
}
private static int getClosingParenPosition(MethodInvocationTree tree, VisitorState state) {
int startPosition = ASTHelpers.getStartPosition(tree);
if (startPosition == Position.NOPOS) {
return Position.NOPOS;
}
return Streams.findLast(
ErrorProneTokens.getTokens(SourceCode.treeToString(tree, state), state.context).stream()
.filter(t -> t.kind() == RPAREN))
.map(token -> startPosition + token.pos())
.orElse(Position.NOPOS);
}
}

View File

@@ -103,7 +103,7 @@ public final class AnnotationAttributeMatcher implements Serializable {
* @param tree The annotation AST node to be inspected.
* @return Any matching annotation arguments.
*/
public Stream<? extends ExpressionTree> extractMatchingArguments(AnnotationTree tree) {
public Stream<ExpressionTree> extractMatchingArguments(AnnotationTree tree) {
Type type = ASTHelpers.getType(tree.getAnnotationType());
if (type == null) {
return Stream.empty();
@@ -111,6 +111,7 @@ public final class AnnotationAttributeMatcher implements Serializable {
String annotationType = type.toString();
return tree.getArguments().stream()
.map(ExpressionTree.class::cast)
.filter(a -> matches(annotationType, extractAttributeName(a)));
}

View File

@@ -114,6 +114,7 @@ public final class JavaKeywords {
* @see <a href="https://docs.oracle.com/javase/specs/jls/se17/html/jls-3.html#jls-3.8">JDK 17 JLS
* section 3.8: Identifiers</a>
*/
@SuppressWarnings("java:S1067" /* Chaining conjunctions like this does not impact readability. */)
public static boolean isValidIdentifier(String str) {
return !str.isEmpty()
&& !isReservedKeyword(str)

View File

@@ -18,6 +18,9 @@ import java.util.regex.Pattern;
public final class MethodMatcherFactory {
private static final Splitter ARGUMENT_TYPE_SPLITTER =
Splitter.on(',').trimResults().omitEmptyStrings();
// XXX: Check whether we can use a parser for "standard" Java signatures here. Maybe
// `sun.reflect.generics.parser.SignatureParser`?
@SuppressWarnings("java:S5998" /* In practice there will be only modest recursion. */)
private static final Pattern METHOD_SIGNATURE =
Pattern.compile("([^\\s#(,)]+)#([^\\s#(,)]+)\\(((?:[^\\s#(,)]+(?:,[^\\s#(,)]+)*)?)\\)");

View File

@@ -1,11 +1,14 @@
package tech.picnic.errorprone.bugpatterns.util;
import static com.google.errorprone.matchers.Matchers.typePredicateMatcher;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypePredicates.hasAnnotation;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import com.google.errorprone.predicates.TypePredicate;
import com.google.errorprone.util.ASTHelpers;
import com.google.errorprone.suppliers.Supplier;
import com.sun.source.tree.AnnotationTree;
import com.sun.tools.javac.code.Symbol;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Type;
/**
* A collection of general-purpose {@link Matcher}s.
@@ -25,15 +28,21 @@ public final class MoreMatchers {
* @return A {@link Matcher} that matches trees with the specified meta-annotation.
*/
public static <T extends AnnotationTree> Matcher<T> hasMetaAnnotation(String annotationType) {
TypePredicate typePredicate = hasAnnotation(annotationType);
return (tree, state) -> {
Symbol sym = ASTHelpers.getSymbol(tree);
return sym != null && typePredicate.apply(sym.type, state);
};
return typePredicateMatcher(hasAnnotation(annotationType));
}
// XXX: Consider moving to a `MoreTypePredicates` utility class.
private static TypePredicate hasAnnotation(String annotationClassName) {
return (type, state) -> ASTHelpers.hasAnnotation(type.tsym, annotationClassName, state);
/**
* Returns a {@link Matcher} that determines whether the type of a given {@link Tree} is a subtype
* of the type returned by the specified {@link Supplier}.
*
* <p>This method differs from {@link Matchers#isSubtypeOf(Supplier)} in that it does not perform
* type erasure.
*
* @param <T> The type of tree to match against.
* @param type The {@link Supplier} that returns the type to match against.
* @return A {@link Matcher} that matches trees with the specified type.
*/
public static <T extends Tree> Matcher<T> isSubTypeOf(Supplier<Type> type) {
return typePredicateMatcher(MoreTypePredicates.isSubTypeOf(type));
}
}

View File

@@ -0,0 +1,63 @@
package tech.picnic.errorprone.bugpatterns.util;
import com.google.errorprone.VisitorState;
import com.google.errorprone.predicates.TypePredicate;
import com.google.errorprone.predicates.TypePredicates;
import com.google.errorprone.suppliers.Supplier;
import com.google.errorprone.util.ASTHelpers;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.util.List;
/**
* A collection of general-purpose {@link TypePredicate}s.
*
* <p>These methods are additions to the ones found in {@link TypePredicates}.
*/
// XXX: The methods in this class are tested only indirectly. Consider adding a dedicated test
// class, or make sure that each method is explicitly covered by a tested analog in `MoreMatchers`.
public final class MoreTypePredicates {
private MoreTypePredicates() {}
/**
* Returns a {@link TypePredicate} that matches types that are annotated with the indicated
* annotation type.
*
* @param annotationType The fully-qualified name of the annotation type.
* @return A {@link TypePredicate} that matches appropriate types.
*/
public static TypePredicate hasAnnotation(String annotationType) {
return (type, state) -> ASTHelpers.hasAnnotation(type.tsym, annotationType, state);
}
/**
* Returns a {@link TypePredicate} that matches subtypes of the type returned by the specified
* {@link Supplier}.
*
* @implNote This method does not use {@link ASTHelpers#isSubtype(Type, Type, VisitorState)}, as
* that method performs type erasure.
* @param bound The {@link Supplier} that returns the type to match against.
* @return A {@link TypePredicate} that matches appropriate subtypes.
*/
public static TypePredicate isSubTypeOf(Supplier<Type> bound) {
Supplier<Type> memoizedType = VisitorState.memoize(bound);
return (type, state) -> {
Type boundType = memoizedType.get(state);
return boundType != null && state.getTypes().isSubtype(type, boundType);
};
}
/**
* Returns a {@link TypePredicate} that matches generic types with a type parameter that matches
* the specified {@link TypePredicate} at the indicated index.
*
* @param index The index of the type parameter to match against.
* @param predicate The {@link TypePredicate} to match against the type parameter.
* @return A {@link TypePredicate} that matches appropriate generic types.
*/
public static TypePredicate hasTypeParameter(int index, TypePredicate predicate) {
return (type, state) -> {
List<Type> typeArguments = type.getTypeArguments();
return typeArguments.size() > index && predicate.apply(typeArguments.get(index), state);
};
}
}

View File

@@ -1,12 +1,22 @@
package tech.picnic.errorprone.bugpatterns.util;
import static com.sun.tools.javac.parser.Tokens.TokenKind.RPAREN;
import static com.sun.tools.javac.util.Position.NOPOS;
import static java.util.stream.Collectors.joining;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.google.errorprone.VisitorState;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.util.ErrorProneToken;
import com.google.errorprone.util.ErrorProneTokens;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.Position;
import java.util.Optional;
/**
* A collection of Error Prone utility methods for dealing with the source code representation of
@@ -59,4 +69,57 @@ public final class SourceCode {
whitespaceEndPos == -1 ? sourceCode.length() : whitespaceEndPos,
"");
}
/**
* Creates a {@link SuggestedFix} for the replacement of the given {@link MethodInvocationTree}
* with just the arguments to the method invocation, effectively "unwrapping" the method
* invocation.
*
* <p>For example, given the method invocation {@code foo.bar(1, 2, 3)}, this method will return a
* {@link SuggestedFix} that replaces the method invocation with {@code 1, 2, 3}.
*
* <p>This method aims to preserve the original formatting of the method invocation, including
* whitespace and comments.
*
* @param tree The AST node to be unwrapped.
* @param state A {@link VisitorState} describing the context in which the given {@link
* MethodInvocationTree} is found.
* @return A non-{@code null} {@link SuggestedFix}.
*/
public static SuggestedFix unwrapMethodInvocation(MethodInvocationTree tree, VisitorState state) {
CharSequence sourceCode = state.getSourceCode();
int startPosition = state.getEndPosition(tree.getMethodSelect());
int endPosition = state.getEndPosition(tree);
if (sourceCode == null || startPosition == Position.NOPOS || endPosition == Position.NOPOS) {
return unwrapMethodInvocationDroppingWhitespaceAndComments(tree, state);
}
ImmutableList<ErrorProneToken> tokens =
ErrorProneTokens.getTokens(
sourceCode.subSequence(startPosition, endPosition).toString(), state.context);
Optional<Integer> leftParenPosition =
tokens.stream().findFirst().map(t -> startPosition + t.endPos());
Optional<Integer> rightParenPosition =
Streams.findLast(tokens.stream().filter(t -> t.kind() == RPAREN))
.map(t -> startPosition + t.pos());
if (leftParenPosition.isEmpty() || rightParenPosition.isEmpty()) {
return unwrapMethodInvocationDroppingWhitespaceAndComments(tree, state);
}
return SuggestedFix.replace(
tree,
sourceCode
.subSequence(leftParenPosition.orElseThrow(), rightParenPosition.orElseThrow())
.toString());
}
@VisibleForTesting
static SuggestedFix unwrapMethodInvocationDroppingWhitespaceAndComments(
MethodInvocationTree tree, VisitorState state) {
return SuggestedFix.replace(
tree,
tree.getArguments().stream().map(arg -> treeToString(arg, state)).collect(joining(", ")));
}
}

View File

@@ -4,7 +4,10 @@ import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.suppliers.Supplier;
import com.sun.tools.javac.code.ClassFinder;
import com.sun.tools.javac.code.Source;
import com.sun.tools.javac.code.Symbol.CompletionFailure;
import com.sun.tools.javac.code.Symbol.ModuleSymbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.util.Name;
/**
@@ -86,11 +89,19 @@ public enum ThirdPartyLibrary {
private static boolean canLoadClass(String className, VisitorState state) {
ClassFinder classFinder = ClassFinder.instance(state.context);
Symtab symtab = state.getSymtab();
// XXX: Drop support for targeting Java 8 once the oldest supported JDK drops such support.
ModuleSymbol module =
Source.instance(state.context).compareTo(Source.JDK9) < 0
? symtab.noModule
: symtab.unnamedModule;
Name binaryName = state.binaryNameFromClassname(className);
try {
classFinder.loadClass(state.getSymtab().unnamedModule, binaryName);
classFinder.loadClass(module, binaryName);
return true;
} catch (CompletionFailure e) {
} catch (
@SuppressWarnings("java:S1166" /* Not exceptional. */)
CompletionFailure e) {
return false;
}
}

View File

@@ -16,15 +16,18 @@ import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
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;
@@ -220,6 +223,19 @@ final class AssertJMapRules {
}
}
static final class AssertThatMapContainsOnlyKeys<K, V> {
@BeforeTemplate
AbstractCollectionAssert<?, Collection<? extends K>, K, ?> before(Map<K, V> map, Set<K> keys) {
return assertThat(map.keySet()).hasSameElementsAs(keys);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
MapAssert<K, V> after(Map<K, V> map, Set<K> keys) {
return assertThat(map).containsOnlyKeys(keys);
}
}
static final class AssertThatMapContainsValue<K, V> {
@BeforeTemplate
AbstractBooleanAssert<?> before(Map<K, V> map, V value) {

View File

@@ -23,6 +23,8 @@ final class AssertJPrimitiveRules {
}
@BeforeTemplate
@SuppressWarnings(
"java:S1244" /* The (in)equality checks are fragile, but may be seen in the wild. */)
AbstractBooleanAssert<?> before(double actual, double expected) {
return Refaster.anyOf(
assertThat(actual == expected).isTrue(), assertThat(actual != expected).isFalse());
@@ -43,6 +45,8 @@ final class AssertJPrimitiveRules {
}
@BeforeTemplate
@SuppressWarnings(
"java:S1244" /* The (in)equality checks are fragile, but may be seen in the wild. */)
AbstractBooleanAssert<?> before(double actual, double expected) {
return Refaster.anyOf(
assertThat(actual != expected).isTrue(), assertThat(actual == expected).isFalse());

View File

@@ -2024,8 +2024,8 @@ final class AssertJRules {
////////////////////////////////////////////////////////////////////////////
// Above: Generated code.
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Organize the code below.
// XXX: Do the "single Comparable" match shown below.

View File

@@ -47,7 +47,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByIllegalArgumentExceptionHasMessage {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIllegalArgumentException" /* Matches strictly more specific expressions. */)
"AssertThatThrownByIllegalArgumentException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIllegalArgumentException().isThrownBy(throwingCallable).withMessage(message);
}
@@ -64,7 +64,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByIllegalArgumentExceptionHasMessageParameters {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIllegalArgumentException" /* Matches strictly more specific expressions. */)
"AssertThatThrownByIllegalArgumentException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
ThrowingCallable throwingCallable, String message, @Repeated Object parameters) {
return assertThatIllegalArgumentException()
@@ -85,7 +85,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByIllegalArgumentExceptionHasMessageStartingWith {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIllegalArgumentException" /* Matches strictly more specific expressions. */)
"AssertThatThrownByIllegalArgumentException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIllegalArgumentException()
.isThrownBy(throwingCallable)
@@ -104,7 +104,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByIllegalArgumentExceptionHasMessageContaining {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIllegalArgumentException" /* Matches strictly more specific expressions. */)
"AssertThatThrownByIllegalArgumentException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIllegalArgumentException()
.isThrownBy(throwingCallable)
@@ -123,7 +123,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByIllegalArgumentExceptionHasMessageNotContainingAny {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIllegalArgumentException" /* Matches strictly more specific expressions. */)
"AssertThatThrownByIllegalArgumentException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
ThrowingCallable throwingCallable, @Repeated CharSequence values) {
return assertThatIllegalArgumentException()
@@ -157,7 +157,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByIllegalStateExceptionHasMessage {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIllegalStateException" /* Matches strictly more specific expressions. */)
"AssertThatThrownByIllegalStateException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIllegalStateException().isThrownBy(throwingCallable).withMessage(message);
}
@@ -174,7 +174,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByIllegalStateExceptionHasMessageParameters {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIllegalStateException" /* Matches strictly more specific expressions. */)
"AssertThatThrownByIllegalStateException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
ThrowingCallable throwingCallable, String message, @Repeated Object parameters) {
return assertThatIllegalStateException()
@@ -195,7 +195,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByIllegalStateExceptionHasMessageStartingWith {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIllegalStateException" /* Matches strictly more specific expressions. */)
"AssertThatThrownByIllegalStateException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIllegalStateException()
.isThrownBy(throwingCallable)
@@ -214,7 +214,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByIllegalStateExceptionHasMessageContaining {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIllegalStateException" /* Matches strictly more specific expressions. */)
"AssertThatThrownByIllegalStateException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIllegalStateException()
.isThrownBy(throwingCallable)
@@ -233,7 +233,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByIllegalStateExceptionHasMessageNotContaining {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIllegalStateException" /* Matches strictly more specific expressions. */)
"AssertThatThrownByIllegalStateException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIllegalStateException()
.isThrownBy(throwingCallable)
@@ -265,7 +265,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByNullPointerExceptionHasMessage {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByNullPointerException" /* Matches strictly more specific expressions. */)
"AssertThatThrownByNullPointerException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatNullPointerException().isThrownBy(throwingCallable).withMessage(message);
}
@@ -282,7 +282,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByNullPointerExceptionHasMessageParameters {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByNullPointerException" /* Matches strictly more specific expressions. */)
"AssertThatThrownByNullPointerException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
ThrowingCallable throwingCallable, String message, @Repeated Object parameters) {
return assertThatNullPointerException()
@@ -303,7 +303,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByNullPointerExceptionHasMessageStartingWith {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByNullPointerException" /* Matches strictly more specific expressions. */)
"AssertThatThrownByNullPointerException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatNullPointerException()
.isThrownBy(throwingCallable)
@@ -319,6 +319,44 @@ final class AssertJThrowingCallableRules {
}
}
static final class AssertThatThrownByNullPointerExceptionHasMessageContaining {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByNullPointerException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatNullPointerException()
.isThrownBy(throwingCallable)
.withMessageContaining(message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(ThrowingCallable throwingCallable, String message) {
return assertThatThrownBy(throwingCallable)
.isInstanceOf(NullPointerException.class)
.hasMessageContaining(message);
}
}
static final class AssertThatThrownByNullPointerExceptionHasMessageNotContaining {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByNullPointerException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatNullPointerException()
.isThrownBy(throwingCallable)
.withMessageNotContaining(message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(ThrowingCallable throwingCallable, String message) {
return assertThatThrownBy(throwingCallable)
.isInstanceOf(NullPointerException.class)
.hasMessageNotContaining(message);
}
}
static final class AssertThatThrownByIOException {
@BeforeTemplate
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable) {
@@ -334,8 +372,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByIOExceptionHasMessage {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIOException" /* Matches strictly more specific expressions. */)
@SuppressWarnings("AssertThatThrownByIOException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIOException().isThrownBy(throwingCallable).withMessage(message);
}
@@ -351,8 +388,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByIOExceptionHasMessageParameters {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIOException" /* Matches strictly more specific expressions. */)
@SuppressWarnings("AssertThatThrownByIOException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
ThrowingCallable throwingCallable, String message, @Repeated Object parameters) {
return assertThatIOException().isThrownBy(throwingCallable).withMessage(message, parameters);
@@ -368,6 +404,54 @@ final class AssertJThrowingCallableRules {
}
}
static final class AssertThatThrownByIOExceptionHasMessageStartingWith {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownByIOException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIOException().isThrownBy(throwingCallable).withMessageStartingWith(message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(ThrowingCallable throwingCallable, String message) {
return assertThatThrownBy(throwingCallable)
.isInstanceOf(IOException.class)
.hasMessageStartingWith(message);
}
}
static final class AssertThatThrownByIOExceptionHasMessageContaining {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownByIOException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIOException().isThrownBy(throwingCallable).withMessageContaining(message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(ThrowingCallable throwingCallable, String message) {
return assertThatThrownBy(throwingCallable)
.isInstanceOf(IOException.class)
.hasMessageContaining(message);
}
}
static final class AssertThatThrownByIOExceptionHasMessageNotContaining {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownByIOException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIOException().isThrownBy(throwingCallable).withMessageNotContaining(message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(ThrowingCallable throwingCallable, String message) {
return assertThatThrownBy(throwingCallable)
.isInstanceOf(IOException.class)
.hasMessageNotContaining(message);
}
}
static final class AssertThatThrownBy {
@BeforeTemplate
AbstractObjectAssert<?, ?> before(
@@ -385,7 +469,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByHasMessage {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownBy" /* Matches strictly more specific expressions. */)
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
Class<? extends Throwable> exceptionType,
ThrowingCallable throwingCallable,
@@ -407,7 +491,7 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByHasMessageParameters {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownBy" /* Matches strictly more specific expressions. */)
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
Class<? extends Throwable> exceptionType,
ThrowingCallable throwingCallable,
@@ -431,6 +515,78 @@ final class AssertJThrowingCallableRules {
}
}
static final class AssertThatThrownByHasMessageStartingWith {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
Class<? extends Throwable> exceptionType,
ThrowingCallable throwingCallable,
String message) {
return assertThatExceptionOfType(exceptionType)
.isThrownBy(throwingCallable)
.withMessageStartingWith(message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(
Class<? extends Throwable> exceptionType,
ThrowingCallable throwingCallable,
String message) {
return assertThatThrownBy(throwingCallable)
.isInstanceOf(exceptionType)
.hasMessageStartingWith(message);
}
}
static final class AssertThatThrownByHasMessageContaining {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
Class<? extends Throwable> exceptionType,
ThrowingCallable throwingCallable,
String message) {
return assertThatExceptionOfType(exceptionType)
.isThrownBy(throwingCallable)
.withMessageContaining(message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(
Class<? extends Throwable> exceptionType,
ThrowingCallable throwingCallable,
String message) {
return assertThatThrownBy(throwingCallable)
.isInstanceOf(exceptionType)
.hasMessageContaining(message);
}
}
static final class AssertThatThrownByHasMessageNotContaining {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
Class<? extends Throwable> exceptionType,
ThrowingCallable throwingCallable,
String message) {
return assertThatExceptionOfType(exceptionType)
.isThrownBy(throwingCallable)
.withMessageNotContaining(message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(
Class<? extends Throwable> exceptionType,
ThrowingCallable throwingCallable,
String message) {
return assertThatThrownBy(throwingCallable)
.isInstanceOf(exceptionType)
.hasMessageNotContaining(message);
}
}
// XXX: Drop this rule in favour of a generic Error Prone check that flags `String.format(...)`
// arguments to a wide range of format methods.
static final class AbstractThrowableAssertHasMessage {

View File

@@ -115,6 +115,7 @@ final class AssortedRules {
// intelligently.
static final class LogicalImplication {
@BeforeTemplate
@SuppressWarnings("java:S2589" /* This violation will be rewritten. */)
boolean before(boolean firstTest, boolean secondTest) {
return firstTest || (!firstTest && secondTest);
}

View File

@@ -56,6 +56,7 @@ final class BigDecimalRules {
// `BugChecker`.
static final class BigDecimalValueOf {
@BeforeTemplate
@SuppressWarnings("java:S2111" /* This violation will be rewritten. */)
BigDecimal before(double value) {
return new BigDecimal(value);
}

View File

@@ -3,6 +3,7 @@ package tech.picnic.errorprone.refasterrules;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChooser;
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
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;
@@ -25,6 +26,7 @@ final class BugCheckerRules {
}
@AfterTemplate
@CanIgnoreReturnValue
BugCheckerRefactoringTestHelper after(BugCheckerRefactoringTestHelper helper) {
return helper;
}

View File

@@ -35,6 +35,7 @@ final class CollectionRules {
*/
static final class CollectionIsEmpty<T> {
@BeforeTemplate
@SuppressWarnings("java:S1155" /* This violation will be rewritten. */)
boolean before(Collection<T> collection) {
return Refaster.anyOf(
collection.size() == 0,
@@ -73,6 +74,19 @@ final class CollectionRules {
}
}
/** Prefer {@link Collection#contains(Object)} over more contrived alternatives. */
static final class CollectionContains<T, S> {
@BeforeTemplate
boolean before(Collection<T> collection, S value) {
return collection.stream().anyMatch(value::equals);
}
@AfterTemplate
boolean after(Collection<T> collection, S value) {
return collection.contains(value);
}
}
/**
* Don't call {@link Iterables#addAll(Collection, Iterable)} when the elements to be added are
* already part of a {@link Collection}.
@@ -163,6 +177,8 @@ final class CollectionRules {
}
/** Prefer {@link ArrayList#ArrayList(Collection)} over the Guava alternative. */
@SuppressWarnings(
"NonApiType" /* Matching against `List` would unnecessarily constrain the rule. */)
static final class NewArrayListFromCollection<T> {
@BeforeTemplate
ArrayList<T> before(Collection<T> collection) {

View File

@@ -12,6 +12,7 @@ import static java.util.function.Function.identity;
import com.google.common.collect.Comparators;
import com.google.common.collect.ImmutableList;
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;
@@ -78,6 +79,7 @@ final class ComparatorRules {
}
@AfterTemplate
@CanIgnoreReturnValue
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
Comparator<T> after(Comparator<T> cmp) {
return cmp;
@@ -219,7 +221,6 @@ final class ComparatorRules {
*/
static final class MinOfVarargs<T> {
@BeforeTemplate
@SuppressWarnings("StreamOfArray" /* In practice individual values are provided. */)
T before(@Repeated T value, Comparator<T> cmp) {
return Stream.of(Refaster.asVarargs(value)).min(cmp).orElseThrow();
}
@@ -233,6 +234,7 @@ final class ComparatorRules {
/** Prefer {@link Comparators#min(Comparable, Comparable)}} over more verbose alternatives. */
static final class MinOfPairNaturalOrder<T extends Comparable<? super T>> {
@BeforeTemplate
@SuppressWarnings("java:S1067" /* The conditional operators are independent. */)
T before(T value1, T value2) {
return Refaster.anyOf(
value1.compareTo(value2) <= 0 ? value1 : value2,
@@ -259,6 +261,7 @@ final class ComparatorRules {
*/
static final class MinOfPairCustomOrder<T> {
@BeforeTemplate
@SuppressWarnings("java:S1067" /* The conditional operators are independent. */)
T before(T value1, T value2, Comparator<T> cmp) {
return Refaster.anyOf(
cmp.compare(value1, value2) <= 0 ? value1 : value2,
@@ -285,7 +288,6 @@ final class ComparatorRules {
*/
static final class MaxOfVarargs<T> {
@BeforeTemplate
@SuppressWarnings("StreamOfArray" /* In practice individual values are provided. */)
T before(@Repeated T value, Comparator<T> cmp) {
return Stream.of(Refaster.asVarargs(value)).max(cmp).orElseThrow();
}
@@ -299,6 +301,7 @@ final class ComparatorRules {
/** Prefer {@link Comparators#max(Comparable, Comparable)}} over more verbose alternatives. */
static final class MaxOfPairNaturalOrder<T extends Comparable<? super T>> {
@BeforeTemplate
@SuppressWarnings("java:S1067" /* The conditional operators are independent. */)
T before(T value1, T value2) {
return Refaster.anyOf(
value1.compareTo(value2) >= 0 ? value1 : value2,
@@ -325,6 +328,7 @@ final class ComparatorRules {
*/
static final class MaxOfPairCustomOrder<T> {
@BeforeTemplate
@SuppressWarnings("java:S1067" /* The conditional operators are independent. */)
T before(T value1, T value2, Comparator<T> cmp) {
return Refaster.anyOf(
cmp.compare(value1, value2) >= 0 ? value1 : value2,

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.collect.Streams;
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;
@@ -27,6 +28,7 @@ final class DoubleStreamRules {
}
@AfterTemplate
@CanIgnoreReturnValue
DoubleStream after(DoubleStream stream) {
return stream;
}
@@ -237,6 +239,7 @@ final class DoubleStreamRules {
/** Prefer {@link DoubleStream#anyMatch(DoublePredicate)} over more contrived alternatives. */
static final class DoubleStreamAnyMatch {
@BeforeTemplate
@SuppressWarnings("java:S4034" /* This violation will be rewritten. */)
boolean before(DoubleStream stream, DoublePredicate predicate) {
return Refaster.anyOf(
!stream.noneMatch(predicate), stream.filter(predicate).findAny().isPresent());
@@ -274,4 +277,16 @@ final class DoubleStreamRules {
return stream.allMatch(e -> test(e));
}
}
static final class DoubleStreamTakeWhile {
@BeforeTemplate
DoubleStream before(DoubleStream stream, DoublePredicate predicate) {
return stream.takeWhile(predicate).filter(predicate);
}
@AfterTemplate
DoubleStream after(DoubleStream stream, DoublePredicate predicate) {
return stream.takeWhile(predicate);
}
}
}

View File

@@ -1,10 +1,16 @@
package tech.picnic.errorprone.refasterrules;
import static java.util.function.Predicate.not;
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.AlsoNegation;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
import com.google.errorprone.refaster.annotation.Placeholder;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
@@ -30,6 +36,7 @@ final class EqualityRules {
@AfterTemplate
@AlsoNegation
@SuppressWarnings("java:S1698" /* Reference comparison is valid for enums. */)
boolean after(T a, T b) {
return a == b;
}
@@ -56,11 +63,13 @@ final class EqualityRules {
/** Avoid double negations; this is not Javascript. */
static final class DoubleNegation {
@BeforeTemplate
@SuppressWarnings("java:S2761" /* This violation will be rewritten. */)
boolean before(boolean b) {
return !!b;
}
@AfterTemplate
@CanIgnoreReturnValue
boolean after(boolean b) {
return b;
}
@@ -72,6 +81,7 @@ final class EqualityRules {
*/
// XXX: Replacing `a ? !b : b` with `a != b` changes semantics if both `a` and `b` are boxed
// booleans.
@SuppressWarnings("java:S1940" /* This violation will be rewritten. */)
static final class Negation {
@BeforeTemplate
boolean before(boolean a, boolean b) {
@@ -79,6 +89,8 @@ final class EqualityRules {
}
@BeforeTemplate
@SuppressWarnings(
"java:S1244" /* The equality check is fragile, but may be seen in the wild. */)
boolean before(double a, double b) {
return !(a == b);
}
@@ -100,6 +112,7 @@ final class EqualityRules {
*/
// XXX: Replacing `a ? b : !b` with `a == b` changes semantics if both `a` and `b` are boxed
// booleans.
@SuppressWarnings("java:S1940" /* This violation will be rewritten. */)
static final class IndirectDoubleNegation {
@BeforeTemplate
boolean before(boolean a, boolean b) {
@@ -107,6 +120,8 @@ final class EqualityRules {
}
@BeforeTemplate
@SuppressWarnings(
"java:S1244" /* The inequality check is fragile, but may be seen in the wild. */)
boolean before(double a, double b) {
return !(a != b);
}
@@ -121,4 +136,62 @@ final class EqualityRules {
return a == b;
}
}
/**
* Don't pass a lambda expression to {@link Predicate#not(Predicate)}; instead push the negation
* into the lambda expression.
*/
abstract static class PredicateLambda<T> {
@Placeholder(allowsIdentity = true)
abstract boolean predicate(@MayOptionallyUse T value);
@BeforeTemplate
Predicate<T> before() {
return not(v -> predicate(v));
}
@AfterTemplate
Predicate<T> after() {
return v -> !predicate(v);
}
}
/** Avoid contrived ways of handling {@code null} values during equality testing. */
static final class EqualsLhsNullable<T, S> {
@BeforeTemplate
boolean before(T value1, S value2) {
return Optional.ofNullable(value1).equals(Optional.of(value2));
}
@AfterTemplate
boolean after(T value1, S value2) {
return value2.equals(value1);
}
}
/** Avoid contrived ways of handling {@code null} values during equality testing. */
static final class EqualsRhsNullable<T, S> {
@BeforeTemplate
boolean before(T value1, S value2) {
return Optional.of(value1).equals(Optional.ofNullable(value2));
}
@AfterTemplate
boolean after(T value1, S value2) {
return value1.equals(value2);
}
}
/** Avoid contrived ways of handling {@code null} values during equality testing. */
static final class EqualsLhsAndRhsNullable<T, S> {
@BeforeTemplate
boolean before(T value1, S value2) {
return Optional.ofNullable(value1).equals(Optional.ofNullable(value2));
}
@AfterTemplate
boolean after(T value1, S value2) {
return Objects.equals(value1, value2);
}
}
}

View File

@@ -0,0 +1,43 @@
package tech.picnic.errorprone.refasterrules;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to expressions dealing with files. */
@OnlineDocumentation
final class FileRules {
private FileRules() {}
/** Prefer {@link Files#readString(Path, Charset)} over more contrived alternatives. */
static final class FilesReadStringWithCharset {
@BeforeTemplate
String before(Path path, Charset charset) throws IOException {
return new String(Files.readAllBytes(path), charset);
}
@AfterTemplate
String after(Path path, Charset charset) throws IOException {
return Files.readString(path, charset);
}
}
/** Prefer {@link Files#readString(Path)} over more verbose alternatives. */
static final class FilesReadString {
@BeforeTemplate
String before(Path path) throws IOException {
return Files.readString(path, UTF_8);
}
@AfterTemplate
String after(Path path) throws IOException {
return Files.readString(path);
}
}
}

View File

@@ -250,7 +250,8 @@ final class ImmutableMapRules {
* Prefer {@link ImmutableMap#of(Object, Object, Object, Object)} over alternatives that don't
* communicate the immutability of the resulting map at the type level.
*/
// XXX: Also rewrite the `ImmutableMap.builder()` variant?
// XXX: Consider introducing a `BugChecker` to replace these `ImmutableMapOfX` rules. That will
// also make it easier to rewrite various `ImmutableMap.builder()` variants.
static final class ImmutableMapOf2<K, V> {
@BeforeTemplate
Map<K, V> before(K k1, V v1, K k2, V v2) {
@@ -267,7 +268,8 @@ final class ImmutableMapRules {
* Prefer {@link ImmutableMap#of(Object, Object, Object, Object, Object, Object)} over
* alternatives that don't communicate the immutability of the resulting map at the type level.
*/
// XXX: Also rewrite the `ImmutableMap.builder()` variant?
// XXX: Consider introducing a `BugChecker` to replace these `ImmutableMapOfX` rules. That will
// also make it easier to rewrite various `ImmutableMap.builder()` variants.
static final class ImmutableMapOf3<K, V> {
@BeforeTemplate
Map<K, V> before(K k1, V v1, K k2, V v2, K k3, V v3) {
@@ -285,7 +287,9 @@ final class ImmutableMapRules {
* over alternatives that don't communicate the immutability of the resulting map at the type
* level.
*/
// XXX: Also rewrite the `ImmutableMap.builder()` variant?
// XXX: Consider introducing a `BugChecker` to replace these `ImmutableMapOfX` rules. That will
// also make it easier to rewrite various `ImmutableMap.builder()` variants.
@SuppressWarnings("java:S107" /* Can't avoid many method parameters here. */)
static final class ImmutableMapOf4<K, V> {
@BeforeTemplate
Map<K, V> before(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
@@ -303,7 +307,9 @@ final class ImmutableMapRules {
* Object, Object)} over alternatives that don't communicate the immutability of the resulting map
* at the type level.
*/
// XXX: Also rewrite the `ImmutableMap.builder()` variant?
// XXX: Consider introducing a `BugChecker` to replace these `ImmutableMapOfX` rules. That will
// also make it easier to rewrite various `ImmutableMap.builder()` variants.
@SuppressWarnings("java:S107" /* Can't avoid many method parameters here. */)
static final class ImmutableMapOf5<K, V> {
@BeforeTemplate
Map<K, V> before(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {

View File

@@ -4,8 +4,11 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static java.util.function.Predicate.not;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import com.google.common.collect.Streams;
import com.google.errorprone.refaster.Refaster;
@@ -15,6 +18,7 @@ import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
@@ -211,4 +215,116 @@ final class ImmutableSetRules {
return ImmutableSet.of(e1, e2, e3, e4, e5);
}
}
/**
* Prefer an immutable copy of {@link Sets#difference(Set, Set)} over more contrived alternatives.
*/
static final class SetsDifference<S, T> {
@BeforeTemplate
ImmutableSet<S> before(Set<S> set1, Set<T> set2) {
return set1.stream()
.filter(Refaster.anyOf(not(set2::contains), e -> !set2.contains(e)))
.collect(toImmutableSet());
}
@AfterTemplate
ImmutableSet<S> after(Set<S> set1, Set<T> set2) {
return Sets.difference(set1, set2).immutableCopy();
}
}
/**
* Prefer an immutable copy of {@link Sets#difference(Set, Set)} over more contrived alternatives.
*/
static final class SetsDifferenceMap<T, K, V> {
@BeforeTemplate
ImmutableSet<T> before(Set<T> set, Map<K, V> map) {
return set.stream()
.filter(Refaster.anyOf(not(map::containsKey), e -> !map.containsKey(e)))
.collect(toImmutableSet());
}
@AfterTemplate
ImmutableSet<K> after(Set<K> set, Map<K, V> map) {
return Sets.difference(set, map.keySet()).immutableCopy();
}
}
/**
* Prefer an immutable copy of {@link Sets#difference(Set, Set)} over more contrived alternatives.
*/
static final class SetsDifferenceMultimap<T, K, V> {
@BeforeTemplate
ImmutableSet<T> before(Set<T> set, Multimap<K, V> multimap) {
return set.stream()
.filter(Refaster.anyOf(not(multimap::containsKey), e -> !multimap.containsKey(e)))
.collect(toImmutableSet());
}
@AfterTemplate
ImmutableSet<T> after(Set<T> set, Multimap<K, V> multimap) {
return Sets.difference(set, multimap.keySet()).immutableCopy();
}
}
/**
* Prefer an immutable copy of {@link Sets#intersection(Set, Set)} over more contrived
* alternatives.
*/
static final class SetsIntersection<S, T> {
@BeforeTemplate
ImmutableSet<S> before(Set<S> set1, Set<T> set2) {
return set1.stream().filter(set2::contains).collect(toImmutableSet());
}
@AfterTemplate
ImmutableSet<S> after(Set<S> set1, Set<T> set2) {
return Sets.intersection(set1, set2).immutableCopy();
}
}
/**
* Prefer an immutable copy of {@link Sets#intersection(Set, Set)} over more contrived
* alternatives.
*/
static final class SetsIntersectionMap<T, K, V> {
@BeforeTemplate
ImmutableSet<T> before(Set<T> set, Map<K, V> map) {
return set.stream().filter(map::containsKey).collect(toImmutableSet());
}
@AfterTemplate
ImmutableSet<T> after(Set<T> set, Map<K, V> map) {
return Sets.intersection(set, map.keySet()).immutableCopy();
}
}
/**
* Prefer an immutable copy of {@link Sets#intersection(Set, Set)} over more contrived
* alternatives.
*/
static final class SetsIntersectionMultimap<T, K, V> {
@BeforeTemplate
ImmutableSet<T> before(Set<T> set, Multimap<K, V> multimap) {
return set.stream().filter(multimap::containsKey).collect(toImmutableSet());
}
@AfterTemplate
ImmutableSet<T> after(Set<T> set, Multimap<K, V> multimap) {
return Sets.intersection(set, multimap.keySet()).immutableCopy();
}
}
/** Prefer an immutable copy of {@link Sets#union(Set, Set)} over more contrived alternatives. */
static final class SetsUnion<S, T extends S, U extends S> {
@BeforeTemplate
ImmutableSet<S> before(Set<T> set1, Set<U> set2) {
return Stream.concat(set1.stream(), set2.stream()).collect(toImmutableSet());
}
@AfterTemplate
ImmutableSet<S> after(Set<T> set1, Set<U> set2) {
return Sets.union(set1, set2).immutableCopy();
}
}
}

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.collect.Streams;
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;
@@ -40,6 +41,7 @@ final class IntStreamRules {
}
@AfterTemplate
@CanIgnoreReturnValue
IntStream after(IntStream stream) {
return stream;
}
@@ -250,6 +252,7 @@ final class IntStreamRules {
/** Prefer {@link IntStream#anyMatch(IntPredicate)} over more contrived alternatives. */
static final class IntStreamAnyMatch {
@BeforeTemplate
@SuppressWarnings("java:S4034" /* This violation will be rewritten. */)
boolean before(IntStream stream, IntPredicate predicate) {
return Refaster.anyOf(
!stream.noneMatch(predicate), stream.filter(predicate).findAny().isPresent());
@@ -287,4 +290,16 @@ final class IntStreamRules {
return stream.allMatch(e -> test(e));
}
}
static final class IntStreamTakeWhile {
@BeforeTemplate
IntStream before(IntStream stream, IntPredicate predicate) {
return stream.takeWhile(predicate).filter(predicate);
}
@AfterTemplate
IntStream after(IntStream stream, IntPredicate predicate) {
return stream.takeWhile(predicate);
}
}
}

View File

@@ -34,8 +34,7 @@ import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
* <p>Note that, while both libraries throw an {@link AssertionError} in case of an assertion
* failure, the exact subtype used generally differs.
*/
// XXX: Not all `org.assertj.core.api.Assertions` methods have an associated Refaster rule yet;
// expand this class.
// XXX: Not all JUnit `Assertions` methods have an associated Refaster rule yet; expand this class.
// XXX: Introduce a `@Matcher` on `Executable` and `ThrowingSupplier` expressions, such that they
// are only matched if they are also compatible with the `ThrowingCallable` functional interface.
// When implementing such a matcher, note that expressions with a non-void return type such as
@@ -45,7 +44,7 @@ import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
final class JUnitToAssertJRules {
private JUnitToAssertJRules() {}
public ImmutableSet<?> elidedTypesAndStaticImports() {
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(
Assertions.class,
assertDoesNotThrow(() -> null),

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.collect.Streams;
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;
@@ -40,6 +41,7 @@ final class LongStreamRules {
}
@AfterTemplate
@CanIgnoreReturnValue
LongStream after(LongStream stream) {
return stream;
}
@@ -250,6 +252,7 @@ final class LongStreamRules {
/** Prefer {@link LongStream#anyMatch(LongPredicate)} over more contrived alternatives. */
static final class LongStreamAnyMatch {
@BeforeTemplate
@SuppressWarnings("java:S4034" /* This violation will be rewritten. */)
boolean before(LongStream stream, LongPredicate predicate) {
return Refaster.anyOf(
!stream.noneMatch(predicate), stream.filter(predicate).findAny().isPresent());
@@ -287,4 +290,16 @@ final class LongStreamRules {
return stream.allMatch(e -> test(e));
}
}
static final class LongStreamTakeWhile {
@BeforeTemplate
LongStream before(LongStream stream, LongPredicate predicate) {
return stream.takeWhile(predicate).filter(predicate);
}
@AfterTemplate
LongStream after(LongStream stream, LongPredicate predicate) {
return stream.takeWhile(predicate);
}
}
}

View File

@@ -3,10 +3,12 @@ package tech.picnic.errorprone.refasterrules;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import com.google.common.collect.Streams;
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.MayOptionallyUse;
import com.google.errorprone.refaster.annotation.NotMatches;
import com.google.errorprone.refaster.annotation.Placeholder;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Comparator;
@@ -18,6 +20,7 @@ import java.util.function.Supplier;
import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
import tech.picnic.errorprone.refaster.matchers.IsLikelyTrivialComputation;
/** Refaster rules related to expressions dealing with {@link Optional}s. */
@OnlineDocumentation
@@ -68,7 +71,10 @@ final class OptionalRules {
/** Prefer {@link Optional#orElseThrow()} over the less explicit {@link Optional#get()}. */
static final class OptionalOrElseThrow<T> {
@BeforeTemplate
@SuppressWarnings("NullAway")
@SuppressWarnings({
"java:S3655" /* Matched expressions are in practice embedded in a larger context. */,
"NullAway"
})
T before(Optional<T> optional) {
return optional.get();
}
@@ -94,6 +100,20 @@ final class OptionalRules {
}
}
/** Prefer {@link Optional#equals(Object)} over more contrived alternatives. */
static final class OptionalEqualsOptional<T, S> {
@BeforeTemplate
boolean before(Optional<T> optional, S value) {
return Refaster.anyOf(
optional.filter(value::equals).isPresent(), optional.stream().anyMatch(value::equals));
}
@AfterTemplate
boolean after(Optional<T> optional, S value) {
return optional.equals(Optional.of(value));
}
}
/**
* Don't use the ternary operator to extract the first element of a possibly-empty {@link
* Iterator} as an {@link Optional}.
@@ -114,7 +134,7 @@ final class OptionalRules {
/** Prefer {@link Optional#filter(Predicate)} over usage of the ternary operator. */
// XXX: This rule may introduce a compilation error: the `test` expression may reference a
// non-effectively final variable, which is not allowed in the replacement lambda expression.
// Maybe our `Refaster` checker should test `compilesWithFix`?
// Review whether a `@Matcher` can be used to avoid this.
abstract static class TernaryOperatorOptionalPositiveFiltering<T> {
@Placeholder
abstract boolean test(T value);
@@ -134,7 +154,7 @@ final class OptionalRules {
/** Prefer {@link Optional#filter(Predicate)} over usage of the ternary operator. */
// XXX: This rule may introduce a compilation error: the `test` expression may reference a
// non-effectively final variable, which is not allowed in the replacement lambda expression.
// Maybe our `Refaster` checker should test `compilesWithFix`?
// Review whether a `@Matcher` can be used to avoid this.
abstract static class TernaryOperatorOptionalNegativeFiltering<T> {
@Placeholder
abstract boolean test(T value);
@@ -157,6 +177,7 @@ final class OptionalRules {
*/
static final class MapOptionalToBoolean<T> {
@BeforeTemplate
@SuppressWarnings("OptionalOrElseGet" /* Rule is confused by `Refaster#anyOf` usage. */)
boolean before(Optional<T> optional, Function<? super T, Boolean> predicate) {
return optional.map(predicate).orElse(Refaster.anyOf(false, Boolean.FALSE));
}
@@ -220,6 +241,28 @@ final class OptionalRules {
}
}
/**
* Prefer {@link Optional#orElseGet(Supplier)} over {@link Optional#orElse(Object)} if the
* fallback value is not the result of a trivial computation.
*/
// XXX: This rule may introduce a compilation error: the `value` expression may reference a
// non-effectively final variable, which is not allowed in the replacement lambda expression.
// Review whether a `@Matcher` can be used to avoid this.
// XXX: Once `MethodReferenceUsage` is "production ready", replace
// `@NotMatches(IsLikelyTrivialComputation.class)` with `@Matches(RequiresComputation.class)` (and
// reimplement the matcher accordingly).
static final class OptionalOrElseGet<T> {
@BeforeTemplate
T before(Optional<T> optional, @NotMatches(IsLikelyTrivialComputation.class) T value) {
return optional.orElse(value);
}
@AfterTemplate
T after(Optional<T> optional, T value) {
return optional.orElseGet(() -> value);
}
}
/**
* Flatten a stream of {@link Optional}s using {@link Optional#stream()}, rather than using one of
* the more verbose alternatives.
@@ -304,14 +347,12 @@ final class OptionalRules {
abstract Optional<S> toOptionalFunction(@MayOptionallyUse T element);
@BeforeTemplate
Optional<R> before(
Optional<T> optional, Function<? super S, ? extends Optional<? extends R>> function) {
Optional<R> before(Optional<T> optional, Function<? super S, Optional<? extends R>> function) {
return optional.flatMap(v -> toOptionalFunction(v).flatMap(function));
}
@AfterTemplate
Optional<R> after(
Optional<T> optional, Function<? super S, ? extends Optional<? extends R>> function) {
Optional<R> after(Optional<T> optional, Function<? super S, Optional<? extends R>> function) {
return optional.flatMap(v -> toOptionalFunction(v)).flatMap(function);
}
}
@@ -323,10 +364,14 @@ final class OptionalRules {
Optional<T> before(Optional<T> optional1, Optional<T> optional2) {
// XXX: Note that rewriting the first and third variant will change the code's behavior if
// `optional2` has side-effects.
// XXX: Note that rewriting the first, third and fourth variant will introduce a compilation
// error if `optional2` is not effectively final. Review whether a `@Matcher` can be used to
// avoid this.
return Refaster.anyOf(
optional1.map(Optional::of).orElse(optional2),
optional1.map(Optional::of).orElseGet(() -> optional2),
Stream.of(optional1, optional2).flatMap(Optional::stream).findFirst());
Stream.of(optional1, optional2).flatMap(Optional::stream).findFirst(),
optional1.isPresent() ? optional1 : optional2);
}
@AfterTemplate
@@ -350,6 +395,7 @@ final class OptionalRules {
}
@AfterTemplate
@CanIgnoreReturnValue
Optional<T> after(Optional<T> optional) {
return optional;
}
@@ -381,22 +427,32 @@ final class OptionalRules {
// dropped in favour of `StreamMapFirst` and `OptionalIdentity`.
static final class OptionalMap<S, T> {
@BeforeTemplate
Optional<? extends T> before(Optional<S> optional, Function<? super S, ? extends T> function) {
Optional<? extends T> before(Optional<S> optional, Function<? super S, T> function) {
return optional.stream().map(function).findAny();
}
@AfterTemplate
Optional<? extends T> after(Optional<S> optional, Function<? super S, ? extends T> function) {
Optional<? extends T> after(Optional<S> optional, Function<? super S, T> function) {
return optional.map(function);
}
}
/** Prefer {@link Optional#stream()} over more contrived alternatives. */
static final class OptionalStream<T> {
@BeforeTemplate
Stream<T> before(Optional<T> optional) {
return Refaster.anyOf(
optional.map(Stream::of).orElse(Stream.empty()),
optional.map(Stream::of).orElseGet(Stream::empty));
}
@AfterTemplate
Stream<T> after(Optional<T> optional) {
return optional.stream();
}
}
// XXX: Add a rule for:
// `optional.flatMap(x -> pred(x) ? Optional.empty() : Optional.of(x))` and variants.
// (Maybe canonicalize the inner expression. Maybe we rewrite already.)
// XXX: Add a rule for:
// `optional.map(Stream::of).orElse(Stream.empty())`
// `optional.map(Stream::of).orElseGet(Stream::empty)`
// -> `optional.stream()`
// (Maybe canonicalize the inner expression. Maybe we rewrite it already.)
}

View File

@@ -91,6 +91,7 @@ final class PreconditionsRules {
/** Prefer {@link Objects#requireNonNull(Object)} over more verbose alternatives. */
static final class RequireNonNullStatement<T> {
@BeforeTemplate
@SuppressWarnings("java:S1695" /* This violation will be rewritten. */)
void before(T object) {
if (object == null) {
throw new NullPointerException();
@@ -121,6 +122,7 @@ final class PreconditionsRules {
/** Prefer {@link Objects#requireNonNull(Object, String)} over more verbose alternatives. */
static final class RequireNonNullWithMessageStatement<T> {
@BeforeTemplate
@SuppressWarnings("java:S1695" /* This violation will be rewritten. */)
void before(T object, String message) {
if (object == null) {
throw new NullPointerException(message);

View File

@@ -1,6 +1,13 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.primitives.Booleans;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.Chars;
import com.google.common.primitives.Doubles;
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.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
@@ -13,12 +20,13 @@ final class PrimitiveRules {
/** Avoid contrived ways of expressing the "less than" relationship. */
static final class LessThan {
@BeforeTemplate
@SuppressWarnings("java:S1940" /* This violation will be rewritten. */)
boolean before(double a, double b) {
return !(a >= b);
}
@AfterTemplate
boolean after(long a, long b) {
boolean after(double a, double b) {
return a < b;
}
}
@@ -26,12 +34,13 @@ final class PrimitiveRules {
/** Avoid contrived ways of expressing the "less than or equal to" relationship. */
static final class LessThanOrEqualTo {
@BeforeTemplate
@SuppressWarnings("java:S1940" /* This violation will be rewritten. */)
boolean before(double a, double b) {
return !(a > b);
}
@AfterTemplate
boolean after(long a, long b) {
boolean after(double a, double b) {
return a <= b;
}
}
@@ -39,12 +48,13 @@ final class PrimitiveRules {
/** Avoid contrived ways of expressing the "greater than" relationship. */
static final class GreaterThan {
@BeforeTemplate
@SuppressWarnings("java:S1940" /* This violation will be rewritten. */)
boolean before(double a, double b) {
return !(a <= b);
}
@AfterTemplate
boolean after(long a, long b) {
boolean after(double a, double b) {
return a > b;
}
}
@@ -52,12 +62,13 @@ final class PrimitiveRules {
/** Avoid contrived ways of expressing the "greater than or equal to" relationship. */
static final class GreaterThanOrEqualTo {
@BeforeTemplate
@SuppressWarnings("java:S1940" /* This violation will be rewritten. */)
boolean before(double a, double b) {
return !(a < b);
}
@AfterTemplate
boolean after(long a, long b) {
boolean after(double a, double b) {
return a >= b;
}
}
@@ -65,13 +76,312 @@ final class PrimitiveRules {
/** Prefer {@link Math#toIntExact(long)} over the Guava alternative. */
static final class LongToIntExact {
@BeforeTemplate
int before(long a) {
return Ints.checkedCast(a);
int before(long l) {
return Ints.checkedCast(l);
}
@AfterTemplate
int after(long a) {
return Math.toIntExact(a);
int after(long l) {
return Math.toIntExact(l);
}
}
/** Prefer {@link Boolean#hashCode(boolean)} over the Guava alternative. */
static final class BooleanHashCode {
@BeforeTemplate
int before(boolean b) {
return Booleans.hashCode(b);
}
@AfterTemplate
int after(boolean b) {
return Boolean.hashCode(b);
}
}
/** Prefer {@link Byte#hashCode(byte)} over the Guava alternative. */
static final class ByteHashCode {
@BeforeTemplate
int before(byte b) {
return Bytes.hashCode(b);
}
@AfterTemplate
int after(byte b) {
return Byte.hashCode(b);
}
}
/** Prefer {@link Character#hashCode(char)} over the Guava alternative. */
static final class CharacterHashCode {
@BeforeTemplate
int before(char c) {
return Chars.hashCode(c);
}
@AfterTemplate
int after(char c) {
return Character.hashCode(c);
}
}
/** Prefer {@link Short#hashCode(short)} over the Guava alternative. */
static final class ShortHashCode {
@BeforeTemplate
int before(short s) {
return Shorts.hashCode(s);
}
@AfterTemplate
int after(short s) {
return Short.hashCode(s);
}
}
/** Prefer {@link Integer#hashCode(int)} over the Guava alternative. */
static final class IntegerHashCode {
@BeforeTemplate
int before(int i) {
return Ints.hashCode(i);
}
@AfterTemplate
int after(int i) {
return Integer.hashCode(i);
}
}
/** Prefer {@link Long#hashCode(long)} over the Guava alternative. */
static final class LongHashCode {
@BeforeTemplate
int before(long l) {
return Longs.hashCode(l);
}
@AfterTemplate
int after(long l) {
return Long.hashCode(l);
}
}
/** Prefer {@link Float#hashCode(float)} over the Guava alternative. */
static final class FloatHashCode {
@BeforeTemplate
int before(float f) {
return Floats.hashCode(f);
}
@AfterTemplate
int after(float f) {
return Float.hashCode(f);
}
}
/** Prefer {@link Double#hashCode(double)} over the Guava alternative. */
static final class DoubleHashCode {
@BeforeTemplate
int before(double d) {
return Doubles.hashCode(d);
}
@AfterTemplate
int after(double d) {
return Double.hashCode(d);
}
}
/** Prefer {@link Boolean#compare(boolean, boolean)} over the Guava alternative. */
static final class BooleanCompare {
@BeforeTemplate
int before(boolean a, boolean b) {
return Booleans.compare(a, b);
}
@AfterTemplate
int after(boolean a, boolean b) {
return Boolean.compare(a, b);
}
}
/** Prefer {@link Character#compare(char, char)} over the Guava alternative. */
static final class CharacterCompare {
@BeforeTemplate
int before(char a, char b) {
return Chars.compare(a, b);
}
@AfterTemplate
int after(char a, char b) {
return Character.compare(a, b);
}
}
/** Prefer {@link Short#compare(short, short)} over the Guava alternative. */
static final class ShortCompare {
@BeforeTemplate
int before(short a, short b) {
return Shorts.compare(a, b);
}
@AfterTemplate
int after(short a, short b) {
return Short.compare(a, b);
}
}
/** Prefer {@link Integer#compare(int, int)} over the Guava alternative. */
static final class IntegerCompare {
@BeforeTemplate
int before(int a, int b) {
return Ints.compare(a, b);
}
@AfterTemplate
int after(int a, int b) {
return Integer.compare(a, b);
}
}
/** Prefer {@link Long#compare(long, long)} over the Guava alternative. */
static final class LongCompare {
@BeforeTemplate
int before(long a, long b) {
return Longs.compare(a, b);
}
@AfterTemplate
int after(long a, long b) {
return Long.compare(a, b);
}
}
/** Prefer {@link Float#compare(float, float)} over the Guava alternative. */
static final class FloatCompare {
@BeforeTemplate
int before(float a, float b) {
return Floats.compare(a, b);
}
@AfterTemplate
int after(float a, float b) {
return Float.compare(a, b);
}
}
/** Prefer {@link Double#compare(double, double)} over the Guava alternative. */
static final class DoubleCompare {
@BeforeTemplate
int before(double a, double b) {
return Doubles.compare(a, b);
}
@AfterTemplate
int after(double a, double b) {
return Double.compare(a, b);
}
}
/** Prefer {@link Character#BYTES} over the Guava alternative. */
static final class CharacterBytes {
@BeforeTemplate
int before() {
return Chars.BYTES;
}
@AfterTemplate
int after() {
return Character.BYTES;
}
}
/** Prefer {@link Short#BYTES} over the Guava alternative. */
static final class ShortBytes {
@BeforeTemplate
int before() {
return Shorts.BYTES;
}
@AfterTemplate
int after() {
return Short.BYTES;
}
}
/** Prefer {@link Integer#BYTES} over the Guava alternative. */
static final class IntegerBytes {
@BeforeTemplate
int before() {
return Ints.BYTES;
}
@AfterTemplate
int after() {
return Integer.BYTES;
}
}
/** Prefer {@link Long#BYTES} over the Guava alternative. */
static final class LongBytes {
@BeforeTemplate
int before() {
return Longs.BYTES;
}
@AfterTemplate
int after() {
return Long.BYTES;
}
}
/** Prefer {@link Float#BYTES} over the Guava alternative. */
static final class FloatBytes {
@BeforeTemplate
int before() {
return Floats.BYTES;
}
@AfterTemplate
int after() {
return Float.BYTES;
}
}
/** Prefer {@link Double#BYTES} over the Guava alternative. */
static final class DoubleBytes {
@BeforeTemplate
int before() {
return Doubles.BYTES;
}
@AfterTemplate
int after() {
return Double.BYTES;
}
}
/** Prefer {@link Float#isFinite(float)} over the Guava alternative. */
static final class FloatIsFinite {
@BeforeTemplate
boolean before(float f) {
return Floats.isFinite(f);
}
@AfterTemplate
boolean after(float f) {
return Float.isFinite(f);
}
}
/** Prefer {@link Double#isFinite(double)} over the Guava alternative. */
static final class DoubleIsFinite {
@BeforeTemplate
boolean before(double d) {
return Doubles.isFinite(d);
}
@AfterTemplate
boolean after(double d) {
return Double.isFinite(d);
}
}
}

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.MoreCollectors.toOptional;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
@@ -12,6 +13,8 @@ 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;
@@ -33,6 +36,7 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collector;
import org.jspecify.annotations.Nullable;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
@@ -94,7 +98,7 @@ final class ReactorRules {
}
/** Prefer {@link Mono#justOrEmpty(Object)} over more contrived alternatives. */
static final class MonoJustOrEmpty<@Nullable T> {
static final class MonoJustOrEmptyObject<@Nullable T> {
@BeforeTemplate
Mono<T> before(T value) {
return Mono.justOrEmpty(Optional.ofNullable(value));
@@ -107,9 +111,25 @@ final class ReactorRules {
}
/** Prefer {@link Mono#justOrEmpty(Optional)} over more verbose alternatives. */
static final class MonoJustOrEmptyOptional<T> {
@BeforeTemplate
Mono<T> before(Optional<T> optional) {
return Mono.just(optional).filter(Optional::isPresent).map(Optional::orElseThrow);
}
@AfterTemplate
Mono<T> after(Optional<T> optional) {
return Mono.justOrEmpty(optional);
}
}
/**
* Prefer {@link Mono#defer(Supplier) deferring} {@link Mono#justOrEmpty(Optional)} over more
* verbose alternatives.
*/
// XXX: If `optional` is a constant and effectively-final expression then the `Mono.defer` can be
// dropped. Should look into Refaster support for identifying this.
static final class MonoFromOptional<T> {
static final class MonoDeferMonoJustOrEmpty<T> {
@BeforeTemplate
@SuppressWarnings(
"MonoFromSupplier" /* `optional` may match a checked exception-throwing expression. */)
@@ -384,12 +404,33 @@ final class ReactorRules {
return mono.then();
}
// XXX: Replace this rule with an extension of the `IdentityConversion` rule, supporting
// `Stream#map`, `Mono#map` and `Flux#map`.
@BeforeTemplate
Mono<ImmutableList<T>> before3(Mono<ImmutableList<T>> mono) {
return mono.map(ImmutableList::copyOf);
}
@AfterTemplate
@CanIgnoreReturnValue
Mono<T> after(Mono<T> mono) {
return mono;
}
}
/** Don't unnecessarily transform a {@link Mono} to a {@link Flux} to expect exactly one item. */
static final class MonoSingle<T> {
@BeforeTemplate
Mono<T> before(Mono<T> mono) {
return mono.flux().single();
}
@AfterTemplate
Mono<T> after(Mono<T> mono) {
return mono.single();
}
}
/** Don't unnecessarily pass an empty publisher to {@link Flux#switchIfEmpty(Publisher)}. */
static final class FluxSwitchIfEmptyOfEmptyPublisher<T> {
@BeforeTemplate
@@ -398,6 +439,7 @@ final class ReactorRules {
}
@AfterTemplate
@CanIgnoreReturnValue
Flux<T> after(Flux<T> flux) {
return flux;
}
@@ -406,6 +448,7 @@ final class ReactorRules {
/** Prefer {@link Flux#concatMap(Function)} over more contrived alternatives. */
static final class FluxConcatMap<T, S> {
@BeforeTemplate
@SuppressWarnings("NestedPublishers")
Flux<S> before(Flux<T> flux, Function<? super T, ? extends Publisher<? extends S>> function) {
return Refaster.anyOf(
flux.flatMap(function, 1),
@@ -422,6 +465,7 @@ final class ReactorRules {
/** Prefer {@link Flux#concatMap(Function, int)} over more contrived alternatives. */
static final class FluxConcatMapWithPrefetch<T, S> {
@BeforeTemplate
@SuppressWarnings("NestedPublishers")
Flux<S> before(
Flux<T> flux,
Function<? super T, ? extends Publisher<? extends S>> function,
@@ -594,6 +638,7 @@ final class ReactorRules {
abstract S transformation(@MayOptionallyUse T value);
@BeforeTemplate
@SuppressWarnings("java:S138" /* Method is long, but not complex. */)
Publisher<S> before(Flux<T> flux, boolean delayUntilEnd, int maxConcurrency, int prefetch) {
return Refaster.anyOf(
flux.concatMap(
@@ -752,6 +797,7 @@ final class ReactorRules {
/** Prefer {@link Mono#flatMap(Function)} over more contrived alternatives. */
static final class MonoFlatMap<S, T> {
@BeforeTemplate
@SuppressWarnings("NestedPublishers")
Mono<T> before(Mono<S> mono, Function<? super S, ? extends Mono<? extends T>> function) {
return mono.map(function).flatMap(identity());
}
@@ -765,6 +811,7 @@ final class ReactorRules {
/** Prefer {@link Mono#flatMapMany(Function)} over more contrived alternatives. */
static final class MonoFlatMapMany<S, T> {
@BeforeTemplate
@SuppressWarnings("NestedPublishers")
Flux<T> before(Mono<S> mono, Function<? super S, ? extends Publisher<? extends T>> function) {
return mono.map(function).flatMapMany(identity());
}
@@ -1150,6 +1197,56 @@ final class ReactorRules {
}
}
/**
* Do not unnecessarily {@link Flux#filter(Predicate) filter} the result of {@link
* Flux#takeWhile(Predicate)} using the same {@link Predicate}.
*/
static final class FluxTakeWhile<T> {
@BeforeTemplate
Flux<T> before(Flux<T> flux, Predicate<? super T> predicate) {
return flux.takeWhile(predicate).filter(predicate);
}
@AfterTemplate
Flux<T> after(Flux<T> flux, Predicate<? super T> predicate) {
return flux.takeWhile(predicate);
}
}
/**
* Prefer {@link Flux#collect(Collector)} with {@link ImmutableList#toImmutableList()} over
* alternatives that do not explicitly return an immutable collection.
*/
static final class FluxCollectToImmutableList<T> {
@BeforeTemplate
Mono<List<T>> before(Flux<T> flux) {
return flux.collectList();
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
Mono<ImmutableList<T>> after(Flux<T> flux) {
return flux.collect(toImmutableList());
}
}
/**
* Prefer {@link Flux#collect(Collector)} with {@link ImmutableSet#toImmutableSet()} over more
* contrived alternatives.
*/
static final class FluxCollectToImmutableSet<T> {
@BeforeTemplate
Mono<ImmutableSet<T>> before(Flux<T> flux) {
return flux.collect(toImmutableList()).map(ImmutableSet::copyOf);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
Mono<ImmutableSet<T>> after(Flux<T> flux) {
return flux.collect(toImmutableSet());
}
}
/** 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.
@@ -1215,6 +1312,7 @@ final class ReactorRules {
}
@AfterTemplate
@CanIgnoreReturnValue
StepVerifier.Step<T> after(StepVerifier.Step<T> step) {
return step;
}
@@ -1234,6 +1332,23 @@ final class ReactorRules {
}
}
/** Avoid list collection when verifying that a {@link Flux} emits exactly one value. */
// XXX: This rule assumes that the matched collector does not drop elements. Consider introducing
// a `@Matches(DoesNotDropElements.class)` or `@NotMatches(MayDropElements.class)` guard.
static final class FluxAsStepVerifierExpectNext<T, L extends List<T>> {
@BeforeTemplate
StepVerifier.Step<L> before(Flux<T> flux, Collector<? super T, ?, L> listCollector, T object) {
return flux.collect(listCollector)
.as(StepVerifier::create)
.assertNext(list -> assertThat(list).containsExactly(object));
}
@AfterTemplate
StepVerifier.Step<T> after(Flux<T> flux, T object) {
return flux.as(StepVerifier::create).expectNext(object);
}
}
/** Prefer {@link StepVerifier.LastStep#verifyComplete()} over more verbose alternatives. */
static final class StepVerifierLastStepVerifyComplete {
@BeforeTemplate

View File

@@ -3,31 +3,55 @@ 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;
import static java.util.stream.Collectors.flatMapping;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.maxBy;
import static java.util.stream.Collectors.minBy;
import static java.util.stream.Collectors.reducing;
import static java.util.stream.Collectors.summarizingDouble;
import static java.util.stream.Collectors.summarizingInt;
import static java.util.stream.Collectors.summarizingLong;
import static java.util.stream.Collectors.summingDouble;
import static java.util.stream.Collectors.summingInt;
import static java.util.stream.Collectors.summingLong;
import com.google.common.collect.Streams;
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;
import com.google.errorprone.refaster.annotation.Repeated;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.DoubleSummaryStatistics;
import java.util.IntSummaryStatistics;
import java.util.LongSummaryStatistics;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.function.UnaryOperator;
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.IsLambdaExpressionOrMethodReference;
import tech.picnic.errorprone.refaster.matchers.IsRefasterAsVarargs;
/** Refaster rules related to expressions dealing with {@link Stream}s. */
@OnlineDocumentation
@@ -82,12 +106,9 @@ final class StreamRules {
* Prefer {@link Arrays#stream(Object[])} over {@link Stream#of(Object[])}, as the former is
* clearer.
*/
// XXX: Introduce a `Matcher` that identifies `Refaster.asVarargs(...)` invocations and annotate
// the `array` parameter as `@NotMatches(IsRefasterAsVarargs.class)`. Then elsewhere
// `@SuppressWarnings("StreamOfArray")` annotations can be dropped.
static final class StreamOfArray<T> {
@BeforeTemplate
Stream<T> before(T[] array) {
Stream<T> before(@NotMatches(IsRefasterAsVarargs.class) T[] array) {
return Stream.of(array);
}
@@ -105,6 +126,7 @@ final class StreamRules {
}
@AfterTemplate
@CanIgnoreReturnValue
Stream<T> after(Stream<T> stream) {
return stream;
}
@@ -228,14 +250,18 @@ final class StreamRules {
}
/** In order to test whether a stream has any element, simply try to find one. */
// XXX: This rule assumes that any matched `Collector` does not perform any filtering.
// (Perhaps we could add a `@Matches` guard that validates that the collector expression does not
// contain a `Collectors#filtering` call. That'd still not be 100% accurate, though.)
static final class StreamIsEmpty<T> {
@BeforeTemplate
boolean before(Stream<T> stream) {
boolean before(Stream<T> stream, Collector<? super T, ?, ? extends Collection<?>> collector) {
return Refaster.anyOf(
stream.count() == 0,
stream.count() <= 0,
stream.count() < 1,
stream.findFirst().isEmpty());
stream.findFirst().isEmpty(),
stream.collect(collector).isEmpty());
}
@AfterTemplate
@@ -263,9 +289,12 @@ final class StreamRules {
static final class StreamMin<T> {
@BeforeTemplate
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
Optional<T> before(Stream<T> stream, Comparator<? super T> comparator) {
return Refaster.anyOf(
stream.max(comparator.reversed()), stream.sorted(comparator).findFirst());
stream.max(comparator.reversed()),
stream.sorted(comparator).findFirst(),
stream.collect(minBy(comparator)));
}
@AfterTemplate
@@ -289,9 +318,12 @@ final class StreamRules {
static final class StreamMax<T> {
@BeforeTemplate
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
Optional<T> before(Stream<T> stream, Comparator<? super T> comparator) {
return Refaster.anyOf(
stream.min(comparator.reversed()), Streams.findLast(stream.sorted(comparator)));
stream.min(comparator.reversed()),
Streams.findLast(stream.sorted(comparator)),
stream.collect(maxBy(comparator)));
}
@AfterTemplate
@@ -316,6 +348,7 @@ final class StreamRules {
/** Prefer {@link Stream#noneMatch(Predicate)} over more contrived alternatives. */
static final class StreamNoneMatch<T> {
@BeforeTemplate
@SuppressWarnings("java:S4034" /* This violation will be rewritten. */)
boolean before(Stream<T> stream, Predicate<? super T> predicate) {
return Refaster.anyOf(
!stream.anyMatch(predicate),
@@ -323,6 +356,14 @@ final class StreamRules {
stream.filter(predicate).findAny().isEmpty());
}
@BeforeTemplate
boolean before2(
Stream<T> stream,
@Matches(IsLambdaExpressionOrMethodReference.class)
Function<? super T, Boolean> predicate) {
return stream.map(predicate).noneMatch(Refaster.anyOf(Boolean::booleanValue, b -> b));
}
@AfterTemplate
boolean after(Stream<T> stream, Predicate<? super T> predicate) {
return stream.noneMatch(predicate);
@@ -347,11 +388,20 @@ final class StreamRules {
/** Prefer {@link Stream#anyMatch(Predicate)} over more contrived alternatives. */
static final class StreamAnyMatch<T> {
@BeforeTemplate
@SuppressWarnings("java:S4034" /* This violation will be rewritten. */)
boolean before(Stream<T> stream, Predicate<? super T> predicate) {
return Refaster.anyOf(
!stream.noneMatch(predicate), stream.filter(predicate).findAny().isPresent());
}
@BeforeTemplate
boolean before2(
Stream<T> stream,
@Matches(IsLambdaExpressionOrMethodReference.class)
Function<? super T, Boolean> predicate) {
return stream.map(predicate).anyMatch(Refaster.anyOf(Boolean::booleanValue, b -> b));
}
@AfterTemplate
boolean after(Stream<T> stream, Predicate<? super T> predicate) {
return stream.anyMatch(predicate);
@@ -364,6 +414,14 @@ final class StreamRules {
return stream.noneMatch(Refaster.anyOf(not(predicate), predicate.negate()));
}
@BeforeTemplate
boolean before2(
Stream<T> stream,
@Matches(IsLambdaExpressionOrMethodReference.class)
Function<? super T, Boolean> predicate) {
return stream.map(predicate).allMatch(Refaster.anyOf(Boolean::booleanValue, b -> b));
}
@AfterTemplate
boolean after(Stream<T> stream, Predicate<? super T> predicate) {
return stream.allMatch(predicate);
@@ -387,7 +445,13 @@ final class StreamRules {
static final class StreamMapToIntSum<T> {
@BeforeTemplate
int before(
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
long before(Stream<T> stream, ToIntFunction<T> mapper) {
return stream.collect(summingInt(mapper));
}
@BeforeTemplate
int before2(
Stream<T> stream,
@Matches(IsLambdaExpressionOrMethodReference.class) Function<? super T, Integer> mapper) {
return stream.map(mapper).reduce(0, Integer::sum);
@@ -401,7 +465,13 @@ final class StreamRules {
static final class StreamMapToDoubleSum<T> {
@BeforeTemplate
double before(
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
double before(Stream<T> stream, ToDoubleFunction<T> mapper) {
return stream.collect(summingDouble(mapper));
}
@BeforeTemplate
double before2(
Stream<T> stream,
@Matches(IsLambdaExpressionOrMethodReference.class) Function<? super T, Double> mapper) {
return stream.map(mapper).reduce(0.0, Double::sum);
@@ -415,7 +485,13 @@ final class StreamRules {
static final class StreamMapToLongSum<T> {
@BeforeTemplate
long before(
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
long before(Stream<T> stream, ToLongFunction<T> mapper) {
return stream.collect(summingLong(mapper));
}
@BeforeTemplate
long before2(
Stream<T> stream,
@Matches(IsLambdaExpressionOrMethodReference.class) Function<? super T, Long> mapper) {
return stream.map(mapper).reduce(0L, Long::sum);
@@ -426,4 +502,170 @@ final class StreamRules {
return stream.mapToLong(mapper).sum();
}
}
static final class StreamMapToIntSummaryStatistics<T> {
@BeforeTemplate
IntSummaryStatistics before(Stream<T> stream, ToIntFunction<T> mapper) {
return stream.collect(summarizingInt(mapper));
}
@AfterTemplate
IntSummaryStatistics after(Stream<T> stream, ToIntFunction<T> mapper) {
return stream.mapToInt(mapper).summaryStatistics();
}
}
static final class StreamMapToDoubleSummaryStatistics<T> {
@BeforeTemplate
DoubleSummaryStatistics before(Stream<T> stream, ToDoubleFunction<T> mapper) {
return stream.collect(summarizingDouble(mapper));
}
@AfterTemplate
DoubleSummaryStatistics after(Stream<T> stream, ToDoubleFunction<T> mapper) {
return stream.mapToDouble(mapper).summaryStatistics();
}
}
static final class StreamMapToLongSummaryStatistics<T> {
@BeforeTemplate
LongSummaryStatistics before(Stream<T> stream, ToLongFunction<T> mapper) {
return stream.collect(summarizingLong(mapper));
}
@AfterTemplate
LongSummaryStatistics after(Stream<T> stream, ToLongFunction<T> mapper) {
return stream.mapToLong(mapper).summaryStatistics();
}
}
static final class StreamCount<T> {
@BeforeTemplate
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
long before(Stream<T> stream) {
return stream.collect(counting());
}
@AfterTemplate
long after(Stream<T> stream) {
return stream.count();
}
}
static final class StreamReduce<T> {
@BeforeTemplate
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
Optional<T> before(Stream<T> stream, BinaryOperator<T> accumulator) {
return stream.collect(reducing(accumulator));
}
@AfterTemplate
Optional<T> after(Stream<T> stream, BinaryOperator<T> accumulator) {
return stream.reduce(accumulator);
}
}
static final class StreamReduceWithIdentity<T> {
@BeforeTemplate
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
T before(Stream<T> stream, T identity, BinaryOperator<T> accumulator) {
return stream.collect(reducing(identity, accumulator));
}
@AfterTemplate
T after(Stream<T> stream, T identity, BinaryOperator<T> accumulator) {
return stream.reduce(identity, accumulator);
}
}
static final class StreamFilterCollect<T, R> {
@BeforeTemplate
R before(
Stream<T> stream, Predicate<? super T> predicate, Collector<? super T, ?, R> collector) {
return stream.collect(filtering(predicate, collector));
}
@AfterTemplate
R after(
Stream<T> stream, Predicate<? super T> predicate, Collector<? super T, ?, R> collector) {
return stream.filter(predicate).collect(collector);
}
}
static final class StreamMapCollect<T, U, R> {
@BeforeTemplate
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)
R before(
Stream<T> stream,
Function<? super T, ? extends U> mapper,
Collector<? super U, ?, R> collector) {
return stream.collect(mapping(mapper, collector));
}
@AfterTemplate
R after(
Stream<T> stream,
Function<? super T, ? extends U> mapper,
Collector<? super U, ?, R> collector) {
return stream.map(mapper).collect(collector);
}
}
static final class StreamFlatMapCollect<T, U, R> {
@BeforeTemplate
R before(
Stream<T> stream,
Function<? super T, ? extends Stream<? extends U>> mapper,
Collector<? super U, ?, R> collector) {
return stream.collect(flatMapping(mapper, collector));
}
@AfterTemplate
R after(
Stream<T> stream,
Function<? super T, ? extends Stream<? extends U>> mapper,
Collector<? super U, ?, R> collector) {
return stream.flatMap(mapper).collect(collector);
}
}
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));
}
@AfterTemplate
Stream<T> after(@Repeated Stream<T> stream) {
return Streams.concat(Refaster.asVarargs(stream));
}
}
static final class StreamTakeWhile<T> {
@BeforeTemplate
Stream<T> before(Stream<T> stream, Predicate<? super T> predicate) {
return stream.takeWhile(predicate).filter(predicate);
}
@AfterTemplate
Stream<T> after(Stream<T> stream, Predicate<? super T> predicate) {
return stream.takeWhile(predicate);
}
}
/**
* Prefer {@link Stream#iterate(Object, Predicate, UnaryOperator)} over more contrived
* alternatives.
*/
static final class StreamIterate<T> {
@BeforeTemplate
Stream<T> before(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) {
return Stream.iterate(seed, next).takeWhile(hasNext);
}
@AfterTemplate
Stream<T> after(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) {
return Stream.iterate(seed, hasNext, next);
}
}
}

View File

@@ -1,6 +1,8 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.function.Predicate.not;
import static java.util.stream.Collectors.joining;
import com.google.common.base.Joiner;
@@ -11,21 +13,23 @@ 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 com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to expressions dealing with {@link String}s. */
// XXX: Should we prefer `s -> !s.isEmpty()` or `not(String::isEmpty)`?
@OnlineDocumentation
final class StringRules {
private StringRules() {}
/** Prefer {@link String#isEmpty()} over alternatives that consult the string's length. */
// XXX: Once we target JDK 15+, generalize this rule to cover all `CharSequence` subtypes.
static final class StringIsEmpty {
@BeforeTemplate
boolean before(String str) {
@@ -39,6 +43,37 @@ final class StringRules {
}
}
/** Prefer a method reference to {@link String#isEmpty()} over the equivalent lambda function. */
// XXX: Once we target JDK 15+, generalize this rule to cover all `CharSequence` subtypes.
// XXX: As it stands, this rule is a special case of what `MethodReferenceUsage` tries to achieve.
// If/when `MethodReferenceUsage` becomes production ready, we should simply drop this check.
static final class StringIsEmptyPredicate {
@BeforeTemplate
Predicate<String> before() {
return s -> s.isEmpty();
}
@AfterTemplate
Predicate<String> after() {
return String::isEmpty;
}
}
/** Prefer a method reference to {@link String#isEmpty()} over the equivalent lambda function. */
// XXX: Once we target JDK 15+, generalize this rule to cover all `CharSequence` subtypes.
static final class StringIsNotEmptyPredicate {
@BeforeTemplate
Predicate<String> before() {
return s -> !s.isEmpty();
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
Predicate<String> after() {
return not(String::isEmpty);
}
}
/** Prefer {@link Strings#isNullOrEmpty(String)} over the more verbose alternative. */
static final class StringIsNullOrEmpty {
@BeforeTemplate
@@ -65,7 +100,7 @@ final class StringRules {
@AfterTemplate
Optional<String> after(String str) {
return Optional.ofNullable(str).filter(s -> !s.isEmpty());
return Optional.ofNullable(str).filter(not(String::isEmpty));
}
}
@@ -76,8 +111,9 @@ final class StringRules {
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
Optional<String> after(Optional<String> optional) {
return optional.filter(s -> !s.isEmpty());
return optional.filter(not(String::isEmpty));
}
}

View File

@@ -0,0 +1,106 @@
package tech.picnic.errorprone.refasterrules;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.sun.source.tree.Tree;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to expressions dealing with {@link SuggestedFix}es. */
@OnlineDocumentation
final class SuggestedFixRules {
private SuggestedFixRules() {}
/** Prefer {@link SuggestedFix#delete(Tree)} over more contrived alternatives. */
static final class SuggestedFixDelete {
@BeforeTemplate
SuggestedFix before(Tree tree) {
return SuggestedFix.builder().delete(tree).build();
}
@AfterTemplate
SuggestedFix after(Tree tree) {
return SuggestedFix.delete(tree);
}
}
/** Prefer {@link SuggestedFix#replace(Tree, String)}} over more contrived alternatives. */
static final class SuggestedFixReplaceTree {
@BeforeTemplate
SuggestedFix before(Tree tree, String replaceWith) {
return SuggestedFix.builder().replace(tree, replaceWith).build();
}
@AfterTemplate
SuggestedFix after(Tree tree, String replaceWith) {
return SuggestedFix.replace(tree, replaceWith);
}
}
/** Prefer {@link SuggestedFix#replace(int, int, String)}} over more contrived alternatives. */
static final class SuggestedFixReplaceStartEnd {
@BeforeTemplate
SuggestedFix before(int start, int end, String replaceWith) {
return SuggestedFix.builder().replace(start, end, replaceWith).build();
}
@AfterTemplate
SuggestedFix after(int start, int end, String replaceWith) {
return SuggestedFix.replace(start, end, replaceWith);
}
}
/**
* Prefer {@link SuggestedFix#replace(Tree, String, int, int)}} over more contrived alternatives.
*/
static final class SuggestedFixReplaceTreeStartEnd {
@BeforeTemplate
SuggestedFix before(Tree tree, String replaceWith, int start, int end) {
return SuggestedFix.builder().replace(tree, replaceWith, start, end).build();
}
@AfterTemplate
SuggestedFix after(Tree tree, String replaceWith, int start, int end) {
return SuggestedFix.replace(tree, replaceWith, start, end);
}
}
/** Prefer {@link SuggestedFix#swap(Tree, Tree)} over more contrived alternatives. */
static final class SuggestedFixSwap {
@BeforeTemplate
SuggestedFix before(Tree tree1, Tree tree2) {
return SuggestedFix.builder().swap(tree1, tree2).build();
}
@AfterTemplate
SuggestedFix after(Tree tree1, Tree tree2) {
return SuggestedFix.swap(tree1, tree2);
}
}
/** Prefer {@link SuggestedFix#prefixWith(Tree, String)} over more contrived alternatives. */
static final class SuggestedFixPrefixWith {
@BeforeTemplate
SuggestedFix before(Tree tree, String prefix) {
return SuggestedFix.builder().prefixWith(tree, prefix).build();
}
@AfterTemplate
SuggestedFix after(Tree tree, String prefix) {
return SuggestedFix.prefixWith(tree, prefix);
}
}
/** Prefer {@link SuggestedFix#postfixWith(Tree, String)}} over more contrived alternatives. */
static final class SuggestedFixPostfixWith {
@BeforeTemplate
SuggestedFix before(Tree tree, String postfix) {
return SuggestedFix.builder().postfixWith(tree, postfix).build();
}
@AfterTemplate
SuggestedFix after(Tree tree, String postfix) {
return SuggestedFix.postfixWith(tree, postfix);
}
}
}

View File

@@ -1,8 +1,7 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
@@ -54,6 +53,6 @@ final class AssertJIsNullTest {
" assertThat(\"foo\").isNull();",
" }",
"}")
.doTest(TEXT_MATCH);
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -0,0 +1,110 @@
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 AssociativeMethodInvocationTest {
@Test
void identification() {
CompilationTestHelper.newInstance(AssociativeMethodInvocation.class, getClass())
.addSourceLines(
"A.java",
"import com.google.common.collect.ImmutableList;",
"import com.google.errorprone.matchers.Matchers;",
"import com.google.errorprone.refaster.Refaster;",
"",
"class A {",
" void m() {",
" Matchers.allOf();",
" Matchers.anyOf();",
" Refaster.anyOf();",
"",
" Matchers.allOf((t, s) -> true);",
" Matchers.anyOf((t, s) -> true);",
" Refaster.anyOf(0);",
"",
" Matchers.allOf(Matchers.anyOf((t, s) -> true));",
" Matchers.anyOf(Matchers.allOf((t, s) -> true));",
" Refaster.anyOf(Matchers.allOf((t, s) -> true));",
"",
" // BUG: Diagnostic contains:",
" Matchers.allOf(Matchers.allOf((t, s) -> true));",
" // BUG: Diagnostic contains:",
" Matchers.anyOf(Matchers.anyOf((t, s) -> true));",
" // BUG: Diagnostic contains:",
" Refaster.anyOf(Refaster.anyOf(0));",
"",
" Matchers.allOf(Matchers.allOf(ImmutableList.of((t, s) -> true)));",
" Matchers.anyOf(Matchers.anyOf(ImmutableList.of((t, s) -> true)));",
"",
" // BUG: Diagnostic contains:",
" Matchers.allOf(",
" (t, s) -> true, Matchers.allOf((t, s) -> false, (t, s) -> true), (t, s) -> false);",
" // BUG: Diagnostic contains:",
" Matchers.anyOf(",
" (t, s) -> true, Matchers.anyOf((t, s) -> false, (t, s) -> true), (t, s) -> false);",
" // BUG: Diagnostic contains:",
" Refaster.anyOf(0, Refaster.anyOf(1, 2), 3);",
" }",
"}")
.doTest();
}
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(AssociativeMethodInvocation.class, getClass())
.addInputLines(
"A.java",
"import com.google.errorprone.matchers.Matchers;",
"import com.google.errorprone.refaster.Refaster;",
"",
"class A {",
" void m() {",
" Matchers.allOf(Matchers.allOf());",
" Matchers.anyOf(Matchers.anyOf());",
" Refaster.anyOf(Refaster.anyOf());",
"",
" Matchers.allOf(Matchers.allOf((t, s) -> true));",
" Matchers.anyOf(Matchers.anyOf((t, s) -> true));",
" Refaster.anyOf(Refaster.anyOf(0));",
"",
" Matchers.allOf(",
" Matchers.anyOf(),",
" Matchers.allOf((t, s) -> false, (t, s) -> true),",
" Matchers.allOf(),",
" Matchers.anyOf((t, s) -> false));",
" Matchers.anyOf(",
" Matchers.allOf(),",
" Matchers.anyOf((t, s) -> false, (t, s) -> true),",
" Matchers.anyOf(),",
" Matchers.allOf((t, s) -> false));",
" Refaster.anyOf(Matchers.allOf(), Refaster.anyOf(1, 2), Matchers.anyOf());",
" }",
"}")
.addOutputLines(
"A.java",
"import com.google.errorprone.matchers.Matchers;",
"import com.google.errorprone.refaster.Refaster;",
"",
"class A {",
" void m() {",
" Matchers.allOf();",
" Matchers.anyOf();",
" Refaster.anyOf();",
"",
" Matchers.allOf((t, s) -> true);",
" Matchers.anyOf((t, s) -> true);",
" Refaster.anyOf(0);",
"",
" Matchers.allOf(",
" Matchers.anyOf(), (t, s) -> false, (t, s) -> true, Matchers.anyOf((t, s) -> false));",
" Matchers.anyOf(",
" Matchers.allOf(), (t, s) -> false, (t, s) -> true, Matchers.allOf((t, s) -> false));",
" Refaster.anyOf(Matchers.allOf(), 1, 2, Matchers.anyOf());",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -34,9 +34,11 @@ final class CanonicalAnnotationSyntaxTest {
" // BUG: Diagnostic contains:",
" @pkg.A.Foo()",
" A functional1();",
"",
" // BUG: Diagnostic contains:",
" @A.Foo()",
" A functional2();",
"",
" // BUG: Diagnostic contains:",
" @Foo()",
" A functional3();",
@@ -53,9 +55,11 @@ final class CanonicalAnnotationSyntaxTest {
" // BUG: Diagnostic contains:",
" @pkg.A.Foo({1})",
" A singleton1();",
"",
" // BUG: Diagnostic contains:",
" @A.Foo({1})",
" A singleton2();",
"",
" // BUG: Diagnostic contains:",
" @Foo({1})",
" A singleton3();",
@@ -63,9 +67,11 @@ final class CanonicalAnnotationSyntaxTest {
" // BUG: Diagnostic contains:",
" @pkg.A.Foo(value = 1)",
" A verbose1();",
"",
" // BUG: Diagnostic contains:",
" @A.Foo(value = 1)",
" A verbose2();",
"",
" // BUG: Diagnostic contains:",
" @Foo(value = 1)",
" A verbose3();",
@@ -82,9 +88,11 @@ final class CanonicalAnnotationSyntaxTest {
" // BUG: Diagnostic contains:",
" @pkg.A.Foo(value2 = {2})",
" A customSingleton1();",
"",
" // BUG: Diagnostic contains:",
" @A.Foo(value2 = {2})",
" A customSingleton2();",
"",
" // BUG: Diagnostic contains:",
" @Foo(value2 = {2})",
" A customSingleton3();",
@@ -112,11 +120,13 @@ final class CanonicalAnnotationSyntaxTest {
" 1, 1,",
" })",
" A trailingComma1();",
"",
" // BUG: Diagnostic contains:",
" @A.Foo({",
" 1, 1,",
" })",
" A trailingComma2();",
"",
" // BUG: Diagnostic contains:",
" @Foo({",
" 1, 1,",

View File

@@ -136,6 +136,58 @@ final class DirectReturnTest {
" return variable;",
" };",
" }",
"",
" String redundantAssignmentInsideTryBlock(AutoCloseable closeable) throws Exception {",
" try (closeable) {",
" // BUG: Diagnostic contains:",
" String variable = toString();",
" return variable;",
" }",
" }",
"",
" String redundantAssignmentsInsideTryAndFinallyBlocks() {",
" String variable = toString();",
" try {",
" // BUG: Diagnostic contains:",
" variable = \"foo\";",
" return variable;",
" } finally {",
" String variable2 = toString();",
" if (true) {",
" // BUG: Diagnostic contains:",
" String variable3 = toString();",
" return variable3;",
" }",
" return variable2;",
" }",
" }",
"",
" String assignmentUsedInsideFinallyBlock() {",
" String variable = toString();",
" try {",
" variable = \"foo\";",
" return variable;",
" } finally {",
" String variable2 = toString();",
" return variable + variable2;",
" }",
" }",
"",
" String redundantAssignmentToVariableUsedInsideUnexecutedFinallyBlock(AutoCloseable closeable)",
" throws Exception {",
" String variable = toString();",
" try (closeable) {",
" if (true) {",
" // BUG: Diagnostic contains:",
" variable = \"foo\";",
" return variable;",
" }",
" }",
" try {",
" } finally {",
" return variable;",
" }",
" }",
"}")
.doTest();
}

View File

@@ -50,18 +50,21 @@ final class LexicographicalAnnotationAttributeListingTest {
"",
" @Foo({\"a\", \"b\"})",
" A sortedStrings();",
"",
" // BUG: Diagnostic contains:",
" @Foo({\"b\", \"a\"})",
" A unsortedString();",
"",
" @Foo({\"ab\", \"Ac\"})",
" A sortedStringCaseInsensitive();",
"",
" // BUG: Diagnostic contains:",
" @Foo({\"ac\", \"Ab\"})",
" A unsortedStringCaseInsensitive();",
"",
" @Foo({\"A\", \"a\"})",
" A sortedStringCaseInsensitiveWithTotalOrderFallback();",
"",
" // BUG: Diagnostic contains:",
" @Foo({\"a\", \"A\"})",
" A unsortedStringCaseInsensitiveWithTotalOrderFallback();",
@@ -86,6 +89,7 @@ final class LexicographicalAnnotationAttributeListingTest {
"",
" @Foo(cls = {int.class, long.class})",
" A sortedClasses();",
"",
" // BUG: Diagnostic contains:",
" @Foo(cls = {long.class, int.class})",
" A unsortedClasses();",
@@ -98,6 +102,7 @@ final class LexicographicalAnnotationAttributeListingTest {
"",
" @Foo(enums = {DOWN, UP})",
" A sortedEnums();",
"",
" // BUG: Diagnostic contains:",
" @Foo(enums = {UP, DOWN})",
" A unsortedEnums();",
@@ -110,15 +115,18 @@ final class LexicographicalAnnotationAttributeListingTest {
"",
" @Foo(anns = {@Bar(\"a\"), @Bar(\"b\")})",
" A sortedAnns();",
"",
" // BUG: Diagnostic contains:",
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
" A unsortedAnns();",
"",
" // BUG: Diagnostic contains:",
" @Foo(anns = {@Bar(\"a\"), @Bar({\"b\", \"a\"})})",
" A unsortedInnderAnns();",
"",
" @Foo({\"a=foo\", \"a.b=bar\", \"a.c=baz\"})",
" A hierarchicallySorted();",
"",
" // BUG: Diagnostic contains:",
" @Foo({\"a.b=bar\", \"a.c=baz\", \"a=foo\"})",
" A hierarchicallyUnsorted();",
@@ -265,12 +273,14 @@ final class LexicographicalAnnotationAttributeListingTest {
" // BUG: Diagnostic contains:",
" @Foo({\"b\", \"a\"})",
" A fooValue();",
"",
" // BUG: Diagnostic contains:",
" @Foo(value2 = {\"b\", \"a\"})",
" A fooValue2();",
"",
" @Bar({\"b\", \"a\"})",
" A barValue();",
"",
" // BUG: Diagnostic contains:",
" @Bar(value2 = {\"b\", \"a\"})",
" A barValue2();",

View File

@@ -52,6 +52,7 @@ final class LexicographicalAnnotationListingTest {
" @Foo",
" @Bar",
" A unsortedSimpleCase();",
"",
" // BUG: Diagnostic contains:",
" @Foo()",
" @Bar()",
@@ -69,6 +70,7 @@ final class LexicographicalAnnotationListingTest {
" @Baz",
" @Bar",
" A threeUnsortedAnnotationsSameInitialLetter();",
"",
" // BUG: Diagnostic contains:",
" @Bar",
" @Foo()",
@@ -84,11 +86,13 @@ final class LexicographicalAnnotationListingTest {
" @Foo({\"b\"})",
" @Bar({\"a\"})",
" A unsortedWithStringAttributes();",
"",
" // BUG: Diagnostic contains:",
" @Baz(str = {\"a\", \"b\"})",
" @Foo(ints = {1, 0})",
" @Bar",
" A unsortedWithAttributes();",
"",
" // BUG: Diagnostic contains:",
" @Bar",
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
@@ -104,6 +108,7 @@ final class LexicographicalAnnotationListingTest {
" @Foo(ints = {1, 2})",
" @Foo({\"b\"})",
" A sortedRepeatableAnnotation();",
"",
" // BUG: Diagnostic contains:",
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
" @Bar",

View File

@@ -1,89 +0,0 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.common.base.Predicates.containsPattern;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class MissingRefasterAnnotationTest {
@Test
void identification() {
CompilationTestHelper.newInstance(MissingRefasterAnnotation.class, getClass())
.expectErrorMessage(
"X",
containsPattern("The Refaster rule contains a method without any Refaster annotations"))
.addSourceLines(
"A.java",
"import com.google.errorprone.refaster.annotation.AfterTemplate;",
"import com.google.errorprone.refaster.annotation.AlsoNegation;",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"import java.util.Map;",
"",
"class A {",
" // BUG: Diagnostic matches: X",
" static final class MethodLacksBeforeTemplateAnnotation {",
" @BeforeTemplate",
" boolean before1(String string) {",
" return string.equals(\"\");",
" }",
"",
" // @BeforeTemplate is missing",
" boolean before2(String string) {",
" return string.length() == 0;",
" }",
"",
" @AfterTemplate",
" @AlsoNegation",
" boolean after(String string) {",
" return string.isEmpty();",
" }",
" }",
"",
" // BUG: Diagnostic matches: X",
" static final class MethodLacksAfterTemplateAnnotation {",
" @BeforeTemplate",
" boolean before(String string) {",
" return string.equals(\"\");",
" }",
"",
" // @AfterTemplate is missing",
" boolean after(String string) {",
" return string.isEmpty();",
" }",
" }",
"",
" // BUG: Diagnostic matches: X",
" abstract class MethodLacksPlaceholderAnnotation<K, V> {",
" // @Placeholder is missing",
" abstract V function(K key);",
"",
" @BeforeTemplate",
" void before(Map<K, V> map, K key) {",
" if (!map.containsKey(key)) {",
" map.put(key, function(key));",
" }",
" }",
"",
" @AfterTemplate",
" void after(Map<K, V> map, K key) {",
" map.computeIfAbsent(key, k -> function(k));",
" }",
" }",
"",
" static final class ValidRefasterRule {",
" @BeforeTemplate",
" void unusedPureFunctionCall(Object o) {",
" o.toString();",
" }",
" }",
"",
" static final class NotARefasterRule {",
" @Override",
" public String toString() {",
" return \"This is not a Refaster rule\";",
" }",
" }",
"}")
.doTest();
}
}

View File

@@ -0,0 +1,26 @@
package tech.picnic.errorprone.bugpatterns;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class MongoDBTextFilterUsageTest {
@Test
void identification() {
CompilationTestHelper.newInstance(MongoDBTextFilterUsage.class, getClass())
.addSourceLines(
"A.java",
"import com.mongodb.client.model.Filters;",
"import com.mongodb.client.model.TextSearchOptions;",
"",
"class A {",
" void m() {",
" Filters.eq(\"foo\", \"bar\");",
" // BUG: Diagnostic contains:",
" Filters.text(\"foo\");",
" // BUG: Diagnostic contains:",
" Filters.text(\"foo\", new TextSearchOptions());",
" }",
"}")
.doTest();
}
}

View File

@@ -36,19 +36,4 @@ final class NestedOptionalsTest {
"}")
.doTest();
}
@Test
void identificationOptionalTypeNotLoaded() {
CompilationTestHelper.newInstance(NestedOptionals.class, getClass())
.addSourceLines(
"A.java",
"import java.time.Duration;",
"",
"class A {",
" void m() {",
" Duration.ofSeconds(1);",
" }",
"}")
.doTest();
}
}

View File

@@ -0,0 +1,37 @@
package tech.picnic.errorprone.bugpatterns;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class NestedPublishersTest {
@Test
void identification() {
CompilationTestHelper.newInstance(NestedPublishers.class, getClass())
.addSourceLines(
"A.java",
"import org.reactivestreams.Publisher;",
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.GroupedFlux;",
"import reactor.core.publisher.Mono;",
"",
"class A {",
" void m() {",
" Mono.empty();",
" Flux.just(1);",
" Flux.just(1, 2).groupBy(i -> i).map(groupedFlux -> (GroupedFlux) groupedFlux);",
"",
" // BUG: Diagnostic contains:",
" Mono.just(Mono.empty());",
" // BUG: Diagnostic contains:",
" Flux.just(Flux.empty());",
" // BUG: Diagnostic contains:",
" Mono.just((Flux) Flux.just(1));",
" // BUG: Diagnostic contains:",
" Flux.just((Publisher) Mono.just(1));",
" // BUG: Diagnostic contains:",
" Mono.just(1).map(Mono::just);",
" }",
"}")
.doTest();
}
}

View File

@@ -1,8 +1,7 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
@@ -145,6 +144,6 @@ final class NonEmptyMonoTest {
" Mono.just(2).hasElement();",
" }",
"}")
.doTest(TEXT_MATCH);
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -10,12 +10,9 @@ import org.junit.jupiter.api.Test;
// `String.valueOf(null)` may not. That is because the latter matches `String#valueOf(char[])`. We
// could special-case `null` arguments, but that doesn't seem worth the trouble.
final class RedundantStringConversionTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass());
@Test
void identificationOfIdentityTransformation() {
compilationTestHelper
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"class A {",
@@ -38,7 +35,7 @@ final class RedundantStringConversionTest {
@Test
void identificationWithinMutatingAssignment() {
compilationTestHelper
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"import java.math.BigInteger;",
@@ -99,7 +96,7 @@ final class RedundantStringConversionTest {
@Test
void identificationWithinBinaryOperation() {
compilationTestHelper
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"import java.math.BigInteger;",
@@ -194,7 +191,7 @@ final class RedundantStringConversionTest {
@Test
void identificationWithinStringBuilderMethod() {
compilationTestHelper
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"import java.math.BigInteger;",
@@ -249,7 +246,7 @@ final class RedundantStringConversionTest {
// XXX: Also test the other formatter methods.
@Test
void identificationWithinFormatterMethod() {
compilationTestHelper
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"import java.util.Formattable;",
@@ -294,7 +291,7 @@ final class RedundantStringConversionTest {
@Test
void identificationWithinGuavaGuardMethod() {
compilationTestHelper
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"import static com.google.common.base.Preconditions.checkArgument;",
@@ -354,7 +351,7 @@ final class RedundantStringConversionTest {
@Test
void identificationWithinSlf4jLoggerMethod() {
compilationTestHelper
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"import java.util.Formattable;",

View File

@@ -0,0 +1,28 @@
package tech.picnic.errorprone.bugpatterns;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class RefasterExpressionTypeInvarianceTest {
@Test
void identification() {
CompilationTestHelper.newInstance(RefasterExpressionTypeInvariance.class, getClass())
.addSourceLines(
"A.java",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"",
"class A {",
" @BeforeTemplate",
" Object before(String str) {",
" if (str == null) {",
" return null;",
" }",
" if (str != null) {",
" return (CharSequence) null;",
" }",
" return str;",
" }",
"}")
.doTest();
}
}

View File

@@ -0,0 +1,25 @@
package tech.picnic.errorprone.bugpatterns;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class RefasterRefasterParameterTypeGenericsTest {
// XXX: Rename inner class.
@Test
void identification() {
CompilationTestHelper.newInstance(RefasterParameterTypeGenerics.class, getClass())
.addSourceLines(
"A.java",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"import java.util.function.Function;",
"",
"class A {",
" static class B<I, O> {",
" @BeforeTemplate",
" // BUG: Diagnostic contains:",
" void before(Function<? super I, ? extends O> function) {}",
" }",
"}")
.doTest();
}
}

View File

@@ -9,12 +9,12 @@ final class RequestMappingAnnotationTest {
CompilationTestHelper.newInstance(RequestMappingAnnotation.class, getClass())
.addSourceLines(
"A.java",
"import jakarta.servlet.http.HttpServletRequest;",
"import jakarta.servlet.http.HttpServletResponse;",
"import java.io.InputStream;",
"import java.time.ZoneId;",
"import java.util.Locale;",
"import java.util.TimeZone;",
"import javax.servlet.http.HttpServletRequest;",
"import javax.servlet.http.HttpServletResponse;",
"import org.springframework.http.HttpMethod;",
"import org.springframework.ui.Model;",
"import org.springframework.validation.BindingResult;",

View File

@@ -32,18 +32,23 @@ final class SpringMvcAnnotationTest {
"",
" @RequestMapping(method = {})",
" A explicitDefault();",
"",
" // BUG: Diagnostic contains:",
" @RequestMapping(method = RequestMethod.GET)",
" A get();",
"",
" // BUG: Diagnostic contains:",
" @RequestMapping(method = {RequestMethod.POST})",
" A post();",
"",
" // BUG: Diagnostic contains:",
" @RequestMapping(method = {PUT})",
" A put();",
"",
" // BUG: Diagnostic contains:",
" @RequestMapping(method = {DELETE})",
" A delete();",
"",
" // BUG: Diagnostic contains:",
" @RequestMapping(method = {PATCH})",
" A patch();",

View File

@@ -1,124 +0,0 @@
package tech.picnic.errorprone.bugpatterns;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class StringCaseLocaleUsageTest {
@Test
void identification() {
CompilationTestHelper.newInstance(StringCaseLocaleUsage.class, getClass())
.addSourceLines(
"A.java",
"import static java.util.Locale.ROOT;",
"",
"import java.util.Locale;",
"",
"class A {",
" void m() {",
" \"a\".toLowerCase(Locale.ROOT);",
" \"a\".toUpperCase(Locale.ROOT);",
" \"b\".toLowerCase(ROOT);",
" \"b\".toUpperCase(ROOT);",
" \"c\".toLowerCase(Locale.getDefault());",
" \"c\".toUpperCase(Locale.getDefault());",
" \"d\".toLowerCase(Locale.ENGLISH);",
" \"d\".toUpperCase(Locale.ENGLISH);",
" \"e\".toLowerCase(new Locale(\"foo\"));",
" \"e\".toUpperCase(new Locale(\"foo\"));",
"",
" // BUG: Diagnostic contains:",
" \"f\".toLowerCase();",
" // BUG: Diagnostic contains:",
" \"g\".toUpperCase();",
"",
" String h = \"h\";",
" // BUG: Diagnostic contains:",
" h.toLowerCase();",
" String i = \"i\";",
" // BUG: Diagnostic contains:",
" i.toUpperCase();",
" }",
"}")
.doTest();
}
@Test
void replacementFirstSuggestedFix() {
BugCheckerRefactoringTestHelper.newInstance(StringCaseLocaleUsage.class, getClass())
.addInputLines(
"A.java",
"class A {",
" void m() {",
" \"a\".toLowerCase(/* Comment with parens: (). */ );",
" \"b\".toUpperCase();",
" \"c\".toLowerCase().toString();",
"",
" toString().toLowerCase();",
" toString().toUpperCase /* Comment with parens: (). */();",
"",
" this.toString().toLowerCase() /* Comment with parens: (). */;",
" this.toString().toUpperCase();",
" }",
"}")
.addOutputLines(
"A.java",
"import java.util.Locale;",
"",
"class A {",
" void m() {",
" \"a\".toLowerCase(/* Comment with parens: (). */ Locale.ROOT);",
" \"b\".toUpperCase(Locale.ROOT);",
" \"c\".toLowerCase(Locale.ROOT).toString();",
"",
" toString().toLowerCase(Locale.ROOT);",
" toString().toUpperCase /* Comment with parens: (). */(Locale.ROOT);",
"",
" this.toString().toLowerCase(Locale.ROOT) /* Comment with parens: (). */;",
" this.toString().toUpperCase(Locale.ROOT);",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
@Test
void replacementSecondSuggestedFix() {
BugCheckerRefactoringTestHelper.newInstance(StringCaseLocaleUsage.class, getClass())
.setFixChooser(FixChoosers.SECOND)
.addInputLines(
"A.java",
"class A {",
" void m() {",
" \"a\".toLowerCase();",
" \"b\".toUpperCase(/* Comment with parens: (). */ );",
" \"c\".toLowerCase().toString();",
"",
" toString().toLowerCase();",
" toString().toUpperCase /* Comment with parens: (). */();",
"",
" this.toString().toLowerCase() /* Comment with parens: (). */;",
" this.toString().toUpperCase();",
" }",
"}")
.addOutputLines(
"A.java",
"import java.util.Locale;",
"",
"class A {",
" void m() {",
" \"a\".toLowerCase(Locale.getDefault());",
" \"b\".toUpperCase(/* Comment with parens: (). */ Locale.getDefault());",
" \"c\".toLowerCase(Locale.getDefault()).toString();",
"",
" toString().toLowerCase(Locale.getDefault());",
" toString().toUpperCase /* Comment with parens: (). */(Locale.getDefault());",
"",
" this.toString().toLowerCase(Locale.getDefault()) /* Comment with parens: (). */;",
" this.toString().toUpperCase(Locale.getDefault());",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -10,7 +10,7 @@ import org.junit.jupiter.params.provider.MethodSource;
final class JavaKeywordsTest {
private static Stream<Arguments> isValidIdentifierTestCases() {
/* { str, expected } */
/* { string, expected } */
return Stream.of(
arguments("", false),
arguments("public", false),
@@ -28,7 +28,7 @@ final class JavaKeywordsTest {
@MethodSource("isValidIdentifierTestCases")
@ParameterizedTest
void isValidIdentifier(String str, boolean expected) {
assertThat(JavaKeywords.isValidIdentifier(str)).isEqualTo(expected);
void isValidIdentifier(String string, boolean expected) {
assertThat(JavaKeywords.isValidIdentifier(string)).isEqualTo(expected);
}
}

View File

@@ -2,6 +2,7 @@ package tech.picnic.errorprone.bugpatterns.util;
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.BugPattern;
@@ -13,25 +14,13 @@ import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
final class MethodMatcherFactoryTest {
/** A {@link BugChecker} that flags method invocations matched by {@link #TEST_MATCHER}. */
@BugPattern(severity = SUGGESTION, summary = "Flags methods matched by the test matcher.")
public static final class MatchedMethodsFlagger extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (TEST_MATCHER.matches(tree, state)) {
return buildDescription(tree).build();
}
return Description.NO_MATCH;
}
}
private static final Matcher<ExpressionTree> TEST_MATCHER =
new MethodMatcherFactory()
.create(
@@ -40,16 +29,20 @@ final class MethodMatcherFactoryTest {
"com.example.A#m2(java.lang.String)",
"com.example.sub.B#m3(int,int)"));
@Test
void createWithMalformedSignatures() {
private static Stream<Arguments> createWithMalformedSignaturesTestCases() {
/* { signatures } */
return Stream.of(
arguments(ImmutableList.of("foo.bar")),
arguments(ImmutableList.of("foo.bar#baz")),
arguments(ImmutableList.of("a", "foo.bar#baz()")),
arguments(ImmutableList.of("foo.bar#baz()", "a")));
}
@MethodSource("createWithMalformedSignaturesTestCases")
@ParameterizedTest
void createWithMalformedSignatures(ImmutableList<String> signatures) {
MethodMatcherFactory factory = new MethodMatcherFactory();
assertThatThrownBy(() -> factory.create(ImmutableList.of("foo.bar")))
.isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> factory.create(ImmutableList.of("foo.bar#baz")))
.isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> factory.create(ImmutableList.of("a", "foo.bar#baz()")))
.isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> factory.create(ImmutableList.of("foo.bar#baz()", "a")))
assertThatThrownBy(() -> factory.create(signatures))
.isInstanceOf(IllegalArgumentException.class);
}
@@ -207,4 +200,20 @@ final class MethodMatcherFactoryTest {
"}")
.doTest();
}
/** A {@link BugChecker} that flags method invocations matched by {@link #TEST_MATCHER}. */
@BugPattern(severity = SUGGESTION, summary = "Flags methods matched by the test matcher.")
public static final class MatchedMethodsFlagger extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (TEST_MATCHER.matches(tree, state)) {
return buildDescription(tree).build();
}
return Description.NO_MATCH;
}
}
}

View File

@@ -1,21 +1,29 @@
package tech.picnic.errorprone.bugpatterns.util;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.generic;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.subOf;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.type;
import com.google.common.collect.ImmutableSet;
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.AnnotationTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.suppliers.Supplier;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree;
import org.junit.jupiter.api.Test;
final class MoreMatchersTest {
@Test
void hasMetaAnnotation() {
CompilationTestHelper.newInstance(TestMatcher.class, getClass())
CompilationTestHelper.newInstance(HasMetaAnnotationTestChecker.class, getClass())
.addSourceLines(
"A.java",
"import org.junit.jupiter.api.AfterAll;",
@@ -47,9 +55,59 @@ final class MoreMatchersTest {
.doTest();
}
/** A {@link BugChecker} that delegates to {@link MoreMatchers#hasMetaAnnotation(String)} . */
@Test
void isSubTypeOf() {
CompilationTestHelper.newInstance(IsSubTypeOfTestChecker.class, getClass())
.addSourceLines(
"A.java",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.common.collect.ImmutableSortedSet;",
"",
"class A {",
" void m() {",
" ImmutableSet.of(\"foo\");",
" ImmutableSortedSet.of(\"foo\");",
" ImmutableList.of(\"foo\");",
" ImmutableList.of(1);",
" ImmutableList.of(1.0);",
" ImmutableList.of((Number) 1);",
"",
" // BUG: Diagnostic contains:",
" ImmutableSet.of(1);",
" // BUG: Diagnostic contains:",
" ImmutableSet.of(1.0);",
" // BUG: Diagnostic contains:",
" ImmutableSet.of((Number) 1);",
" // BUG: Diagnostic contains:",
" ImmutableSortedSet.of(1);",
" // BUG: Diagnostic contains:",
" ImmutableSortedSet.of(1.0);",
" // BUG: Diagnostic contains:",
" ImmutableSortedSet.of((Number) 1);",
" }",
"}")
.doTest();
}
@Test
void isSubTypeOfBoundTypeUnknown() {
CompilationTestHelper.newInstance(IsSubTypeOfTestChecker.class, getClass())
.withClasspath()
.addSourceLines(
"A.java",
"class A {",
" void m() {",
" System.out.println(toString());",
" }",
"}")
.doTest();
}
/** A {@link BugChecker} that delegates to {@link MoreMatchers#hasMetaAnnotation(String)}. */
@BugPattern(summary = "Interacts with `MoreMatchers` for testing purposes", severity = ERROR)
public static final class TestMatcher extends BugChecker implements AnnotationTreeMatcher {
public static final class HasMetaAnnotationTestChecker extends BugChecker
implements AnnotationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<AnnotationTree> DELEGATE =
MoreMatchers.hasMetaAnnotation("org.junit.jupiter.api.TestTemplate");
@@ -59,4 +117,19 @@ final class MoreMatchersTest {
return DELEGATE.matches(tree, state) ? describeMatch(tree) : Description.NO_MATCH;
}
}
/** A {@link BugChecker} that delegates to {@link MoreMatchers#isSubTypeOf(Supplier)}. */
@BugPattern(summary = "Interacts with `MoreMatchers` for testing purposes", severity = ERROR)
public static final class IsSubTypeOfTestChecker extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<Tree> DELEGATE =
MoreMatchers.isSubTypeOf(
generic(type(ImmutableSet.class.getName()), subOf(type(Number.class.getName()))));
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
return DELEGATE.matches(tree, state) ? describeMatch(tree) : Description.NO_MATCH;
}
}
}

View File

@@ -8,22 +8,22 @@ import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.AnnotationTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import javax.lang.model.element.Name;
import org.junit.jupiter.api.Test;
final class SourceCodeTest {
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass());
@Test
void deleteWithTrailingWhitespaceAnnotations() {
refactoringTestHelper
BugCheckerRefactoringTestHelper.newInstance(
DeleteWithTrailingWhitespaceTestChecker.class, getClass())
.addInputLines("AnnotationToBeDeleted.java", "@interface AnnotationToBeDeleted {}")
.expectUnchanged()
.addInputLines(
@@ -96,7 +96,8 @@ final class SourceCodeTest {
@Test
void deleteWithTrailingWhitespaceMethods() {
refactoringTestHelper
BugCheckerRefactoringTestHelper.newInstance(
DeleteWithTrailingWhitespaceTestChecker.class, getClass())
.addInputLines(
"MethodDeletions.java",
"interface MethodDeletions {",
@@ -162,13 +163,78 @@ final class SourceCodeTest {
.doTest(TestMode.TEXT_MATCH);
}
@Test
void unwrapMethodInvocation() {
BugCheckerRefactoringTestHelper.newInstance(UnwrapMethodInvocationTestChecker.class, getClass())
.addInputLines(
"A.java",
"import com.google.common.collect.ImmutableList;",
"",
"class A {",
" Object[] m() {",
" return new Object[][] {",
" {ImmutableList.of()},",
" {ImmutableList.of(1)},",
" {com.google.common.collect.ImmutableList.of(1, 2)},",
" {",
" 0, /*a*/",
" ImmutableList /*b*/./*c*/ <Integer> /*d*/of /*e*/(/*f*/ 1 /*g*/, /*h*/ 2 /*i*/) /*j*/",
" }",
" };",
" }",
"}")
.addOutputLines(
"A.java",
"import com.google.common.collect.ImmutableList;",
"",
"class A {",
" Object[] m() {",
" return new Object[][] {{}, {1}, {1, 2}, {0, /*a*/ /*f*/ 1 /*g*/, /*h*/ 2 /*i*/ /*j*/}};",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
@Test
void unwrapMethodInvocationDroppingWhitespaceAndComments() {
BugCheckerRefactoringTestHelper.newInstance(
UnwrapMethodInvocationDroppingWhitespaceAndCommentsTestChecker.class, getClass())
.addInputLines(
"A.java",
"import com.google.common.collect.ImmutableList;",
"",
"class A {",
" Object[] m() {",
" return new Object[][] {",
" {ImmutableList.of()},",
" {ImmutableList.of(1)},",
" {com.google.common.collect.ImmutableList.of(1, 2)},",
" {",
" 0, /*a*/",
" ImmutableList /*b*/./*c*/ <Integer> /*d*/of /*e*/(/*f*/ 1 /*g*/, /*h*/ 2 /*i*/) /*j*/",
" }",
" };",
" }",
"}")
.addOutputLines(
"A.java",
"import com.google.common.collect.ImmutableList;",
"",
"class A {",
" Object[] m() {",
" return new Object[][] {{}, {1}, {1, 2}, {0, /*a*/ 1, 2 /*j*/}};",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
/**
* A {@link BugChecker} that uses {@link SourceCode#deleteWithTrailingWhitespace(Tree,
* VisitorState)} to suggest the deletion of annotations and methods with a name containing
* {@value DELETION_MARKER}.
*/
@BugPattern(severity = ERROR, summary = "Interacts with `SourceCode` for testing purposes")
public static final class TestChecker extends BugChecker
public static final class DeleteWithTrailingWhitespaceTestChecker extends BugChecker
implements AnnotationTreeMatcher, MethodTreeMatcher {
private static final long serialVersionUID = 1L;
private static final String DELETION_MARKER = "ToBeDeleted";
@@ -192,4 +258,37 @@ final class SourceCodeTest {
: Description.NO_MATCH;
}
}
/**
* A {@link BugChecker} that applies {@link
* SourceCode#unwrapMethodInvocation(MethodInvocationTree, VisitorState)} to all method
* invocations.
*/
@BugPattern(severity = ERROR, summary = "Interacts with `SourceCode` for testing purposes")
public static final class UnwrapMethodInvocationTestChecker extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
return describeMatch(tree, SourceCode.unwrapMethodInvocation(tree, state));
}
}
/**
* A {@link BugChecker} that applies {@link
* SourceCode#unwrapMethodInvocationDroppingWhitespaceAndComments(MethodInvocationTree,
* VisitorState)} to all method invocations.
*/
@BugPattern(severity = ERROR, summary = "Interacts with `SourceCode` for testing purposes")
public static final class UnwrapMethodInvocationDroppingWhitespaceAndCommentsTestChecker
extends BugChecker implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
return describeMatch(
tree, SourceCode.unwrapMethodInvocationDroppingWhitespaceAndComments(tree, state));
}
}
}

View File

@@ -18,12 +18,9 @@ import org.junit.jupiter.params.provider.ValueSource;
import reactor.core.publisher.Flux;
final class ThirdPartyLibraryTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(TestChecker.class, getClass());
@Test
void isIntroductionAllowed() {
compilationTestHelper
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.addSourceLines(
"A.java",
"// BUG: Diagnostic contains: ASSERTJ: true, GUAVA: true, NEW_RELIC_AGENT_API: true, REACTOR: true",
@@ -33,7 +30,7 @@ final class ThirdPartyLibraryTest {
@Test
void isIntroductionAllowedWitnessClassesInSymtab() {
compilationTestHelper
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.addSourceLines(
"A.java",
"import com.google.common.collect.ImmutableList;",
@@ -55,7 +52,7 @@ final class ThirdPartyLibraryTest {
@Test
void isIntroductionAllowedWitnessClassesPartiallyOnClassPath() {
compilationTestHelper
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.withClasspath(ImmutableList.class, Flux.class)
.addSourceLines(
"A.java",
@@ -66,7 +63,7 @@ final class ThirdPartyLibraryTest {
@Test
void isIntroductionAllowedWitnessClassesNotOnClassPath() {
compilationTestHelper
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.withClasspath()
.addSourceLines(
"A.java",
@@ -79,7 +76,7 @@ final class ThirdPartyLibraryTest {
@ParameterizedTest
@ValueSource(booleans = {true, false})
void isIntroductionAllowedIgnoreClasspathCompat(boolean ignoreClassPath) {
compilationTestHelper
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.setArgs("-XepOpt:ErrorProneSupport:IgnoreClasspathCompat=" + ignoreClassPath)
.withClasspath(ImmutableList.class, Flux.class)
.addSourceLines(

View File

@@ -13,7 +13,6 @@ final class RefasterRulesTest {
/** The names of all Refaster rule groups defined in this module. */
private static final ImmutableSet<Class<?>> RULE_COLLECTIONS =
ImmutableSet.of(
AssertJRules.class,
AssertJBigDecimalRules.class,
AssertJBigIntegerRules.class,
AssertJBooleanRules.class,
@@ -25,11 +24,12 @@ final class RefasterRulesTest {
AssertJFloatRules.class,
AssertJIntegerRules.class,
AssertJLongRules.class,
AssertJNumberRules.class,
AssertJMapRules.class,
AssertJNumberRules.class,
AssertJObjectRules.class,
AssertJOptionalRules.class,
AssertJPrimitiveRules.class,
AssertJRules.class,
AssertJShortRules.class,
AssertJStringRules.class,
AssertJThrowingCallableRules.class,
@@ -40,6 +40,7 @@ final class RefasterRulesTest {
ComparatorRules.class,
DoubleStreamRules.class,
EqualityRules.class,
FileRules.class,
ImmutableListRules.class,
ImmutableListMultimapRules.class,
ImmutableMapRules.class,
@@ -65,6 +66,7 @@ final class RefasterRulesTest {
RxJava2AdapterRules.class,
StreamRules.class,
StringRules.class,
SuggestedFixRules.class,
TestNGToAssertJRules.class,
TimeRules.class,
WebClientRules.class);

View File

@@ -11,7 +11,7 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class AssertJBigDecimalRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<?> elidedTypesAndStaticImports() {
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(offset(0), withPercentage(0));
}

View File

@@ -11,7 +11,7 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class AssertJBigDecimalRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<?> elidedTypesAndStaticImports() {
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(offset(0), withPercentage(0));
}

View File

@@ -11,7 +11,7 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class AssertJBigIntegerRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<?> elidedTypesAndStaticImports() {
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(offset(0), withPercentage(0));
}

View File

@@ -11,7 +11,7 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class AssertJBigIntegerRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<?> elidedTypesAndStaticImports() {
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(offset(0), withPercentage(0));
}

View File

@@ -10,7 +10,7 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class AssertJByteRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<?> elidedTypesAndStaticImports() {
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(offset(0), withPercentage(0));
}

Some files were not shown because too many files have changed in this diff Show More