Compare commits

...

120 Commits

Author SHA1 Message Date
Rick Ossendrijver
001854b01b Add dependency 2024-11-06 10:04:27 +01:00
Stephan Schroevers
c3de7e9eb9 Provide possible assignment solutions 2024-11-06 09:40:44 +01:00
Rick Ossendrijver
8b6293c21f Update version in workshop module 2024-11-06 09:37:00 +01:00
Rick Ossendrijver
bb9b7d409d Make dependency provided 2024-11-06 09:36:12 +01:00
Rick Ossendrijver
a9b440c54b Post rebase fix 2024-11-06 09:26:20 +01:00
Rick Ossendrijver
92ab6e4a4e Fix for @Generated problem with OpenRewrite 2024-11-06 09:25:15 +01:00
Rick Ossendrijver
dcd3818ea0 Add link to presentation 2024-11-06 09:25:14 +01:00
Rick Ossendrijver
dbd9aea657 Correct version in pom 2024-11-06 09:25:14 +01:00
Rick Ossendrijver
3df4b2287a Introduce trouble shooting page 2024-11-06 09:25:13 +01:00
Rick Ossendrijver
e57ee5fdc1 Updates slides and move README section 2024-11-06 09:25:13 +01:00
Rick Ossendrijver
7f4794541b Add extra explanation 2024-11-06 09:25:12 +01:00
Rick Ossendrijver
68d0635b9b Improve the third example 2024-11-06 09:25:12 +01:00
Rick Ossendrijver
296bdbde4c Introduce workshop module 2024-11-06 09:25:10 +01:00
Picnic-DevPla-Bot
bdef83bce5 Upgrade Byte Buddy 1.15.7 -> 1.15.10 (#1388)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.15.8
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.15.9
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.15.10
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.15.7...byte-buddy-1.15.10
2024-11-06 09:03:44 +01:00
Picnic-DevPla-Bot
f7f665681d Upgrade Surefire 3.5.1 -> 3.5.2 (#1396)
See:
- https://github.com/apache/maven-surefire/releases/tag/surefire-3.5.2
- https://github.com/apache/maven-surefire/compare/surefire-3.5.1...surefire-3.5.2
2024-11-05 11:36:11 +01:00
Picnic-DevPla-Bot
7c0d544cf8 Upgrade Checker Framework Annotations 3.48.1 -> 3.48.2 (#1389)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.48.2
- https://github.com/typetools/checker-framework/compare/checker-framework-3.48.1...checker-framework-3.48.2
2024-11-04 14:51:00 +01:00
Stephan Schroevers
9390b6f571 Run GitHub Actions workflows on ubuntu-24.04 (#1391)
See https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md
2024-11-03 18:36:54 +01:00
Stephan Schroevers
176a833d89 Upgrade ruby/setup-ruby v1.174.0 -> v1.199.0 (#1392)
See:
- https://github.com/ruby/setup-ruby/releases/tag/v1.175.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.176.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.177.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.178.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.179.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.180.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.181.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.182.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.183.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.184.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.185.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.186.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.187.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.188.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.189.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.190.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.191.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.192.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.193.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.194.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.195.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.196.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.197.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.198.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.199.0
- https://github.com/ruby/setup-ruby/compare/v1.174.0...v1.199.0
2024-11-03 18:17:38 +01:00
Stephan Schroevers
3cacd27248 [maven-release-plugin] prepare for next development iteration 2024-11-03 16:58:19 +01:00
Stephan Schroevers
f06a2e4d43 [maven-release-plugin] prepare release v0.19.1 2024-11-03 16:58:19 +01:00
Stephan Schroevers
cd06288f5b Fix error-prone-contrib runtime classpath (#1387) 2024-11-03 16:53:15 +01:00
Picnic-DevPla-Bot
4b458e01bd Upgrade Checkstyle 10.19.0 -> 10.20.0 (#1386)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.20.0
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.19.0...checkstyle-10.20.0
2024-11-01 09:37:37 +01:00
Stephan Schroevers
eccfc34c78 [maven-release-plugin] prepare for next development iteration 2024-10-31 20:57:03 +01:00
Stephan Schroevers
0317cb73d6 [maven-release-plugin] prepare release v0.19.0 2024-10-31 20:57:03 +01:00
Mohamed Sameh
ce6931cc36 Ignore primitive casts in ClassCastLambdaUsage check (#1385) 2024-10-30 15:37:40 +01:00
Stephan Schroevers
9940576ea8 Introduce NameContentEquals Refaster rule (#1379) 2024-10-29 10:55:55 +01:00
Mohamed Sameh
caf2a86922 Introduce ClassCastLambdaUsage check (#1381)
This new check replaces the `ClassLiteralCast` Refaster rule, as the 
latter produced invalid code by suggesting expressions of the form
`T.class::cast` for generic `T`.
2024-10-29 10:35:36 +01:00
Stephan Schroevers
507d759d02 Upgrade JDKs used by GitHub Actions builds (#1329)
Summary of changes:
- Use JDK 17.0.13 instead of 17.0.10.
- Use JDK 21.0.5 instead of 21.0.2.
- Use JDK 23.0.1 instead of 22.0.2.
- Have GitHub issue template reference more recent version numbers.

See:
- https://adoptium.net/temurin/release-notes/?version=jdk-17.0.11+9
- https://adoptium.net/temurin/release-notes/?version=jdk-17.0.12+7
- https://adoptium.net/temurin/release-notes/?version=jdk-17.0.13+11
- https://adoptium.net/temurin/release-notes/?version=jdk-21.0.3+9
- https://adoptium.net/temurin/release-notes/?version=jdk-21.0.4+7
- https://adoptium.net/temurin/release-notes/?version=jdk-21.0.5+11
- https://adoptium.net/temurin/release-notes/?version=jdk-23+37
- https://adoptium.net/temurin/release-notes/?version=jdk-23.0.1+11
2024-10-29 10:26:04 +01:00
Picnic-DevPla-Bot
8ec4936980 Upgrade NullAway 0.12.0 -> 0.12.1 (#1383)
See:
- https://github.com/uber/NullAway/blob/master/CHANGELOG.md
- https://github.com/uber/NullAway/releases/tag/v0.12.1
- https://github.com/uber/NullAway/compare/v0.12.0...v0.12.1
2024-10-29 10:16:33 +01:00
Picnic-DevPla-Bot
0f0b27abb7 Upgrade Jackson 2.18.0 -> 2.18.1 (#1382)
See:
- https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.18.1
- https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.18.0...jackson-bom-2.18.1
2024-10-29 09:43:59 +01:00
Mohamed Sameh
2b1dbd98cd Introduce Slf4jLoggerDeclaration check (#783) 2024-10-28 12:37:10 +01:00
Picnic-DevPla-Bot
e0583a8f0a Upgrade OpenRewrite Templating 1.16.2 -> 1.16.3 (#1380)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.16.3
- https://github.com/openrewrite/rewrite-templating/compare/v1.16.2...v1.16.3
2024-10-28 08:44:43 +01:00
Mohamed Sameh
21437a43aa Introduce ConstantNaming check (#794)
This check flags static constants that do not follow the upper snake
case naming convention.

While there, introduce the `Flags#getSet` utility method, and use it to
replace `Flags#getList` in relevant places.
2024-10-27 14:23:59 +01:00
Stephan Schroevers
cf8af8c5cf Use dynamic Reproducible Builds badge (#1378) 2024-10-27 13:19:46 +01:00
Picnic-DevPla-Bot
07bd4b0b54 Upgrade maven-checkstyle-plugin 3.5.0 -> 3.6.0 (#1377)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MCHECKSTYLE%20AND%20fixVersion%20%3E%203.5.0%20AND%20fixVersion%20%3C%3D%203.6.0
- https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.5.0...maven-checkstyle-plugin-3.6.0
2024-10-27 12:46:41 +01:00
Picnic-DevPla-Bot
d1765fea0e Upgrade maven-dependency-plugin 3.8.0 -> 3.8.1 (#1375)
See:
- https://github.com/apache/maven-dependency-plugin/releases/tag/maven-dependency-plugin-3.8.1
- https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.8.0...maven-dependency-plugin-3.8.1
2024-10-27 12:37:15 +01:00
Picnic-DevPla-Bot
5922c5b032 Upgrade Checkstyle 10.18.2 -> 10.19.0 (#1376)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.19.0
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.18.2...checkstyle-10.19.0
2024-10-27 12:12:35 +01:00
Danylo Naumenko
56b60f5cf6 Introduce MicrometerRules Refaster rule collection (#1365) 2024-10-26 14:56:11 +02:00
Mohamed Sameh
be6b17b7dc Introduce ImmutableEnumSetRules Refaster rule collection (#1302) 2024-10-26 14:01:25 +02:00
Picnic-DevPla-Bot
f782ec2d8f Upgrade Spring Boot 3.3.4 -> 3.3.5 (#1374)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v3.3.5
- https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.3.5
2024-10-26 13:36:32 +02:00
Picnic-DevPla-Bot
6e88e3cea8 Upgrade Error Prone 2.33.0 -> 2.35.1 (#1363)
See:
- https://github.com/google/error-prone/releases/tag/v2.34.0
- https://github.com/google/error-prone/releases/tag/v2.35.0
- https://github.com/google/error-prone/releases/tag/v2.35.1
- https://github.com/google/error-prone/compare/v2.33.0...v2.35.1
- https://github.com/PicnicSupermarket/error-prone/compare/v2.33.0-picnic-2...v2.35.1-picnic-1
2024-10-26 13:20:13 +02:00
Picnic-DevPla-Bot
09fb21358a Upgrade Project Reactor 2023.0.10 -> 2023.0.11 (#1359)
See:
- https://github.com/reactor/reactor/releases/tag/2023.0.11
- https://github.com/reactor/reactor/compare/2023.0.10...2023.0.11
2024-10-26 11:53:55 +02:00
Picnic-DevPla-Bot
c27a626042 Upgrade OpenRewrite 2.21.0 -> 2.21.1 (#1369)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v2.21.1
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.21.0...v2.21.1
2024-10-26 11:33:07 +02:00
Picnic-DevPla-Bot
da33e800fa Upgrade Spring Security 6.3.3 -> 6.3.4 (#1367)
See:
- https://github.com/spring-projects/spring-security/releases/tag/6.3.4
- https://github.com/spring-projects/spring-security/compare/6.3.3...6.3.4
2024-10-25 23:50:12 +02:00
Picnic-DevPla-Bot
bc13976cc7 Upgrade OpenRewrite Templating 1.16.1 -> 1.16.2 (#1370)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.16.2
- https://github.com/openrewrite/rewrite-templating/compare/v1.16.1...v1.16.2
2024-10-25 23:36:02 +02:00
Picnic-DevPla-Bot
7f1f0656fd Upgrade maven-site-plugin 3.20.0 -> 3.21.0 (#1371)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MSITE%20AND%20fixVersion%20%3E%203.20.0%20AND%20fixVersion%20%3C%3D%203.21.0
- https://github.com/apache/maven-site-plugin/compare/maven-site-plugin-3.20.0...maven-site-plugin-3.21.0
2024-10-25 23:26:57 +02:00
Picnic-DevPla-Bot
4e99382f42 Upgrade pomchecker-maven-plugin 1.13.0 -> 1.14.0 (#1372)
See:
- https://github.com/kordamp/pomchecker/releases/tag/v1.14.0
- https://github.com/kordamp/pomchecker/compare/v1.13.0...v1.14.0
2024-10-25 22:50:26 +02:00
Picnic-DevPla-Bot
9aa03aa842 Upgrade JUnit 5 5.11.2 -> 5.11.3 (#1366)
See:
- https://junit.org/junit5/docs/current/release-notes/
- https://github.com/junit-team/junit5/releases/tag/r5.11.3
- https://github.com/junit-team/junit5/compare/r5.11.2...r5.11.3
2024-10-25 22:39:48 +02:00
Picnic-DevPla-Bot
71cc09a7e7 Upgrade Spring 6.1.13 -> 6.1.14 (#1362)
See:
- https://github.com/spring-projects/spring-framework/releases/tag/v6.1.14
- https://github.com/spring-projects/spring-framework/compare/v6.1.13...v6.1.14
2024-10-25 17:07:18 +02:00
Picnic-DevPla-Bot
9b4cbfb84d Upgrade Byte Buddy 1.15.5 -> 1.15.7 (#1368)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.15.6
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.15.7
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.15.5...byte-buddy-1.15.7
2024-10-24 09:01:49 +02:00
Picnic-DevPla-Bot
f2238c779c Upgrade Guava 33.3.0-jre -> 33.3.1-jre (#1336)
And drop some Refaster rules now covered by `@InlineMe` instructions.

See:
- https://guava.dev/releases/33.3.1-jre/api/diffs/
- https://github.com/google/guava/releases/tag/v33.3.1
- https://github.com/google/guava/compare/v33.3.0...v33.3.1
2024-10-23 10:06:19 +02:00
Picnic-DevPla-Bot
6e893e9869 Upgrade fmt-maven-plugin 2.24 -> 2.25 (#1356)
See:
- https://github.com/spotify/fmt-maven-plugin/releases/tag/2.25
- https://github.com/spotify/fmt-maven-plugin/compare/2.24...2.25
2024-10-23 09:30:41 +02:00
Picnic-DevPla-Bot
7ee8826e96 Upgrade NullAway 0.11.3 -> 0.12.0 (#1364)
See:
- https://github.com/uber/NullAway/blob/master/CHANGELOG.md
- https://github.com/uber/NullAway/releases/tag/v0.12.0
- https://github.com/uber/NullAway/compare/v0.11.3...v0.12.0
2024-10-20 12:35:33 +02:00
Picnic-DevPla-Bot
a0689f62b4 Upgrade Byte Buddy 1.15.4 -> 1.15.5 (#1360)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.15.5
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.15.4...byte-buddy-1.15.5
2024-10-18 13:53:23 +02:00
Picnic-DevPla-Bot
1b00c87b2f Upgrade Mockito 5.14.0 -> 5.14.2 (#1361)
See:
- https://github.com/mockito/mockito/releases/tag/v5.14.1
- https://github.com/mockito/mockito/releases/tag/v5.14.2
- https://github.com/mockito/mockito/compare/v5.14.0...v5.14.2
2024-10-18 13:23:57 +02:00
Picnic-DevPla-Bot
0bce1a0e29 Upgrade Mockito 5.13.0 -> 5.14.0 (#1345)
And configure its Java agent such that Byte Buddy does not need to use
the deprecated self-attach mechanism.

See:
- https://github.com/mockito/mockito/releases/tag/v5.14.0
- https://github.com/mockito/mockito/compare/v5.13.0...v5.14.0
2024-10-17 15:11:27 +02:00
Picnic-DevPla-Bot
ce18b0c058 Upgrade Google Java Format 1.23.0 -> 1.24.0 (#1352)
See:
- https://github.com/google/google-java-format/releases/tag/v1.24.0
- https://github.com/google/google-java-format/compare/v1.23.0...v1.24.0
2024-10-16 20:53:44 +02:00
Picnic-DevPla-Bot
4f2c143b9c Upgrade maven-gpg-plugin 3.2.6 -> 3.2.7 (#1344)
See:
- https://github.com/apache/maven-gpg-plugin/releases/tag/maven-gpg-plugin-3.2.7
- https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.6...maven-gpg-plugin-3.2.7
2024-10-16 19:07:41 +02:00
Picnic-DevPla-Bot
f4740e0e64 Upgrade JUnit 5 5.11.1 -> 5.11.2 (#1351)
See:
- https://junit.org/junit5/docs/current/release-notes/
- https://github.com/junit-team/junit5/releases/tag/r5.11.2
- https://github.com/junit-team/junit5/compare/r5.11.1...r5.11.2
2024-10-15 09:07:40 +02:00
Picnic-DevPla-Bot
3a155169ef Upgrade MongoDB driver 5.1.4 -> 5.2.0 (#1341)
See:
- https://jira.mongodb.org/issues/?jql=project%20%3D%20JAVA%20AND%20fixVersion%20%3E%205.1.4%20AND%20fixVersion%20%3C%3D%205.2.0
- https://github.com/mongodb/mongo-java-driver/releases/tag/r5.2.0
- https://github.com/mongodb/mongo-java-driver/compare/r5.1.4...r5.2.0
2024-10-14 14:22:27 +02:00
Picnic-DevPla-Bot
13847f6d2c Upgrade Jackson 2.17.2 -> 2.18.0 (#1343)
See:
- https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.18
- https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.17.2...jackson-bom-2.18.0
2024-10-14 13:26:24 +02:00
Picnic-DevPla-Bot
6a81b92e11 Upgrade Surefire 3.5.0 -> 3.5.1 (#1354)
See:
- https://github.com/apache/maven-surefire/releases/tag/surefire-3.5.1
- https://github.com/apache/maven-surefire/compare/surefire-3.5.0...surefire-3.5.1
2024-10-14 12:06:14 +02:00
Stephan Schroevers
5794b404f2 Update Error Prone compatibility matrix (#1358) 2024-10-14 09:53:24 +02:00
Picnic-DevPla-Bot
b2d7ed4dc7 Upgrade Forbidden APIs plugin 3.7 -> 3.8 (#1355)
See:
- https://github.com/policeman-tools/forbidden-apis/wiki/Changes
- https://github.com/policeman-tools/forbidden-apis/compare/3.7...3.8
2024-10-14 07:03:03 +02:00
Picnic-DevPla-Bot
0fdc102aa5 Upgrade Swagger 2.2.23 -> 2.2.25 (#1337)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.24
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.25
- https://github.com/swagger-api/swagger-core/compare/v2.2.23...v2.2.25
2024-10-13 20:52:45 +02:00
Picnic-DevPla-Bot
ce3cf6a2d0 Upgrade maven-javadoc-plugin 3.10.0 -> 3.10.1 (#1349)
See:
- https://github.com/apache/maven-javadoc-plugin/releases/tag/maven-javadoc-plugin-3.10.1
- https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.10.0...maven-javadoc-plugin-3.10.1
2024-10-13 18:22:35 +02:00
Picnic-DevPla-Bot
0f657f8835 Upgrade OpenRewrite 2.19.0 -> 2.21.0 (#1338)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v2.20.0
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v2.21.0
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.19.0...v2.21.0
2024-10-13 16:52:43 +02:00
Picnic-DevPla-Bot
5f56f43c40 Upgrade OpenRewrite Templating 1.15.0 -> 1.16.1 (#1339)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.16.0
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.16.1
- https://github.com/openrewrite/rewrite-templating/compare/v1.15.0...v1.16.1
2024-10-13 16:03:59 +02:00
Picnic-DevPla-Bot
ef6faf518a Upgrade Checker Framework Annotations 3.47.0 -> 3.48.1 (#1350)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.48.0
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.48.1
- https://github.com/typetools/checker-framework/compare/checker-framework-3.47.0...checker-framework-3.48.1
2024-10-13 15:55:12 +02:00
Picnic-DevPla-Bot
aa08d954a0 Upgrade Byte Buddy 1.15.1 -> 1.15.4 (#1340)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.15.2
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.15.3
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.15.4
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.15.1...byte-buddy-1.15.4
2024-10-13 15:43:21 +02:00
Picnic-DevPla-Bot
e4d1818ed0 Upgrade Error Prone 2.32.0 -> 2.33.0 (#1348)
See:
- https://github.com/google/error-prone/releases/tag/v2.33.0
- https://github.com/google/error-prone/compare/v2.32.0...v2.33.0
- https://github.com/PicnicSupermarket/error-prone/compare/v2.32.0-picnic-1...v2.33.0-picnic-2
2024-10-13 15:27:01 +02:00
Mohamed Sameh
beb96f0f4b Introduce CollectionIterator Refaster rule (#1347)
This rule supersedes the more specific `ImmutableCollectionIterator` rule.
2024-10-01 10:05:33 +02:00
Picnic-DevPla-Bot
4d1eeb2be1 Upgrade Checkstyle 10.18.1 -> 10.18.2 (#1346)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.18.2
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.18.1...checkstyle-10.18.2
2024-09-30 14:04:53 +02:00
Stephan Schroevers
9cbc6f875c Have LexicographicalAnnotationAttributeListing also sort booleans and chars (#1334) 2024-09-30 06:38:44 +02:00
Picnic-DevPla-Bot
5583630fb4 Upgrade JUnit 5 5.11.0 -> 5.11.1 (#1342)
See:
- https://junit.org/junit5/docs/current/release-notes/
- https://github.com/junit-team/junit5/releases/tag/r5.11.1
- https://github.com/junit-team/junit5/compare/r5.11.0...r5.11.1
2024-09-27 14:17:50 +02:00
Picnic-DevPla-Bot
91e841ce12 Upgrade maven-gpg-plugin 3.2.5 -> 3.2.6 (#1331)
See:
- https://github.com/apache/maven-gpg-plugin/releases/tag/maven-gpg-plugin-3.2.6
- https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.5...maven-gpg-plugin-3.2.6
2024-09-24 11:40:34 +02:00
Picnic-DevPla-Bot
3956a82cd5 Upgrade extra-enforcer-rules 1.8.0 -> 1.9.0 (#1335)
See:
- https://github.com/mojohaus/extra-enforcer-rules/releases/tag/1.9.0
- https://github.com/mojohaus/extra-enforcer-rules/compare/1.8.0...1.9.0
2024-09-23 10:26:57 +02:00
Picnic-DevPla-Bot
c57debdc25 Upgrade maven-site-plugin 3.12.1 -> 3.20.0 (#1296)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MSITE%20AND%20fixVersion%20%3E%203.12.1%20AND%20fixVersion%20%3C%3D%203.20.0
- https://github.com/apache/maven-site-plugin/releases/tag/maven-site-plugin-3.20.0
- https://github.com/apache/maven-site-plugin/compare/maven-site-plugin-3.12.1...maven-site-plugin-3.20.0
2024-09-23 09:46:35 +02:00
Picnic-DevPla-Bot
6a13efded8 Upgrade Spring Boot 3.3.3 -> 3.3.4 (#1333)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v3.3.4
- https://github.com/spring-projects/spring-boot/compare/v3.3.3...v3.3.4
2024-09-20 22:27:00 +02:00
Picnic-DevPla-Bot
432cf4560d Upgrade pitest-maven-plugin 1.16.1 -> 1.17.0 (#1313)
See:
- https://github.com/hcoles/pitest/releases/tag/1.16.2
- https://github.com/hcoles/pitest/releases/tag/1.16.3
- https://github.com/hcoles/pitest/releases/tag/1.17.0
- https://github.com/hcoles/pitest/compare/1.16.1...1.17.0
2024-09-19 13:03:04 +02:00
Picnic-DevPla-Bot
966fb36ac9 Upgrade Spring 6.1.12 -> 6.1.13 (#1328)
See:
- https://github.com/spring-projects/spring-framework/releases/tag/v6.1.13
- https://github.com/spring-projects/spring-framework/compare/v6.1.12...v6.1.13
2024-09-17 11:55:54 +02:00
Picnic-DevPla-Bot
c63d9350d2 Upgrade OpenRewrite Templating 1.14.1 -> 1.15.0 (#1327)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.15.0
- https://github.com/openrewrite/rewrite-templating/compare/v1.14.1...v1.15.0
2024-09-17 11:37:37 +02:00
Picnic-DevPla-Bot
e74874b04c Upgrade OpenRewrite 2.18.0 -> 2.19.0 (#1326)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v2.19.0
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.18.0...v2.19.0
2024-09-17 11:17:35 +02:00
Picnic-DevPla-Bot
f821d3775b Upgrade NullAway 0.11.2 -> 0.11.3 (#1332)
See:
- https://github.com/uber/NullAway/blob/master/CHANGELOG.md
- https://github.com/uber/NullAway/releases/tag/v0.11.3
- https://github.com/uber/NullAway/compare/v0.11.2...v0.11.3
2024-09-17 10:48:58 +02:00
Stephan Schroevers
3d5ee10d93 Introduce Refaster rules for Sonar's java:S4635 rule (#1320)
As well as two related expressions that can be optimized.

See:
- https://sonarcloud.io/organizations/picnic-technologies/rules?open=java%3AS4635&rule_key=java%3AS4635
- 2615792731/java-checks/src/main/java/org/sonar/java/checks/StringOffsetMethodsCheck.java
2024-09-16 09:15:12 +02:00
Stephan Schroevers
26da67d1f5 Use rewrite-java-17 rather than rewrite-java-11 (#1330) 2024-09-16 08:02:56 +02:00
Stephan Schroevers
b3ca01a6c7 Introduce RefasterRuleTestExtractor for documentation generation (#1317)
This new `Extractor` implementation collects Refaster example input and 
output code from rule collection tests.

This change also introduces explicit compilation steps for the test
code. As a side-effect this produces faster feedback in case of invalid
input or output code.
2024-09-14 12:02:37 +02:00
Picnic-DevPla-Bot
9f222e9efe Upgrade Error Prone 2.31.0 -> 2.32.0 (#1325)
See:
- https://github.com/google/error-prone/releases/tag/v2.32.0
- https://github.com/google/error-prone/compare/v2.31.0...v2.32.0
- https://github.com/PicnicSupermarket/error-prone/compare/v2.31.0-picnic-1...v2.32.0-picnic-1
2024-09-13 09:44:18 +02:00
Picnic-DevPla-Bot
097af51a3e Upgrade Project Reactor 2023.0.9 -> 2023.0.10 (#1323)
See:
- https://github.com/reactor/reactor/releases/tag/2023.0.10
- https://github.com/reactor/reactor/compare/2023.0.9...2023.0.10
2024-09-11 15:00:08 +02:00
Picnic-DevPla-Bot
c62e6c1127 Upgrade errorprone-slf4j 0.1.25 -> 0.1.28 (#1324)
See:
- https://github.com/KengoTODA/errorprone-slf4j/releases/tag/v0.1.28
- https://github.com/KengoTODA/errorprone-slf4j/compare/v0.1.25...v0.1.28
2024-09-11 12:55:23 +02:00
Picnic-DevPla-Bot
5960423c4e Upgrade maven-dependency-plugin 3.7.1 -> 3.8.0 (#1304)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MDEP%20AND%20fixVersion%20%3E%203.7.1%20AND%20fixVersion%20%3C%3D%203.8.0
- https://github.com/apache/maven-dependency-plugin/releases/tag/maven-dependency-plugin-3.8.0
- https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.7.1...maven-dependency-plugin-3.8.0
2024-09-11 09:42:34 +02:00
Picnic-DevPla-Bot
188715c3c0 Upgrade Checkstyle 10.18.0 -> 10.18.1 (#1318)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.18.1
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.18.0...checkstyle-10.18.1
2024-09-11 09:25:26 +02:00
Picnic-DevPla-Bot
fb45bb00ed Upgrade maven-javadoc-plugin 3.8.0 -> 3.10.0 (#1310)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MJAVADOC%20AND%20fixVersion%20%3E%203.8.0%20AND%20fixVersion%20%3C%3D%203.10.0
- https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.8.0...maven-javadoc-plugin-3.10.0
2024-09-10 19:55:17 +02:00
Picnic-DevPla-Bot
fd5fc913ce Upgrade maven-install-plugin 3.1.2 -> 3.1.3 (#1301)
See:
- https://github.com/apache/maven-install-plugin/releases/tag/maven-install-plugin-3.1.3
- https://github.com/apache/maven-install-plugin/compare/maven-install-plugin-3.1.2...maven-install-plugin-3.1.3
2024-09-10 19:44:45 +02:00
Picnic-DevPla-Bot
ae20c6069d Upgrade OpenRewrite 2.17.0 -> 2.18.0 (#1315)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v2.18.0
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.17.0...v2.18.0
2024-09-10 19:14:25 +02:00
Picnic-DevPla-Bot
8457bd5026 Upgrade MongoDB driver 5.1.3 -> 5.1.4 (#1321)
See:
- https://jira.mongodb.org/issues/?jql=project%20%3D%20JAVA%20AND%20fixVersion%20%3E%204.11.3%20AND%20fixVersion%20%3C%3D%204.11.4
- https://github.com/mongodb/mongo-java-driver/releases/tag/r5.1.4
- https://github.com/mongodb/mongo-java-driver/compare/r5.1.3...r5.1.4
2024-09-10 18:07:43 +02:00
Picnic-DevPla-Bot
fcfb97b0e0 Upgrade maven-deploy-plugin 3.1.2 -> 3.1.3 (#1300)
See:
- https://github.com/apache/maven-deploy-plugin/releases/tag/maven-deploy-plugin-3.1.3
- https://github.com/apache/maven-deploy-plugin/compare/maven-deploy-plugin-3.1.2...maven-deploy-plugin-3.1.3
2024-09-10 17:57:22 +02:00
Picnic-DevPla-Bot
15680b4cb3 Upgrade Surefire 3.3.1 -> 3.5.0 (#1298)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20SUREFIRE%20AND%20fixVersion%20%3E%203.3.1%20AND%20fixVersion%20%3C%3D%203.5.0
- https://github.com/apache/maven-surefire/releases/tag/surefire-3.4.0
- https://github.com/apache/maven-surefire/releases/tag/surefire-3.5.0
- https://github.com/apache/maven-surefire/compare/surefire-3.3.1...surefire-3.5.0
2024-09-10 17:05:51 +02:00
Picnic-DevPla-Bot
afebfbf478 Upgrade pomchecker-maven-plugin 1.11.0 -> 1.13.0 (#1319)
See:
- https://github.com/kordamp/pomchecker/releases/tag/v1.12.0
- https://github.com/kordamp/pomchecker/releases/tag/v1.13.0
- https://github.com/kordamp/pomchecker/compare/v1.11.0...v1.13.0
2024-09-10 13:32:08 +02:00
Picnic-DevPla-Bot
fe2ac938f3 Upgrade Checker Framework Annotations 3.46.0 -> 3.47.0 (#1322)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.47.0
- https://github.com/typetools/checker-framework/compare/checker-framework-3.46.0...checker-framework-3.47.0
2024-09-10 10:55:36 +02:00
Picnic-DevPla-Bot
078d8c16fa Upgrade OpenRewrite Templating 1.14.0 -> 1.14.1 (#1311)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.14.1
- https://github.com/openrewrite/rewrite-templating/compare/v1.14.0...v1.14.1
2024-09-04 15:01:36 +02:00
Stephan Schroevers
c679a3fc0c Improve Optional#orElse{,Get} support (#1283)
Summary of changes:
- Consolidate the `OptionalOrElseGet` Refaster rule and the 
  `OptionalOrElse` bug checker into the latter, keeping the best of
  both.
- Rename the `OptionalOrElse` bug checker to `OptionalOrElseGet` to
  avoid confusion.
- Replace the `IsLikelyTrivialComputation` matcher with the inverse
  `RequiresComputation` variant.
- Introduce an `OptionalOrElse` Refaster rule that simplifies
  expressions in the "opposite direction".
2024-09-03 16:00:33 +02:00
Stephan Schroevers
de54b4bf64 Introduce assorted unsigned int/long Refaster rules (#1291) 2024-09-03 15:35:15 +02:00
Picnic-DevPla-Bot
ea60241782 Upgrade Maven 3.9.8 -> 3.9.9 (#1295)
See:
- https://maven.apache.org/release-notes-all.html
- https://github.com/apache/maven/compare/maven-3.9.8...maven-3.9.9
2024-09-03 15:26:41 +02:00
Picnic-Bot
e4f928addb Upgrade fmt-maven-plugin 2.22.1 -> 2.24 (#1069)
See:
- https://github.com/spotify/fmt-maven-plugin/releases/tag/2.23
- https://github.com/spotify/fmt-maven-plugin/releases/tag/2.24
- https://github.com/spotify/fmt-maven-plugin/compare/2.22.1...2.24
2024-09-03 13:13:18 +02:00
Picnic-DevPla-Bot
059cc9e2db Upgrade Error Prone 2.30.0 -> 2.31.0 (#1314)
See:
- https://github.com/google/error-prone/releases/tag/v2.31.0
- https://github.com/google/error-prone/compare/v2.30.0...v2.31.0
2024-09-03 12:19:04 +02:00
Picnic-DevPla-Bot
1e43c28c95 Upgrade Byte Buddy 1.15.0 -> 1.15.1 (#1316)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.15.1
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.15.0...byte-buddy-1.15.1
2024-09-02 21:10:41 +02:00
Picnic-DevPla-Bot
a07e9b3115 Upgrade Swagger 2.2.22 -> 2.2.23 (#1312)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.23
- https://github.com/swagger-api/swagger-core/compare/v2.2.22...v2.2.23
2024-08-29 09:23:09 +02:00
Picnic-DevPla-Bot
aec38d2e33 Upgrade Mockito 5.12.0 -> 5.13.0 (#1309)
See:
- https://github.com/mockito/mockito/releases/tag/v5.13.0
- https://github.com/mockito/mockito/compare/v5.12.0...v5.13.0
2024-08-28 09:32:16 +02:00
Picnic-DevPla-Bot
5b77663288 Upgrade Spring 6.1.11 -> 6.1.12 (#1288)
See:
- https://github.com/spring-projects/spring-framework/releases/tag/v6.1.12
- https://github.com/spring-projects/spring-framework/compare/v6.1.11...v6.1.12
2024-08-27 15:48:14 +02:00
Stephan Schroevers
97c2bbd4b1 Introduce MonoFromFutureAsyncLoadingCacheGet Refaster rule (#1290) 2024-08-26 16:33:57 +02:00
Picnic-DevPla-Bot
9c8fbfd36a Upgrade OpenRewrite Templating 1.13.0 -> 1.14.0 (#1294)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.14.0
- https://github.com/openrewrite/rewrite-templating/compare/v1.13.0...v1.14.0
2024-08-26 13:42:53 +02:00
Picnic-DevPla-Bot
38e6c3fdb5 Upgrade OpenRewrite 2.16.0 -> 2.17.0 (#1286)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v2.17.0
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.16.0...v2.17.0
2024-08-26 13:24:42 +02:00
Picnic-DevPla-Bot
5b1d82cfeb Upgrade Guava 33.2.1-jre -> 33.3.0-jre (#1293)
And drop some Refaster rules now covered by `@InlineMe` instructions.

See:
- https://guava.dev/releases/33.3.0-jre/api/diffs/
- https://github.com/google/guava/releases/tag/v33.3.0
- https://github.com/google/guava/compare/v33.2.0...v33.3.0
2024-08-26 13:02:15 +02:00
Picnic-DevPla-Bot
0821a95fcc Upgrade Spring Security 6.3.1 -> 6.3.3 (#1303)
See:
- https://github.com/spring-projects/spring-security/releases/tag/6.3.2
- https://github.com/spring-projects/spring-security/releases/tag/6.3.3
- https://github.com/spring-projects/spring-security/compare/6.3.1...6.3.3
2024-08-26 12:50:25 +02:00
Picnic-DevPla-Bot
f4afe457cb Upgrade Checkstyle 10.17.0 -> 10.18.0 (#1308)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.18.0
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.17.0...checkstyle-10.18.0
2024-08-26 09:19:52 +02:00
Picnic-DevPla-Bot
fd56ca8b6e Upgrade Spring Boot 3.3.2 -> 3.3.3 (#1305)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v3.3.3
- https://github.com/spring-projects/spring-boot/compare/v3.3.2...v3.3.3
2024-08-26 09:04:43 +02:00
Picnic-DevPla-Bot
882794d63b Upgrade maven-checkstyle-plugin 3.4.0 -> 3.5.0 (#1307)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MCHECKSTYLE%20AND%20fixVersion%20%3E%203.4.0%20AND%20fixVersion%20%3C%3D%203.5.0
- https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.4.0...maven-checkstyle-plugin-3.5.0
2024-08-26 08:22:34 +02:00
Mohamed Sameh
73ed6e32c7 Introduce Collections{Max,Min,Sort} Refaster rules (#1297)
While there, rename `{Max,Min}OfCollection` to
`Collections{Max,Min}WithComparator`.
2024-08-26 07:38:26 +02:00
Picnic-DevPla-Bot
ca5c3dd3b4 Upgrade Byte Buddy 1.14.19 -> 1.15.0 (#1306)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.15.0
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.19...byte-buddy-1.15.0
2024-08-26 07:28:00 +02:00
128 changed files with 5145 additions and 622 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.10`).
- Error Prone version (e.g. `2.25.0`).
- Error Prone Support version (e.g. `0.15.0`).
- Java version (i.e. `java --version`, e.g. `17.0.13`).
- Error Prone version (e.g. `2.35.1`).
- Error Prone Support version (e.g. `0.19.0`).
### Additional context

View File

@@ -9,17 +9,17 @@ jobs:
build:
strategy:
matrix:
os: [ ubuntu-22.04 ]
jdk: [ 17.0.10, 21.0.2, 22.0.2 ]
os: [ ubuntu-24.04 ]
jdk: [ 17.0.13, 21.0.5, 23.0.1 ]
distribution: [ temurin ]
experimental: [ false ]
include:
- os: macos-14
jdk: 17.0.10
jdk: 17.0.13
distribution: temurin
experimental: false
- os: windows-2022
jdk: 17.0.10
jdk: 17.0.13
distribution: temurin
experimental: false
runs-on: ${{ matrix.os }}
@@ -46,7 +46,7 @@ jobs:
with:
java-version: ${{ matrix.jdk }}
java-distribution: ${{ matrix.distribution }}
maven-version: 3.9.8
maven-version: 3.9.9
- name: Display build environment details
run: mvn --version
- name: Build project against vanilla Error Prone, compile Javadoc

View File

@@ -19,7 +19,7 @@ jobs:
permissions:
contents: read
security-events: write
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
@@ -36,9 +36,9 @@ jobs:
- name: Check out code and set up JDK and Maven
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
with:
java-version: 17.0.10
java-version: 17.0.13
java-distribution: temurin
maven-version: 3.9.8
maven-version: 3.9.9
- name: Initialize CodeQL
uses: github/codeql-action/init@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
with:

View File

@@ -9,7 +9,7 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
@@ -42,7 +42,7 @@ jobs:
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with:
persist-credentials: false
- uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0
- uses: ruby/setup-ruby@7d3497fd78c07c0d84ebafa58d8dac60cd1f0763 # v1.199.0
with:
working-directory: ./website
bundler-cache: true
@@ -68,7 +68,7 @@ jobs:
permissions:
id-token: write
pages: write
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

View File

@@ -18,7 +18,7 @@ jobs:
contents: read
security-events: write
id-token: write
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0

View File

@@ -9,7 +9,7 @@ permissions:
contents: read
jobs:
analyze-pr:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
@@ -25,9 +25,9 @@ jobs:
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
with:
checkout-fetch-depth: 2
java-version: 17.0.10
java-version: 17.0.13
java-distribution: temurin
maven-version: 3.9.8
maven-version: 3.9.9
- name: Run Pitest
# By running with features `+GIT(from[HEAD~1]), +gitci`, Pitest only
# analyzes lines changed in the associated pull request, as GitHub

View File

@@ -17,7 +17,7 @@ jobs:
checks: write
contents: read
pull-requests: write
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
@@ -33,9 +33,9 @@ jobs:
- name: Check out code and set up JDK and Maven
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
with:
java-version: 17.0.10
java-version: 17.0.13
java-distribution: temurin
maven-version: 3.9.8
maven-version: 3.9.9
- name: Download Pitest analysis artifact
uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4
with:

View File

@@ -16,7 +16,7 @@ jobs:
name: On-demand integration test
if: |
github.event.issue.pull_request && contains(github.event.comment.body, '/integration-test')
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
@@ -36,9 +36,9 @@ jobs:
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
with:
checkout-ref: "refs/pull/${{ github.event.issue.number }}/head"
java-version: 17.0.10
java-version: 17.0.13
java-distribution: temurin
maven-version: 3.9.8
maven-version: 3.9.9
- name: Install project to local Maven repository
run: mvn -T1C install -DskipTests -Dverification.skip
- name: Run integration test

View File

@@ -16,7 +16,7 @@ jobs:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
permissions:
contents: read
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
@@ -38,9 +38,9 @@ jobs:
uses: s4u/setup-maven-action@489441643219d2b93ee2a127b2402eb640a1b947 # v1.13.0
with:
checkout-fetch-depth: 0
java-version: 17.0.10
java-version: 17.0.13
java-distribution: temurin
maven-version: 3.9.8
maven-version: 3.9.9
- name: Create missing `test` directory
# XXX: Drop this step in favour of actually having a test.
run: mkdir refaster-compiler/src/test

BIN
.mvn/wrapper/maven-wrapper.jar vendored Normal file

Binary file not shown.

18
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@@ -0,0 +1,18 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar

View File

@@ -302,7 +302,7 @@ channel; please see our [security policy][security] for details.
[refaster]: https://errorprone.info/docs/refaster
[refaster-rules-bigdecimal]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/BigDecimalRules.java
[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-badge]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/jvm-repo-rebuild/reproducible-central/master/content/tech/picnic/error-prone-support/error-prone-support/badge.json
[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

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.18.1-SNAPSHOT</version>
<version>0.19.2-SNAPSHOT</version>
</parent>
<artifactId>documentation-support</artifactId>
@@ -33,6 +33,15 @@
<artifactId>error_prone_test_helpers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>error-prone-utils</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-test-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>

View File

@@ -27,7 +27,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.TestCases;
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.BugPatternTestCases;
/**
* An {@link Extractor} that describes how to extract data from classes that test a {@code
@@ -40,7 +40,7 @@ import tech.picnic.errorprone.documentation.BugPatternTestExtractor.TestCases;
@Immutable
@AutoService(Extractor.class)
@SuppressWarnings("rawtypes" /* See https://github.com/google/auto/issues/870. */)
public final class BugPatternTestExtractor implements Extractor<TestCases> {
public final class BugPatternTestExtractor implements Extractor<BugPatternTestCases> {
/** Instantiates a new {@link BugPatternTestExtractor} instance. */
public BugPatternTestExtractor() {}
@@ -50,7 +50,7 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
}
@Override
public Optional<TestCases> tryExtract(ClassTree tree, VisitorState state) {
public Optional<BugPatternTestCases> tryExtract(ClassTree tree, VisitorState state) {
BugPatternTestCollector collector = new BugPatternTestCollector();
collector.scan(tree, state);
@@ -59,7 +59,7 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
.filter(not(ImmutableList::isEmpty))
.map(
tests ->
new AutoValue_BugPatternTestExtractor_TestCases(
new AutoValue_BugPatternTestExtractor_BugPatternTestCases(
state.getPath().getCompilationUnit().getSourceFile().toUri(),
ASTHelpers.getSymbol(tree).className(),
tests));
@@ -95,10 +95,10 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper.ExpectOutput")
.namedAnyOf("addOutputLines", "expectUnchanged");
private final List<TestCase> collectedTestCases = new ArrayList<>();
private final List<BugPatternTestCase> collectedBugPatternTestCases = new ArrayList<>();
private ImmutableList<TestCase> getCollectedTests() {
return ImmutableList.copyOf(collectedTestCases);
private ImmutableList<BugPatternTestCase> getCollectedTests() {
return ImmutableList.copyOf(collectedBugPatternTestCases);
}
@Override
@@ -110,14 +110,14 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
classUnderTest -> {
List<TestEntry> entries = new ArrayList<>();
if (isReplacementTest) {
extractReplacementTestCases(node, entries, state);
extractReplacementBugPatternTestCases(node, entries, state);
} else {
extractIdentificationTestCases(node, entries, state);
extractIdentificationBugPatternTestCases(node, entries, state);
}
if (!entries.isEmpty()) {
collectedTestCases.add(
new AutoValue_BugPatternTestExtractor_TestCase(
collectedBugPatternTestCases.add(
new AutoValue_BugPatternTestExtractor_BugPatternTestCase(
classUnderTest, ImmutableList.copyOf(entries).reverse()));
}
});
@@ -140,7 +140,7 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
: Optional.empty();
}
private static void extractIdentificationTestCases(
private static void extractIdentificationBugPatternTestCases(
MethodInvocationTree tree, List<TestEntry> sink, VisitorState state) {
if (IDENTIFICATION_SOURCE_LINES.matches(tree, state)) {
String path = ASTHelpers.constValue(tree.getArguments().get(0), String.class);
@@ -155,11 +155,11 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
if (receiver instanceof MethodInvocationTree methodInvocation) {
extractIdentificationTestCases(methodInvocation, sink, state);
extractIdentificationBugPatternTestCases(methodInvocation, sink, state);
}
}
private static void extractReplacementTestCases(
private static void extractReplacementBugPatternTestCases(
MethodInvocationTree tree, List<TestEntry> sink, VisitorState state) {
if (REPLACEMENT_OUTPUT_SOURCE_LINES.matches(tree, state)) {
/*
@@ -185,7 +185,7 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
if (receiver instanceof MethodInvocationTree methodInvocation) {
extractReplacementTestCases(methodInvocation, sink, state);
extractReplacementBugPatternTestCases(methodInvocation, sink, state);
}
}
@@ -208,24 +208,26 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
}
@AutoValue
@JsonDeserialize(as = AutoValue_BugPatternTestExtractor_TestCases.class)
abstract static class TestCases {
static TestCases create(URI source, String testClass, ImmutableList<TestCase> testCases) {
return new AutoValue_BugPatternTestExtractor_TestCases(source, testClass, testCases);
@JsonDeserialize(as = AutoValue_BugPatternTestExtractor_BugPatternTestCases.class)
abstract static class BugPatternTestCases {
static BugPatternTestCases create(
URI source, String testClass, ImmutableList<BugPatternTestCase> testCases) {
return new AutoValue_BugPatternTestExtractor_BugPatternTestCases(
source, testClass, testCases);
}
abstract URI source();
abstract String testClass();
abstract ImmutableList<TestCase> testCases();
abstract ImmutableList<BugPatternTestCase> testCases();
}
@AutoValue
@JsonDeserialize(as = AutoValue_BugPatternTestExtractor_TestCase.class)
abstract static class TestCase {
static TestCase create(String classUnderTest, ImmutableList<TestEntry> entries) {
return new AutoValue_BugPatternTestExtractor_TestCase(classUnderTest, entries);
@JsonDeserialize(as = AutoValue_BugPatternTestExtractor_BugPatternTestCase.class)
abstract static class BugPatternTestCase {
static BugPatternTestCase create(String classUnderTest, ImmutableList<TestEntry> entries) {
return new AutoValue_BugPatternTestExtractor_BugPatternTestCase(classUnderTest, entries);
}
abstract String classUnderTest();

View File

@@ -0,0 +1,176 @@
package tech.picnic.errorprone.documentation;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
import static java.util.stream.Collectors.joining;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.google.auto.service.AutoService;
import com.google.auto.value.AutoValue;
import com.google.common.base.Splitter;
import com.google.common.base.Supplier;
import com.google.common.base.VerifyException;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.FormatMethod;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.matchers.Matcher;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodTree;
import java.net.URI;
import java.util.Optional;
import java.util.regex.Pattern;
import tech.picnic.errorprone.documentation.RefasterRuleCollectionTestExtractor.RefasterTestCases;
import tech.picnic.errorprone.utils.SourceCode;
/**
* An {@link Extractor} that describes how to extract data from Refaster rule input and output test
* classes.
*/
// XXX: Drop this extractor if/when the Refaster test framework is reimplemented such that tests can
// be located alongside rules, rather than in two additional resource files as currently required by
// `RefasterRuleCollection`.
@Immutable
@AutoService(Extractor.class)
@SuppressWarnings("rawtypes" /* See https://github.com/google/auto/issues/870. */)
public final class RefasterRuleCollectionTestExtractor implements Extractor<RefasterTestCases> {
private static final Matcher<ClassTree> IS_REFASTER_RULE_COLLECTION_TEST_CASE =
isSubtypeOf("tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase");
private static final Pattern TEST_CLASS_NAME_PATTERN = Pattern.compile("(.*)Test");
private static final Pattern TEST_CLASS_FILE_NAME_PATTERN =
Pattern.compile(".*(Input|Output)\\.java");
private static final Pattern TEST_METHOD_NAME_PATTERN = Pattern.compile("test(.*)");
private static final String LINE_SEPARATOR = "\n";
private static final Splitter LINE_SPLITTER = Splitter.on(LINE_SEPARATOR);
/** Instantiates a new {@link RefasterRuleCollectionTestExtractor} instance. */
public RefasterRuleCollectionTestExtractor() {}
@Override
public String identifier() {
return "refaster-rule-collection-test";
}
@Override
public Optional<RefasterTestCases> tryExtract(ClassTree tree, VisitorState state) {
if (!IS_REFASTER_RULE_COLLECTION_TEST_CASE.matches(tree, state)) {
return Optional.empty();
}
URI sourceFile = state.getPath().getCompilationUnit().getSourceFile().toUri();
return Optional.of(
RefasterTestCases.create(
sourceFile,
getRuleCollectionName(tree),
isInputFile(sourceFile),
getRefasterTestCases(tree, state)));
}
private static String getRuleCollectionName(ClassTree tree) {
String className = tree.getSimpleName().toString();
// XXX: Instead of throwing an error here, it'd be nicer to have a bug checker validate key
// aspects of `RefasterRuleCollectionTestCase` subtypes.
return tryExtractPatternGroup(className, TEST_CLASS_NAME_PATTERN)
.orElseThrow(
violation(
"Refaster rule collection test class name '%s' does not match '%s'",
className, TEST_CLASS_NAME_PATTERN));
}
private static boolean isInputFile(URI sourceFile) {
String path = sourceFile.getPath();
// XXX: Instead of throwing an error here, it'd be nicer to have a bug checker validate key
// aspects of `RefasterRuleCollectionTestCase` subtypes.
return "Input"
.equals(
tryExtractPatternGroup(path, TEST_CLASS_FILE_NAME_PATTERN)
.orElseThrow(
violation(
"Refaster rule collection test file name '%s' does not match '%s'",
path, TEST_CLASS_FILE_NAME_PATTERN)));
}
private static ImmutableList<RefasterTestCase> getRefasterTestCases(
ClassTree tree, VisitorState state) {
return tree.getMembers().stream()
.filter(MethodTree.class::isInstance)
.map(MethodTree.class::cast)
.flatMap(m -> tryExtractRefasterTestCase(m, state).stream())
.collect(toImmutableList());
}
private static Optional<RefasterTestCase> tryExtractRefasterTestCase(
MethodTree method, VisitorState state) {
return tryExtractPatternGroup(method.getName().toString(), TEST_METHOD_NAME_PATTERN)
.map(name -> RefasterTestCase.create(name, getFormattedSource(method, state)));
}
/**
* Returns the source code for the specified method.
*
* @implNote This operation attempts to trim leading whitespace, such that the start and end of
* the method declaration are aligned. The implemented heuristic assumes that the code is
* formatted using Google Java Format.
*/
// XXX: Leading Javadoc and other comments are currently not extracted. Consider fixing this.
private static String getFormattedSource(MethodTree method, VisitorState state) {
String source = SourceCode.treeToString(method, state);
int finalNewline = source.lastIndexOf(LINE_SEPARATOR);
if (finalNewline < 0) {
return source;
}
int indentation = Math.max(0, source.lastIndexOf(' ') - finalNewline);
String prefixToStrip = " ".repeat(indentation);
return LINE_SPLITTER
.splitToStream(source)
.map(line -> line.startsWith(prefixToStrip) ? line.substring(indentation) : line)
.collect(joining(LINE_SEPARATOR));
}
private static Optional<String> tryExtractPatternGroup(String input, Pattern pattern) {
java.util.regex.Matcher matcher = pattern.matcher(input);
return matcher.matches() ? Optional.of(matcher.group(1)) : Optional.empty();
}
@FormatMethod
private static Supplier<VerifyException> violation(String format, Object... args) {
return () -> new VerifyException(String.format(format, args));
}
@AutoValue
@JsonDeserialize(as = AutoValue_RefasterRuleCollectionTestExtractor_RefasterTestCases.class)
abstract static class RefasterTestCases {
static RefasterTestCases create(
URI source,
String ruleCollection,
boolean isInput,
ImmutableList<RefasterTestCase> testCases) {
return new AutoValue_RefasterRuleCollectionTestExtractor_RefasterTestCases(
source, ruleCollection, isInput, testCases);
}
abstract URI source();
abstract String ruleCollection();
abstract boolean isInput();
abstract ImmutableList<RefasterTestCase> testCases();
}
@AutoValue
@JsonDeserialize(as = AutoValue_RefasterRuleCollectionTestExtractor_RefasterTestCase.class)
abstract static class RefasterTestCase {
static RefasterTestCase create(String name, String content) {
return new AutoValue_RefasterRuleCollectionTestExtractor_RefasterTestCase(name, content);
}
abstract String name();
abstract String content();
}
}

View File

@@ -7,10 +7,10 @@ import java.net.URI;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.BugPatternTestCase;
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.BugPatternTestCases;
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.IdentificationTestEntry;
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.ReplacementTestEntry;
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.TestCase;
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.TestCases;
final class BugPatternTestExtractorTest {
@Test
@@ -269,11 +269,11 @@ final class BugPatternTestExtractorTest {
verifyGeneratedFileContent(
outputDirectory,
"SingleFileCompilationTestHelperTest",
TestCases.create(
BugPatternTestCases.create(
URI.create("file:///SingleFileCompilationTestHelperTest.java"),
"SingleFileCompilationTestHelperTest",
ImmutableList.of(
TestCase.create(
BugPatternTestCase.create(
"SingleFileCompilationTestHelperTest.TestChecker",
ImmutableList.of(
IdentificationTestEntry.create(
@@ -302,11 +302,11 @@ final class BugPatternTestExtractorTest {
verifyGeneratedFileContent(
outputDirectory,
"SingleFileCompilationTestHelperWithSetArgsTest",
TestCases.create(
BugPatternTestCases.create(
URI.create("file:///SingleFileCompilationTestHelperWithSetArgsTest.java"),
"SingleFileCompilationTestHelperWithSetArgsTest",
ImmutableList.of(
TestCase.create(
BugPatternTestCase.create(
"SingleFileCompilationTestHelperWithSetArgsTest.TestChecker",
ImmutableList.of(
IdentificationTestEntry.create(
@@ -335,11 +335,11 @@ final class BugPatternTestExtractorTest {
verifyGeneratedFileContent(
outputDirectory,
"MultiFileCompilationTestHelperTest",
TestCases.create(
BugPatternTestCases.create(
URI.create("file:///MultiFileCompilationTestHelperTest.java"),
"MultiFileCompilationTestHelperTest",
ImmutableList.of(
TestCase.create(
BugPatternTestCase.create(
"MultiFileCompilationTestHelperTest.TestChecker",
ImmutableList.of(
IdentificationTestEntry.create(
@@ -370,11 +370,11 @@ final class BugPatternTestExtractorTest {
verifyGeneratedFileContent(
outputDirectory,
"SingleFileBugCheckerRefactoringTestHelperTest",
TestCases.create(
BugPatternTestCases.create(
URI.create("file:///SingleFileBugCheckerRefactoringTestHelperTest.java"),
"SingleFileBugCheckerRefactoringTestHelperTest",
ImmutableList.of(
TestCase.create(
BugPatternTestCase.create(
"SingleFileBugCheckerRefactoringTestHelperTest.TestChecker",
ImmutableList.of(
ReplacementTestEntry.create(
@@ -408,12 +408,12 @@ final class BugPatternTestExtractorTest {
verifyGeneratedFileContent(
outputDirectory,
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest",
TestCases.create(
BugPatternTestCases.create(
URI.create(
"file:///SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest.java"),
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest",
ImmutableList.of(
TestCase.create(
BugPatternTestCase.create(
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest.TestChecker",
ImmutableList.of(
ReplacementTestEntry.create(
@@ -444,11 +444,11 @@ final class BugPatternTestExtractorTest {
verifyGeneratedFileContent(
outputDirectory,
"MultiFileBugCheckerRefactoringTestHelperTest",
TestCases.create(
BugPatternTestCases.create(
URI.create("file:///MultiFileBugCheckerRefactoringTestHelperTest.java"),
"MultiFileBugCheckerRefactoringTestHelperTest",
ImmutableList.of(
TestCase.create(
BugPatternTestCase.create(
"MultiFileBugCheckerRefactoringTestHelperTest.TestChecker",
ImmutableList.of(
ReplacementTestEntry.create(
@@ -484,16 +484,16 @@ final class BugPatternTestExtractorTest {
verifyGeneratedFileContent(
outputDirectory,
"CompilationAndBugCheckerRefactoringTestHelpersTest",
TestCases.create(
BugPatternTestCases.create(
URI.create("file:///CompilationAndBugCheckerRefactoringTestHelpersTest.java"),
"CompilationAndBugCheckerRefactoringTestHelpersTest",
ImmutableList.of(
TestCase.create(
BugPatternTestCase.create(
"CompilationAndBugCheckerRefactoringTestHelpersTest.TestChecker",
ImmutableList.of(
IdentificationTestEntry.create(
"A.java", "// BUG: Diagnostic contains:\nclass A {}\n"))),
TestCase.create(
BugPatternTestCase.create(
"CompilationAndBugCheckerRefactoringTestHelpersTest.TestChecker",
ImmutableList.of(
ReplacementTestEntry.create(
@@ -532,17 +532,17 @@ final class BugPatternTestExtractorTest {
verifyGeneratedFileContent(
outputDirectory,
"CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest",
TestCases.create(
BugPatternTestCases.create(
URI.create(
"file:///CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.java"),
"pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest",
ImmutableList.of(
TestCase.create(
BugPatternTestCase.create(
"pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.CustomTestChecker",
ImmutableList.of(
IdentificationTestEntry.create(
"A.java", "// BUG: Diagnostic contains:\nclass A {}\n"))),
TestCase.create(
BugPatternTestCase.create(
"pkg.CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.CustomTestChecker2",
ImmutableList.of(
ReplacementTestEntry.create(
@@ -550,9 +550,9 @@ final class BugPatternTestExtractorTest {
}
private static void verifyGeneratedFileContent(
Path outputDirectory, String testClass, TestCases expected) {
Path outputDirectory, String testClass, BugPatternTestCases expected) {
assertThat(outputDirectory.resolve(String.format("bugpattern-test-%s.json", testClass)))
.exists()
.returns(expected, path -> Json.read(path, TestCases.class));
.returns(expected, path -> Json.read(path, BugPatternTestCases.class));
}
}

View File

@@ -10,7 +10,6 @@ import static org.junit.jupiter.api.condition.OS.WINDOWS;
import com.google.auto.service.AutoService;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.google.errorprone.VisitorState;
@@ -41,7 +40,8 @@ final class DocumentationGeneratorTaskListenerTest {
entry ->
AclEntry.newBuilder(entry)
.setPermissions(
Sets.difference(entry.permissions(), ImmutableSet.of(ADD_SUBDIRECTORY)))
Sets.difference(
entry.permissions(), Sets.immutableEnumSet(ADD_SUBDIRECTORY)))
.build())
.collect(toImmutableList()));

View File

@@ -0,0 +1,166 @@
package tech.picnic.errorprone.documentation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import com.google.common.base.VerifyException;
import com.google.common.collect.ImmutableList;
import java.net.URI;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import tech.picnic.errorprone.documentation.RefasterRuleCollectionTestExtractor.RefasterTestCase;
import tech.picnic.errorprone.documentation.RefasterRuleCollectionTestExtractor.RefasterTestCases;
final class RefasterRuleCollectionTestExtractorTest {
@Test
void noRefasterRuleTest(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory, "NoRefasterRuleTest.java", "public final class NoRefasterRuleTest {}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void invalidTestClassName(@TempDir Path outputDirectory) {
assertThatThrownBy(
() ->
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"InvalidTestClassNameInput.java",
"import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;",
"",
"final class InvalidTestClassName implements RefasterRuleCollectionTestCase {}"))
.cause()
.isInstanceOf(VerifyException.class)
.hasMessage(
"Refaster rule collection test class name 'InvalidTestClassName' does not match '(.*)Test'");
}
@Test
void invalidFileName(@TempDir Path outputDirectory) {
assertThatThrownBy(
() ->
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"InvalidFileNameTest.java",
"import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;",
"",
"final class InvalidFileNameTest implements RefasterRuleCollectionTestCase {}"))
.cause()
.isInstanceOf(VerifyException.class)
.hasMessage(
"Refaster rule collection test file name '/InvalidFileNameTest.java' does not match '.*(Input|Output)\\.java'");
}
@Test
void emptyRefasterRuleCollectionTestInput(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"EmptyRefasterRuleCollectionTestInput.java",
"import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;",
"",
"final class EmptyRefasterRuleCollectionTest implements RefasterRuleCollectionTestCase {}");
verifyGeneratedFileContent(
outputDirectory,
"EmptyRefasterRuleCollectionTestInput",
RefasterTestCases.create(
URI.create("file:///EmptyRefasterRuleCollectionTestInput.java"),
"EmptyRefasterRuleCollection",
/* isInput= */ true,
ImmutableList.of()));
}
@Test
void singletonRefasterRuleCollectionTestOutput(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"SingletonRefasterRuleCollectionTestOutput.java",
"import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;",
"",
"final class SingletonRefasterRuleCollectionTest implements RefasterRuleCollectionTestCase {",
" int testMyRule() {",
" return 42;",
" }",
"}");
verifyGeneratedFileContent(
outputDirectory,
"SingletonRefasterRuleCollectionTestOutput",
RefasterTestCases.create(
URI.create("file:///SingletonRefasterRuleCollectionTestOutput.java"),
"SingletonRefasterRuleCollection",
/* isInput= */ false,
ImmutableList.of(
RefasterTestCase.create(
"MyRule",
"""
int testMyRule() {
return 42;
}"""))));
}
@Test
void complexRefasterRuleCollectionTestOutput(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"pkg/ComplexRefasterRuleCollectionTestInput.java",
"package pkg;",
"",
"import com.google.common.collect.ImmutableSet;",
"import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;",
"",
"final class ComplexRefasterRuleCollectionTest implements RefasterRuleCollectionTestCase {",
" private static final String IGNORED_CONSTANT = \"constant\";",
"",
" @Override",
" public ImmutableSet<Object> elidedTypesAndStaticImports() {",
" return ImmutableSet.of();",
" }",
"",
" /** Javadoc. */",
" String testFirstRule() {",
" return \"Don't panic\";",
" }",
"",
" // Comment.",
" String testSecondRule() {",
" return \"Carry a towel\";",
" }",
"",
" void testEmptyRule() {}",
"}");
verifyGeneratedFileContent(
outputDirectory,
"ComplexRefasterRuleCollectionTestInput",
RefasterTestCases.create(
URI.create("file:///pkg/ComplexRefasterRuleCollectionTestInput.java"),
"ComplexRefasterRuleCollection",
/* isInput= */ true,
ImmutableList.of(
RefasterTestCase.create(
"FirstRule",
"""
String testFirstRule() {
return "Don't panic";
}"""),
RefasterTestCase.create(
"SecondRule",
"""
String testSecondRule() {
return "Carry a towel";
}"""),
RefasterTestCase.create("EmptyRule", "void testEmptyRule() {}"))));
}
private static void verifyGeneratedFileContent(
Path outputDirectory, String testIdentifier, RefasterTestCases expected) {
assertThat(
outputDirectory.resolve(
String.format("refaster-rule-collection-test-%s.json", testIdentifier)))
.exists()
.returns(expected, path -> Json.read(path, RefasterTestCases.class));
}
}

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.18.1-SNAPSHOT</version>
<version>0.19.2-SNAPSHOT</version>
</parent>
<artifactId>error-prone-contrib</artifactId>
@@ -55,7 +55,12 @@
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-support</artifactId>
<scope>provided</scope>
<!-- XXX: One would expect this to be a `provided` dependency (as
Refaster rules are interpreted by the `refaster-runner` module),
but the `OptionalOrElseGet` bug checker defined by this module
depends on the `RequiresComputation` matcher that
`refaster-support` primarily exposes for use by Refaster rules.
Review this setup. (Should the matchers be moved elsewhere?) -->
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
@@ -67,6 +72,11 @@
<artifactId>jackson-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service-annotations</artifactId>
@@ -82,6 +92,11 @@
<artifactId>guava</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
@@ -196,7 +211,7 @@
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-java-11</artifactId>
<artifactId>rewrite-java-17</artifactId>
<scope>test</scope>
</dependency>
<dependency>
@@ -283,10 +298,56 @@
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs combine.children="append">
<arg>-Xplugin:DocumentationGenerator -XoutputDirectory=${project.build.directory}/docs</arg>
</compilerArgs>
</configuration>
<executions>
<!-- The Refaster input/output test classes used by
`RefasterRuleCollection` are modelled as classpath
resources, and thus not subject to the default test
compilation step. These two custom compilation steps
serve two purposes:
- To provide early feedback in case of syntax errors.
- To enable the `DocumentationGenerator` compiler
plugin to extract documentation metadata from them.
Note that the input and output files must be compiled
separately and to distinct output directories, as they
define the same set of class names. -->
<!-- XXX: Drop these executions if/when the Refaster
test framework is reimplemented such that tests can be
located alongside rules, rather than in two additional
resource files. -->
<execution>
<id>compile-refaster-test-input</id>
<goals>
<goal>testCompile</goal>
</goals>
<phase>process-test-resources</phase>
<configuration>
<compileSourceRoots>
<compileSourceRoot>${project.basedir}/src/test/resources</compileSourceRoot>
</compileSourceRoots>
<testIncludes>
<testInclude>**/*Input.java</testInclude>
</testIncludes>
<outputDirectory>${project.build.directory}/refaster-test-input</outputDirectory>
</configuration>
</execution>
<execution>
<id>compile-refaster-test-output</id>
<goals>
<goal>testCompile</goal>
</goals>
<phase>process-test-resources</phase>
<configuration>
<compileSourceRoots>
<compileSourceRoot>${project.basedir}/src/test/resources</compileSourceRoot>
</compileSourceRoots>
<testIncludes>
<testInclude>**/*Output.java</testInclude>
</testIncludes>
<outputDirectory>${project.build.directory}/refaster-test-output</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>

View File

@@ -0,0 +1,72 @@
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 tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.common.collect.Iterables;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.LambdaExpressionTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.code.Type;
import tech.picnic.errorprone.utils.SourceCode;
/**
* A {@link BugChecker} that flags lambda expressions that can be replaced with a method reference
* of the form {@code T.class::cast}.
*/
// XXX: Consider folding this logic into the `MethodReferenceUsage` check of the
// `error-prone-experimental` module.
// XXX: This check and its tests are structurally nearly identical to `IsInstanceLambdaUsage`.
// Unless folded into `MethodReferenceUsage`, consider merging the two.
@AutoService(BugChecker.class)
@BugPattern(
summary = "Prefer `Class::cast` method reference over equivalent lambda expression",
link = BUG_PATTERNS_BASE_URL + "ClassCastLambdaUsage",
linkType = CUSTOM,
severity = SUGGESTION,
tags = SIMPLIFICATION)
public final class ClassCastLambdaUsage extends BugChecker implements LambdaExpressionTreeMatcher {
private static final long serialVersionUID = 1L;
/** Instantiates a new {@link ClassCastLambdaUsage} instance. */
public ClassCastLambdaUsage() {}
@Override
public Description matchLambdaExpression(LambdaExpressionTree tree, VisitorState state) {
if (tree.getParameters().size() != 1 || !(tree.getBody() instanceof TypeCastTree typeCast)) {
return Description.NO_MATCH;
}
Type type = ASTHelpers.getType(typeCast);
if (type == null || type.isParameterized() || type.isPrimitive()) {
/*
* The method reference syntax does not support casting to parameterized types. Additionally,
* `Class#cast` does not support the same range of type conversions between (boxed) primitive
* types as the cast operator.
*/
// XXX: Depending on the declared type of the value being cast, in some cases we _can_ rewrite
// primitive casts. Add support for this.
return Description.NO_MATCH;
}
VariableTree param = Iterables.getOnlyElement(tree.getParameters());
if (!ASTHelpers.getSymbol(param).equals(ASTHelpers.getSymbol(typeCast.getExpression()))) {
return Description.NO_MATCH;
}
return describeMatch(
tree,
SuggestedFix.replace(
tree, SourceCode.treeToString(typeCast.getType(), state) + ".class::cast"));
}
}

View File

@@ -0,0 +1,125 @@
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.STYLE;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.hasModifier;
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.errorprone.BugPattern;
import com.google.errorprone.ErrorProneFlags;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.VariableTreeMatcher;
import com.google.errorprone.fixes.SuggestedFixes;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreeScanner;
import java.util.Locale;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.lang.model.element.Modifier;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.utils.Flags;
/**
* A {@link BugChecker} that flags static constants that do not follow the upper snake case naming
* convention.
*/
@AutoService(BugChecker.class)
@BugPattern(
summary = "Constant variables should adhere to the `UPPER_SNAKE_CASE` naming convention",
link = BUG_PATTERNS_BASE_URL + "ConstantNaming",
linkType = CUSTOM,
severity = WARNING,
tags = STYLE)
@SuppressWarnings("java:S2160" /* Super class equality definition suffices. */)
public final class ConstantNaming extends BugChecker implements VariableTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<VariableTree> IS_CONSTANT =
allOf(hasModifier(Modifier.STATIC), hasModifier(Modifier.FINAL));
private static final Matcher<VariableTree> IS_PRIVATE = hasModifier(Modifier.PRIVATE);
private static final Pattern SNAKE_CASE = Pattern.compile("([a-z])([A-Z])");
private static final ImmutableSet<String> DEFAULT_EXEMPTED_NAMES =
ImmutableSet.of("serialVersionUID");
/**
* Flag using which constant names that must not be flagged (in addition to those defined by
* {@link #DEFAULT_EXEMPTED_NAMES}) can be specified.
*/
private static final String ADDITIONAL_EXEMPTED_NAMES_FLAG =
"CanonicalConstantNaming:ExemptedNames";
private final ImmutableSet<String> exemptedNames;
/** Instantiates a default {@link ConstantNaming} instance. */
public ConstantNaming() {
this(ErrorProneFlags.empty());
}
/**
* Instantiates a customized {@link ConstantNaming}.
*
* @param flags Any provided command line flags.
*/
@Inject
ConstantNaming(ErrorProneFlags flags) {
exemptedNames =
Sets.union(DEFAULT_EXEMPTED_NAMES, Flags.getSet(flags, ADDITIONAL_EXEMPTED_NAMES_FLAG))
.immutableCopy();
}
@Override
public Description matchVariable(VariableTree tree, VisitorState state) {
String variableName = tree.getName().toString();
if (!IS_CONSTANT.matches(tree, state) || exemptedNames.contains(variableName)) {
return Description.NO_MATCH;
}
String replacement = toUpperSnakeCase(variableName);
if (replacement.equals(variableName)) {
return Description.NO_MATCH;
}
Description.Builder description = buildDescription(tree);
if (!IS_PRIVATE.matches(tree, state)) {
description.setMessage(
"%s; consider renaming to '%s', though note that this is not a private constant"
.formatted(message(), replacement));
} else if (isVariableNameInUse(replacement, state)) {
description.setMessage(
"%s; consider renaming to '%s', though note that a variable with this name is already declared"
.formatted(message(), replacement));
} else {
description.addFix(SuggestedFixes.renameVariable(tree, replacement, state));
}
return description.build();
}
private static String toUpperSnakeCase(String variableName) {
return SNAKE_CASE.matcher(variableName).replaceAll("$1_$2").toUpperCase(Locale.ROOT);
}
private static boolean isVariableNameInUse(String name, VisitorState state) {
return Boolean.TRUE.equals(
new TreeScanner<Boolean, @Nullable Void>() {
@Override
public Boolean visitVariable(VariableTree tree, @Nullable Void unused) {
return ASTHelpers.getSymbol(tree).getSimpleName().contentEquals(name)
|| super.visitVariable(tree, null);
}
@Override
public Boolean reduce(Boolean r1, Boolean r2) {
return Boolean.TRUE.equals(r1) || Boolean.TRUE.equals(r2);
}
}.scan(state.getPath().getCompilationUnit(), null));
}
}

View File

@@ -62,7 +62,7 @@ public final class EmptyMethod extends BugChecker implements MethodTreeMatcher {
}
private static boolean isInPossibleTestHelperClass(VisitorState state) {
return Optional.ofNullable(ASTHelpers.findEnclosingNode(state.getPath(), ClassTree.class))
return Optional.ofNullable(state.findEnclosing(ClassTree.class))
.map(ClassTree::getSimpleName)
.filter(name -> name.toString().contains("Test"))
.isPresent();

View File

@@ -36,6 +36,7 @@ import java.util.Formatter;
import java.util.List;
import java.util.Optional;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.utils.MoreASTHelpers;
import tech.picnic.errorprone.utils.SourceCode;
/**
@@ -203,14 +204,10 @@ public final class FormatStringConcatenation extends BugChecker
ExpressionTree argument = ASTHelpers.stripParentheses(arguments.get(argPosition));
return argument instanceof BinaryTree
&& isStringTyped(argument, state)
&& MoreASTHelpers.isStringTyped(argument, state)
&& ASTHelpers.constValue(argument, String.class) == null;
}
private static boolean isStringTyped(ExpressionTree tree, VisitorState state) {
return ASTHelpers.isSameType(ASTHelpers.getType(tree), state.getSymtab().stringType, state);
}
private static class ReplacementArgumentsConstructor
extends SimpleTreeVisitor<@Nullable Void, VisitorState> {
private final StringBuilder formatString = new StringBuilder();
@@ -223,7 +220,7 @@ public final class FormatStringConcatenation extends BugChecker
@Override
public @Nullable Void visitBinary(BinaryTree tree, VisitorState state) {
if (tree.getKind() == Kind.PLUS && isStringTyped(tree, state)) {
if (tree.getKind() == Kind.PLUS && MoreASTHelpers.isStringTyped(tree, state)) {
tree.getLeftOperand().accept(this, state);
tree.getRightOperand().accept(this, state);
} else {

View File

@@ -25,6 +25,8 @@ import tech.picnic.errorprone.utils.SourceCode;
*/
// XXX: Consider folding this logic into the `MethodReferenceUsage` check of the
// `error-prone-experimental` module.
// XXX: This check and its tests are structurally nearly identical to `ClassCastLambdaUsage`. Unless
// folded into `MethodReferenceUsage`, consider merging the two.
@AutoService(BugChecker.class)
@BugPattern(
summary = "Prefer `Class::isInstance` method reference over equivalent lambda expression",

View File

@@ -16,7 +16,7 @@ import static tech.picnic.errorprone.utils.MoreJUnitMatchers.TEST_METHOD;
import static tech.picnic.errorprone.utils.MoreMatchers.hasMetaAnnotation;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
@@ -70,7 +70,7 @@ public final class JUnitClassModifiers extends BugChecker implements ClassTreeMa
SuggestedFixes.removeModifiers(
tree.getModifiers(),
state,
ImmutableSet.of(Modifier.PRIVATE, Modifier.PROTECTED, Modifier.PUBLIC))
Sets.immutableEnumSet(Modifier.PRIVATE, Modifier.PROTECTED, Modifier.PUBLIC))
.ifPresent(fixBuilder::merge);
if (!HAS_SPRING_CONFIGURATION_ANNOTATION.matches(tree, state)) {

View File

@@ -14,6 +14,7 @@ import com.google.common.base.Splitter;
import com.google.common.collect.Comparators;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.errorprone.BugPattern;
import com.google.errorprone.ErrorProneFlags;
import com.google.errorprone.VisitorState;
@@ -34,10 +35,8 @@ import com.sun.source.tree.Tree;
import com.sun.source.util.TreeScanner;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import java.util.HashSet;
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;
@@ -163,7 +162,12 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
/* For now we don't force sorting on numeric types. */
return Stream.of(
symtab.annotationType, symtab.classType, symtab.enumSym.type, symtab.stringType)
symtab.annotationType,
symtab.booleanType,
symtab.charType,
symtab.classType,
symtab.enumSym.type,
symtab.stringType)
.anyMatch(t -> ASTHelpers.isSubtype(elemType, t, state));
}
@@ -225,10 +229,8 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
excludedAnnotations(flags));
}
private static ImmutableList<String> excludedAnnotations(ErrorProneFlags flags) {
Set<String> exclusions = new HashSet<>();
exclusions.addAll(Flags.getList(flags, EXCLUDED_ANNOTATIONS_FLAG));
exclusions.addAll(BLACKLISTED_ANNOTATIONS);
return ImmutableList.copyOf(exclusions);
private static ImmutableSet<String> excludedAnnotations(ErrorProneFlags flags) {
return Sets.union(BLACKLISTED_ANNOTATIONS, Flags.getSet(flags, EXCLUDED_ANNOTATIONS_FLAG))
.immutableCopy();
}
}

View File

@@ -18,14 +18,12 @@ import com.google.errorprone.fixes.SuggestedFixes;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import java.util.Optional;
import java.util.function.Supplier;
import tech.picnic.errorprone.refaster.matchers.RequiresComputation;
import tech.picnic.errorprone.utils.SourceCode;
/**
@@ -36,12 +34,12 @@ import tech.picnic.errorprone.utils.SourceCode;
* it does, the suggested fix changes the program's semantics. Such fragile code must instead be
* refactored such that the side-effectful code does not appear accidental.
*/
// XXX: Consider also implementing the inverse, in which `.orElseGet(() -> someConstant)` is
// flagged.
// XXX: Once the `MethodReferenceUsageCheck` becomes generally usable, consider leaving the method
// reference cleanup to that check, and express the remainder of the logic in this class using a
// Refaster template, i.c.w. a `@Matches` constraint that implements the `requiresComputation`
// logic.
// 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 the `MethodReferenceUsageCheck` bug checker becomes generally usable, consider leaving
// the method reference cleanup to that check, and express the remainder of the logic in this class
// using a Refaster template, i.c.w. a `@NotMatches(RequiresComputation.class)` constraint.
@AutoService(BugChecker.class)
@BugPattern(
summary =
@@ -51,16 +49,17 @@ import tech.picnic.errorprone.utils.SourceCode;
linkType = NONE,
severity = WARNING,
tags = PERFORMANCE)
public final class OptionalOrElse extends BugChecker implements MethodInvocationTreeMatcher {
public final class OptionalOrElseGet extends BugChecker implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> REQUIRES_COMPUTATION = new RequiresComputation();
private static final Matcher<ExpressionTree> OPTIONAL_OR_ELSE_METHOD =
instanceMethod().onExactClass(Optional.class.getCanonicalName()).namedAnyOf("orElse");
// XXX: Also exclude invocations of `@Placeholder`-annotated methods.
private static final Matcher<ExpressionTree> REFASTER_METHOD =
staticMethod().onClass(Refaster.class.getCanonicalName());
/** Instantiates a new {@link OptionalOrElse} instance. */
public OptionalOrElse() {}
/** Instantiates a new {@link OptionalOrElseGet} instance. */
public OptionalOrElseGet() {}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
@@ -69,7 +68,8 @@ public final class OptionalOrElse extends BugChecker implements MethodInvocation
}
ExpressionTree argument = Iterables.getOnlyElement(tree.getArguments());
if (!requiresComputation(argument) || REFASTER_METHOD.matches(argument, state)) {
if (!REQUIRES_COMPUTATION.matches(argument, state)
|| REFASTER_METHOD.matches(argument, state)) {
return Description.NO_MATCH;
}
@@ -91,18 +91,6 @@ public final class OptionalOrElse extends BugChecker implements MethodInvocation
return describeMatch(tree, fix);
}
/**
* Tells whether the given expression contains anything other than a literal or a (possibly
* dereferenced) variable or constant.
*/
private static boolean requiresComputation(ExpressionTree tree) {
return !(tree instanceof IdentifierTree
|| tree instanceof LiteralTree
|| (tree instanceof MemberSelectTree memberSelect
&& !requiresComputation(memberSelect.getExpression()))
|| ASTHelpers.constValue(tree) != null);
}
/** Returns the nullary method reference matching the given expression, if any. */
private static Optional<String> tryMethodReferenceConversion(
ExpressionTree tree, VisitorState state) {
@@ -118,7 +106,7 @@ public final class OptionalOrElse extends BugChecker implements MethodInvocation
return Optional.empty();
}
if (requiresComputation(memberSelect.getExpression())) {
if (REQUIRES_COMPUTATION.matches(memberSelect.getExpression(), state)) {
return Optional.empty();
}

View File

@@ -378,11 +378,11 @@ public final class RedundantStringConversion extends BugChecker
private static Matcher<MethodInvocationTree> createConversionMethodMatcher(
ErrorProneFlags flags) {
// XXX: ErrorProneFlags#getList splits by comma, but method signatures may also contain commas.
// For this class methods accepting more than one argument are not valid, but still: not nice.
// XXX: `Flags#getSet` splits by comma, but method signatures may also contain commas. For this
// class methods accepting more than one argument are not valid, but still: not nice.
return anyOf(
WELL_KNOWN_STRING_CONVERSION_METHODS,
new MethodMatcherFactory()
.create(Flags.getList(flags, EXTRA_STRING_CONVERSION_METHODS_FLAG)));
.create(Flags.getSet(flags, EXTRA_STRING_CONVERSION_METHODS_FLAG)));
}
}

View File

@@ -15,8 +15,8 @@ import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
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.BugPattern;
import com.google.errorprone.ErrorProneFlags;
import com.google.errorprone.VisitorState;
@@ -74,10 +74,10 @@ public final class RequestParamType extends BugChecker implements VariableTreeMa
return allOf(
annotations(AT_LEAST_ONE, isType("org.springframework.web.bind.annotation.RequestParam")),
anyOf(isSubtypeOf(ImmutableCollection.class), isSubtypeOf(ImmutableMap.class)),
not(isSubtypeOfAny(Flags.getList(flags, SUPPORTED_CUSTOM_TYPES_FLAG))));
not(isSubtypeOfAny(Flags.getSet(flags, SUPPORTED_CUSTOM_TYPES_FLAG))));
}
private static Matcher<Tree> isSubtypeOfAny(ImmutableList<String> inclusions) {
private static Matcher<Tree> isSubtypeOfAny(ImmutableSet<String> inclusions) {
return anyOf(
inclusions.stream()
.map(inclusion -> isSubtypeOf(Suppliers.typeFromString(inclusion)))

View File

@@ -0,0 +1,185 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.common.base.Verify.verify;
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.BugPattern.StandardTags.STYLE;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.classLiteral;
import static com.google.errorprone.matchers.Matchers.instanceMethod;
import static com.google.errorprone.matchers.Matchers.staticMethod;
import static com.google.errorprone.matchers.Matchers.toType;
import static java.util.Objects.requireNonNull;
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.common.base.CaseFormat;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.errorprone.BugPattern;
import com.google.errorprone.ErrorProneFlags;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.VariableTreeMatcher;
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.util.ASTHelpers;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.code.Symbol;
import java.util.EnumSet;
import javax.inject.Inject;
import javax.lang.model.element.Modifier;
import tech.picnic.errorprone.utils.MoreASTHelpers;
/** A {@link BugChecker} that flags non-canonical SLF4J logger declarations. */
@AutoService(BugChecker.class)
@BugPattern(
summary = "SLF4J logger declarations should follow established best-practices",
link = BUG_PATTERNS_BASE_URL + "Slf4jLoggerDeclaration",
linkType = CUSTOM,
severity = WARNING,
tags = STYLE)
@SuppressWarnings("java:S2160" /* Super class equality definition suffices. */)
public final class Slf4jLoggerDeclaration extends BugChecker implements VariableTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> IS_GET_LOGGER =
staticMethod().onDescendantOf("org.slf4j.LoggerFactory").named("getLogger");
private static final String CANONICAL_STATIC_LOGGER_NAME_FLAG =
"Slf4jLogDeclaration:CanonicalStaticLoggerName";
private static final String DEFAULT_CANONICAL_LOGGER_NAME = "LOG";
private static final Matcher<ExpressionTree> IS_STATIC_ENCLOSING_CLASS_REFERENCE =
classLiteral(Slf4jLoggerDeclaration::isEnclosingClassReference);
private static final Matcher<ExpressionTree> IS_DYNAMIC_ENCLOSING_CLASS_REFERENCE =
toType(
MethodInvocationTree.class,
allOf(
instanceMethod().anyClass().named("getClass").withNoParameters(),
Slf4jLoggerDeclaration::getClassReceiverIsEnclosingClassInstance));
private static final ImmutableSet<Modifier> INSTANCE_DECLARATION_MODIFIERS =
Sets.immutableEnumSet(Modifier.PRIVATE, Modifier.FINAL);
private static final ImmutableSet<Modifier> STATIC_DECLARATION_MODIFIERS =
Sets.immutableEnumSet(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL);
private final String canonicalStaticFieldName;
private final String canonicalInstanceFieldName;
/** Instantiates a default {@link Slf4jLoggerDeclaration} instance. */
public Slf4jLoggerDeclaration() {
this(ErrorProneFlags.empty());
}
/**
* Instantiates a customized {@link Slf4jLoggerDeclaration}.
*
* @param flags Any provided command line flags.
*/
@Inject
Slf4jLoggerDeclaration(ErrorProneFlags flags) {
canonicalStaticFieldName =
flags.get(CANONICAL_STATIC_LOGGER_NAME_FLAG).orElse(DEFAULT_CANONICAL_LOGGER_NAME);
canonicalInstanceFieldName =
CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, canonicalStaticFieldName);
}
@Override
public Description matchVariable(VariableTree tree, VisitorState state) {
ExpressionTree initializer = tree.getInitializer();
if (!IS_GET_LOGGER.matches(initializer, state)) {
return Description.NO_MATCH;
}
ClassTree clazz = getEnclosingClass(state);
ExpressionTree factoryArg =
Iterables.getOnlyElement(((MethodInvocationTree) initializer).getArguments());
SuggestedFix.Builder fix = SuggestedFix.builder();
if (clazz.getModifiers().getFlags().contains(Modifier.ABSTRACT)
&& IS_DYNAMIC_ENCLOSING_CLASS_REFERENCE.matches(factoryArg, state)) {
/*
* While generally we prefer `Logger` declarations to be static and named after their
* enclosing class, we allow one exception: loggers in abstract classes with a name derived
* from `getClass()`.
*/
suggestModifiers(tree, INSTANCE_DECLARATION_MODIFIERS, fix, state);
suggestRename(tree, canonicalInstanceFieldName, fix, state);
} else {
suggestModifiers(
tree,
clazz.getKind() == Kind.INTERFACE ? ImmutableSet.of() : STATIC_DECLARATION_MODIFIERS,
fix,
state);
suggestRename(tree, canonicalStaticFieldName, fix, state);
if (!MoreASTHelpers.isStringTyped(factoryArg, state)
&& !IS_STATIC_ENCLOSING_CLASS_REFERENCE.matches(factoryArg, state)) {
/*
* Loggers with a custom string name are generally "special", but those with a name derived
* from a class other than the one that encloses it are likely in error.
*/
fix.merge(SuggestedFix.replace(factoryArg, clazz.getSimpleName() + ".class"));
}
}
return fix.isEmpty() ? Description.NO_MATCH : describeMatch(tree, fix.build());
}
private static void suggestModifiers(
VariableTree tree,
ImmutableSet<Modifier> modifiers,
SuggestedFix.Builder fixBuilder,
VisitorState state) {
ModifiersTree modifiersTree =
requireNonNull(ASTHelpers.getModifiers(tree), "`VariableTree` must have modifiers");
SuggestedFixes.addModifiers(tree, modifiersTree, state, modifiers).ifPresent(fixBuilder::merge);
SuggestedFixes.removeModifiers(
modifiersTree, state, Sets.difference(EnumSet.allOf(Modifier.class), modifiers))
.ifPresent(fixBuilder::merge);
}
private static void suggestRename(
VariableTree variableTree, String name, SuggestedFix.Builder fixBuilder, VisitorState state) {
if (!variableTree.getName().contentEquals(name)) {
fixBuilder.merge(SuggestedFixes.renameVariable(variableTree, name, state));
}
}
private static boolean isEnclosingClassReference(ExpressionTree tree, VisitorState state) {
return ASTHelpers.getSymbol(getEnclosingClass(state)).equals(ASTHelpers.getSymbol(tree));
}
private static boolean getClassReceiverIsEnclosingClassInstance(
MethodInvocationTree getClassInvocationTree, VisitorState state) {
ExpressionTree receiver = ASTHelpers.getReceiver(getClassInvocationTree);
if (receiver == null) {
/*
* Method invocations without an explicit receiver either involve static methods (possibly
* statically imported), or instance methods invoked on the enclosing class. As the given
* `getClassInvocationTree` is guaranteed to be a nullary `#getClass()` invocation, the latter
* must be the case.
*/
return true;
}
Symbol symbol = ASTHelpers.getSymbol(receiver);
return symbol != null
&& symbol.asType().tsym.equals(ASTHelpers.getSymbol(getEnclosingClass(state)));
}
private static ClassTree getEnclosingClass(VisitorState state) {
ClassTree clazz = state.findEnclosing(ClassTree.class);
// XXX: Review whether we should relax this constraint in the face of so-called anonymous
// classes. See
// https://docs.oracle.com/en/java/javase/23/language/implicitly-declared-classes-and-instance-main-methods.html
verify(clazz != null, "Variable not defined inside class");
return clazz;
}
}

View File

@@ -1,8 +1,6 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.base.Preconditions.checkElementIndex;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Sets.toImmutableEnumSet;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import static java.util.Collections.disjoint;
import static java.util.Objects.checkIndex;
@@ -70,28 +68,6 @@ final class AssortedRules {
}
}
/**
* Use {@link Sets#toImmutableEnumSet()} when possible, as it is more efficient than {@link
* ImmutableSet#toImmutableSet()} and produces a more compact object.
*
* <p><strong>Warning:</strong> this rewrite rule is not completely behavior preserving: while the
* original code produces a set that iterates over the elements in encounter order, the
* replacement code iterates over the elements in enum definition order.
*/
// XXX: ^ Consider emitting a comment warning about this fact?
static final class StreamToImmutableEnumSet<T extends Enum<T>> {
@BeforeTemplate
ImmutableSet<T> before(Stream<T> stream) {
return stream.collect(toImmutableSet());
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
ImmutableSet<T> after(Stream<T> stream) {
return stream.collect(toImmutableEnumSet());
}
}
/** Prefer {@link Iterators#getNext(Iterator, Object)} over more contrived alternatives. */
static final class IteratorGetNextOrDefault<T> {
@BeforeTemplate

View File

@@ -9,6 +9,7 @@ import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.sun.tools.javac.util.Constants;
import com.sun.tools.javac.util.Convert;
import javax.lang.model.element.Name;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to {@link com.google.errorprone.bugpatterns.BugChecker} classes. */
@@ -67,4 +68,22 @@ final class BugCheckerRules {
return Constants.format(value);
}
}
/** Prefer {@link Name#contentEquals(CharSequence)} over more verbose alternatives. */
static final class NameContentEquals {
@BeforeTemplate
boolean before(Name name, CharSequence string) {
return name.toString().equals(string.toString());
}
@BeforeTemplate
boolean before(Name name, String string) {
return name.toString().equals(string);
}
@AfterTemplate
boolean after(Name name, CharSequence string) {
return name.contentEquals(string);
}
}
}

View File

@@ -73,24 +73,6 @@ final class ClassRules {
}
}
/**
* Prefer {@link Class#cast(Object)} method references over lambda expressions that require naming
* a variable.
*/
// XXX: Once the `ClassReferenceCast` rule is dropped, rename this rule to just `ClassCast`.
static final class ClassLiteralCast<T, S> {
@BeforeTemplate
@SuppressWarnings("unchecked")
Function<T, S> before() {
return t -> (S) t;
}
@AfterTemplate
Function<T, S> after() {
return Refaster.<S>clazz()::cast;
}
}
/**
* Prefer {@link Class#cast(Object)} method references over lambda expressions that require naming
* a variable.

View File

@@ -365,18 +365,20 @@ final class CollectionRules {
}
}
/**
* Don't call {@link ImmutableCollection#asList()} if {@link ImmutableCollection#iterator()} is
* called on the result; call it directly.
*/
static final class ImmutableCollectionIterator<T> {
/** Prefer {@link Collection#iterator()} over more contrived or less efficient alternatives. */
static final class CollectionIterator<T> {
@BeforeTemplate
Iterator<T> before(Collection<T> collection) {
return collection.stream().iterator();
}
@BeforeTemplate
Iterator<T> before(ImmutableCollection<T> collection) {
return collection.asList().iterator();
}
@AfterTemplate
Iterator<T> after(ImmutableCollection<T> collection) {
Iterator<T> after(Collection<T> collection) {
return collection.iterator();
}
}

View File

@@ -27,6 +27,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.function.Function;
@@ -244,6 +245,33 @@ final class ComparatorRules {
}
}
/** Prefer {@link Collections#sort(List)} over more verbose alternatives. */
static final class CollectionsSort<T extends Comparable<? super T>> {
@BeforeTemplate
void before(List<T> collection) {
Collections.sort(collection, naturalOrder());
}
@AfterTemplate
void after(List<T> collection) {
Collections.sort(collection);
}
}
/** Prefer {@link Collections#min(Collection)} over more verbose alternatives. */
static final class CollectionsMin<T extends Comparable<? super T>> {
@BeforeTemplate
T before(Collection<T> collection) {
return Refaster.anyOf(
Collections.min(collection, naturalOrder()), Collections.max(collection, reverseOrder()));
}
@AfterTemplate
T after(Collection<T> collection) {
return Collections.min(collection);
}
}
/**
* Avoid unnecessary creation of a {@link Stream} to determine the minimum of a known collection
* of values.
@@ -264,7 +292,7 @@ final class ComparatorRules {
* Avoid unnecessary creation of a {@link Stream} to determine the minimum of a known collection
* of values.
*/
static final class MinOfCollection<S, T extends S> {
static final class CollectionsMinWithComparator<S, T extends S> {
@BeforeTemplate
T before(Collection<T> collection, Comparator<S> cmp) {
return collection.stream().min(cmp).orElseThrow();
@@ -343,6 +371,20 @@ final class ComparatorRules {
}
}
/** Prefer {@link Collections#max(Collection)} over more verbose alternatives. */
static final class CollectionsMax<T extends Comparable<? super T>> {
@BeforeTemplate
T before(Collection<T> collection) {
return Refaster.anyOf(
Collections.max(collection, naturalOrder()), Collections.min(collection, reverseOrder()));
}
@AfterTemplate
T after(Collection<T> collection) {
return Collections.max(collection);
}
}
/**
* Avoid unnecessary creation of a {@link Stream} to determine the maximum of a known collection
* of values.
@@ -363,7 +405,7 @@ final class ComparatorRules {
* Avoid unnecessary creation of a {@link Stream} to determine the maximum of a known collection
* of values.
*/
static final class MaxOfCollection<S, T extends S> {
static final class CollectionsMaxWithComparator<S, T extends S> {
@BeforeTemplate
T before(Collection<T> collection, Comparator<S> cmp) {
return collection.stream().max(cmp).orElseThrow();

View File

@@ -0,0 +1,246 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Sets.toImmutableEnumSet;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
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.Repeated;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.stream.Stream;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/**
* Refaster rules related to expressions dealing with {@code
* com.google.common.collect.ImmutableEnumSet}s.
*/
// XXX: Some of the rules defined here impact iteration order. That's a rather subtle change. Should
// we emit a comment warning about this fact? (This may produce a lot of noise. A bug checker could
// in some cases determine whether iteration order is important.)
// XXX: Consider replacing the `SetsImmutableEnumSet[N]` Refaster rules with a bug checker, such
// that call to `ImmutableSet#of(Object, Object, Object, Object, Object, Object, Object[])` with
// enum-typed values can also be rewritten.
@OnlineDocumentation
final class ImmutableEnumSetRules {
private ImmutableEnumSetRules() {}
/**
* Prefer {@link Sets#immutableEnumSet(Iterable)} for enum collections to take advantage of the
* internally used {@link EnumSet}.
*
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the
* original code produces a set that iterates over its elements in the same order as the input
* {@link Iterable}, the replacement code iterates over the elements in enum definition order.
*/
static final class SetsImmutableEnumSetIterable<T extends Enum<T>> {
@BeforeTemplate
ImmutableSet<T> before(Iterable<T> elements) {
return ImmutableSet.copyOf(elements);
}
@BeforeTemplate
ImmutableSet<T> before(Collection<T> elements) {
return ImmutableSet.copyOf(elements);
}
@AfterTemplate
ImmutableSet<T> after(Iterable<T> elements) {
return Sets.immutableEnumSet(elements);
}
}
/**
* Prefer {@link Sets#immutableEnumSet(Iterable)} for enum collections to take advantage of the
* internally used {@link EnumSet}.
*
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the
* original code produces a set that iterates over its elements in the same order as defined in
* the array, the replacement code iterates over the elements in enum definition order.
*/
static final class SetsImmutableEnumSetArraysAsList<T extends Enum<T>> {
@BeforeTemplate
ImmutableSet<T> before(T[] elements) {
return ImmutableSet.copyOf(elements);
}
@AfterTemplate
ImmutableSet<T> after(T[] elements) {
return Sets.immutableEnumSet(Arrays.asList(elements));
}
}
/**
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
* the internally used {@link EnumSet}.
*/
static final class SetsImmutableEnumSet1<T extends Enum<T>> {
@BeforeTemplate
@SuppressWarnings("SetsImmutableEnumSetIterable" /* This is a more specific template. */)
ImmutableSet<T> before(T e1) {
return Refaster.anyOf(ImmutableSet.of(e1), ImmutableSet.copyOf(EnumSet.of(e1)));
}
@AfterTemplate
@SuppressWarnings("unchecked")
ImmutableSet<T> after(T e1) {
return Sets.immutableEnumSet(e1);
}
}
/**
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
* the internally used {@link EnumSet}.
*
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the {@link
* ImmutableSet#of} expression produces a set that iterates over its elements in the listed order,
* the replacement code iterates over the elements in enum definition order.
*/
static final class SetsImmutableEnumSet2<T extends Enum<T>> {
@BeforeTemplate
@SuppressWarnings("SetsImmutableEnumSetIterable" /* This is a more specific template. */)
ImmutableSet<T> before(T e1, T e2) {
return Refaster.anyOf(ImmutableSet.of(e1, e2), ImmutableSet.copyOf(EnumSet.of(e1, e2)));
}
@AfterTemplate
@SuppressWarnings("unchecked")
ImmutableSet<T> after(T e1, T e2) {
return Sets.immutableEnumSet(e1, e2);
}
}
/**
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
* the internally used {@link EnumSet}.
*
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the {@link
* ImmutableSet#of} expression produces a set that iterates over its elements in the listed order,
* the replacement code iterates over the elements in enum definition order.
*/
static final class SetsImmutableEnumSet3<T extends Enum<T>> {
@BeforeTemplate
@SuppressWarnings("SetsImmutableEnumSetIterable" /* This is a more specific template. */)
ImmutableSet<T> before(T e1, T e2, T e3) {
return Refaster.anyOf(
ImmutableSet.of(e1, e2, e3), ImmutableSet.copyOf(EnumSet.of(e1, e2, e3)));
}
@AfterTemplate
@SuppressWarnings("unchecked")
ImmutableSet<T> after(T e1, T e2, T e3) {
return Sets.immutableEnumSet(e1, e2, e3);
}
}
/**
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
* the internally used {@link EnumSet}.
*
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the {@link
* ImmutableSet#of} expression produces a set that iterates over its elements in the listed order,
* the replacement code iterates over the elements in enum definition order.
*/
static final class SetsImmutableEnumSet4<T extends Enum<T>> {
@BeforeTemplate
@SuppressWarnings("SetsImmutableEnumSetIterable" /* This is a more specific template. */)
ImmutableSet<T> before(T e1, T e2, T e3, T e4) {
return Refaster.anyOf(
ImmutableSet.of(e1, e2, e3, e4), ImmutableSet.copyOf(EnumSet.of(e1, e2, e3, e4)));
}
@AfterTemplate
@SuppressWarnings("unchecked")
ImmutableSet<T> after(T e1, T e2, T e3, T e4) {
return Sets.immutableEnumSet(e1, e2, e3, e4);
}
}
/**
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
* the internally used {@link EnumSet}.
*
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the {@link
* ImmutableSet#of} expression produces a set that iterates over its elements in the listed order,
* the replacement code iterates over the elements in enum definition order.
*/
static final class SetsImmutableEnumSet5<T extends Enum<T>> {
@BeforeTemplate
@SuppressWarnings("SetsImmutableEnumSetIterable" /* This is a more specific template. */)
ImmutableSet<T> before(T e1, T e2, T e3, T e4, T e5) {
return Refaster.anyOf(
ImmutableSet.of(e1, e2, e3, e4, e5), ImmutableSet.copyOf(EnumSet.of(e1, e2, e3, e4, e5)));
}
@AfterTemplate
@SuppressWarnings("unchecked")
ImmutableSet<T> after(T e1, T e2, T e3, T e4, T e5) {
return Sets.immutableEnumSet(e1, e2, e3, e4, e5);
}
}
/**
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
* the internally used {@link EnumSet}.
*
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the
* original code produces a set that iterates over its elements in the listed order, the
* replacement code iterates over the elements in enum definition order.
*/
static final class SetsImmutableEnumSet6<T extends Enum<T>> {
@BeforeTemplate
ImmutableSet<T> before(T e1, T e2, T e3, T e4, T e5, T e6) {
return ImmutableSet.of(e1, e2, e3, e4, e5, e6);
}
@AfterTemplate
@SuppressWarnings("unchecked")
ImmutableSet<T> after(T e1, T e2, T e3, T e4, T e5, T e6) {
return Sets.immutableEnumSet(e1, e2, e3, e4, e5, e6);
}
}
/**
* Prefer {@link Sets#immutableEnumSet(Enum, Enum[])} for enum collections to take advantage of
* the internally used {@link EnumSet}.
*/
static final class SetsImmutableEnumSetVarArgs<T extends Enum<T>> {
@BeforeTemplate
@SuppressWarnings("SetsImmutableEnumSetIterable" /* This is a more specific template. */)
ImmutableSet<T> before(T e1, @Repeated T elements) {
return ImmutableSet.copyOf(EnumSet.of(e1, Refaster.asVarargs(elements)));
}
@AfterTemplate
ImmutableSet<T> after(T e1, @Repeated T elements) {
return Sets.immutableEnumSet(e1, Refaster.asVarargs(elements));
}
}
/**
* Use {@link Sets#toImmutableEnumSet()} when possible, as it is more efficient than {@link
* ImmutableSet#toImmutableSet()} and produces a more compact object.
*
* <p><strong>Warning:</strong> this rule is not completely behavior preserving: while the
* original code produces a set that iterates over its elements in encounter order, the
* replacement code iterates over the elements in enum definition order.
*/
static final class StreamToImmutableEnumSet<T extends Enum<T>> {
@BeforeTemplate
ImmutableSet<T> before(Stream<T> stream) {
return stream.collect(toImmutableSet());
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
ImmutableSet<T> after(Stream<T> stream) {
return stream.collect(toImmutableEnumSet());
}
}
}

View File

@@ -0,0 +1,88 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to expressions dealing with Micrometer. */
// XXX: Consider replacing the `TagsOf[N]` rules with a bug checker, so that various other
// expressions (e.g. those creating other collection types, those passing in tags some other way, or
// those passing in more tags) can be replaced as wel.
@OnlineDocumentation
final class MicrometerRules {
private MicrometerRules() {}
/** Prefer using {@link Tags} over other immutable collections. */
static final class TagsOf1 {
@BeforeTemplate
ImmutableCollection<Tag> before(Tag tag) {
return Refaster.anyOf(ImmutableSet.of(tag), ImmutableList.of(tag));
}
@AfterTemplate
Iterable<Tag> after(Tag tag) {
return Tags.of(tag);
}
}
/** Prefer using {@link Tags} over other immutable collections. */
static final class TagsOf2 {
@BeforeTemplate
ImmutableCollection<Tag> before(Tag tag1, Tag tag2) {
return Refaster.anyOf(ImmutableSet.of(tag1, tag2), ImmutableList.of(tag1, tag2));
}
@AfterTemplate
Iterable<Tag> after(Tag tag1, Tag tag2) {
return Tags.of(tag1, tag2);
}
}
/** Prefer using {@link Tags} over other immutable collections. */
static final class TagsOf3 {
@BeforeTemplate
ImmutableCollection<Tag> before(Tag tag1, Tag tag2, Tag tag3) {
return Refaster.anyOf(ImmutableSet.of(tag1, tag2, tag3), ImmutableList.of(tag1, tag2, tag3));
}
@AfterTemplate
Iterable<Tag> after(Tag tag1, Tag tag2, Tag tag3) {
return Tags.of(tag1, tag2, tag3);
}
}
/** Prefer using {@link Tags} over other immutable collections. */
static final class TagsOf4 {
@BeforeTemplate
ImmutableCollection<Tag> before(Tag tag1, Tag tag2, Tag tag3, Tag tag4) {
return Refaster.anyOf(
ImmutableSet.of(tag1, tag2, tag3, tag4), ImmutableList.of(tag1, tag2, tag3, tag4));
}
@AfterTemplate
Iterable<Tag> after(Tag tag1, Tag tag2, Tag tag3, Tag tag4) {
return Tags.of(tag1, tag2, tag3, tag4);
}
}
/** Prefer using {@link Tags} over other immutable collections. */
static final class TagsOf5 {
@BeforeTemplate
ImmutableCollection<Tag> before(Tag tag1, Tag tag2, Tag tag3, Tag tag4, Tag tag5) {
return Refaster.anyOf(
ImmutableSet.of(tag1, tag2, tag3, tag4, tag5),
ImmutableList.of(tag1, tag2, tag3, tag4, tag5));
}
@AfterTemplate
Iterable<Tag> after(Tag tag1, Tag tag2, Tag tag3, Tag tag4, Tag tag5) {
return Tags.of(tag1, tag2, tag3, tag4, tag5);
}
}
}

View File

@@ -20,7 +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;
import tech.picnic.errorprone.refaster.matchers.RequiresComputation;
/** Refaster rules related to expressions dealing with {@link Optional}s. */
@OnlineDocumentation
@@ -255,24 +255,21 @@ 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.
* Prefer {@link Optional#orElse(Object)} over {@link Optional#orElseGet(Supplier)} if the
* fallback value does not require non-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> {
// XXX: This rule is the counterpart to the `OptionalOrElseGet` bug checker. Once the
// `MethodReferenceUsage` bug checker is "production ready", that bug checker may similarly be
// replaced with a Refaster rule.
static final class OptionalOrElse<T> {
@BeforeTemplate
T before(Optional<T> optional, @NotMatches(IsLikelyTrivialComputation.class) T value) {
return optional.orElse(value);
T before(Optional<T> optional, @NotMatches(RequiresComputation.class) T value) {
return optional.orElseGet(() -> value);
}
@AfterTemplate
T after(Optional<T> optional, T value) {
return optional.orElseGet(() -> value);
return optional.orElse(value);
}
}
@@ -373,7 +370,12 @@ final class OptionalRules {
/** Prefer {@link Optional#or(Supplier)} over more verbose alternatives. */
static final class OptionalOrOtherOptional<T> {
@BeforeTemplate
@SuppressWarnings("NestedOptionals")
@SuppressWarnings({
"LexicographicalAnnotationAttributeListing" /* `key-*` entry must remain last. */,
"NestedOptionals" /* This violation will be rewritten. */,
"OptionalOrElse" /* Parameters represent expressions that may require computation. */,
"key-to-resolve-AnnotationUseStyle-and-TrailingComment-check-conflict"
})
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.

View File

@@ -8,6 +8,8 @@ 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.common.primitives.UnsignedInts;
import com.google.common.primitives.UnsignedLongs;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.AlsoNegation;
@@ -76,6 +78,8 @@ final class PrimitiveRules {
}
/** Prefer {@link Math#toIntExact(long)} over the Guava alternative. */
// XXX: This rule changes the exception possibly thrown from `IllegalArgumentException` to
// `ArithmeticException`.
static final class LongToIntExact {
@BeforeTemplate
int before(long l) {
@@ -192,97 +196,6 @@ final class PrimitiveRules {
}
}
/** 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
@@ -442,4 +355,205 @@ final class PrimitiveRules {
return Long.signum(l) == -1;
}
}
/** Prefer JDK's {@link Integer#compareUnsigned(int, int)} over third-party alternatives. */
static final class IntegerCompareUnsigned {
@BeforeTemplate
int before(int x, int y) {
return UnsignedInts.compare(x, y);
}
@AfterTemplate
int after(int x, int y) {
return Integer.compareUnsigned(x, y);
}
}
/** Prefer JDK's {@link Long#compareUnsigned(long, long)} over third-party alternatives. */
static final class LongCompareUnsigned {
@BeforeTemplate
long before(long x, long y) {
return UnsignedLongs.compare(x, y);
}
@AfterTemplate
long after(long x, long y) {
return Long.compareUnsigned(x, y);
}
}
/** Prefer JDK's {@link Integer#divideUnsigned(int, int)} over third-party alternatives. */
static final class IntegerDivideUnsigned {
@BeforeTemplate
int before(int x, int y) {
return UnsignedInts.divide(x, y);
}
@AfterTemplate
int after(int x, int y) {
return Integer.divideUnsigned(x, y);
}
}
/** Prefer JDK's {@link Long#divideUnsigned(long, long)} over third-party alternatives. */
static final class LongDivideUnsigned {
@BeforeTemplate
long before(long x, long y) {
return UnsignedLongs.divide(x, y);
}
@AfterTemplate
long after(long x, long y) {
return Long.divideUnsigned(x, y);
}
}
/** Prefer JDK's {@link Integer#remainderUnsigned(int, int)} over third-party alternatives. */
static final class IntegerRemainderUnsigned {
@BeforeTemplate
int before(int x, int y) {
return UnsignedInts.remainder(x, y);
}
@AfterTemplate
int after(int x, int y) {
return Integer.remainderUnsigned(x, y);
}
}
/** Prefer JDK's {@link Long#remainderUnsigned(long, long)} over third-party alternatives. */
static final class LongRemainderUnsigned {
@BeforeTemplate
long before(long x, long y) {
return UnsignedLongs.remainder(x, y);
}
@AfterTemplate
long after(long x, long y) {
return Long.remainderUnsigned(x, y);
}
}
/**
* Prefer JDK's {@link Integer#parseUnsignedInt(String)} over third-party or more verbose
* alternatives.
*/
static final class IntegerParseUnsignedInt {
@BeforeTemplate
int before(String string) {
return Refaster.anyOf(
UnsignedInts.parseUnsignedInt(string), Integer.parseUnsignedInt(string, 10));
}
@AfterTemplate
int after(String string) {
return Integer.parseUnsignedInt(string);
}
}
/**
* Prefer JDK's {@link Long#parseUnsignedLong(String)} over third-party or more verbose
* alternatives.
*/
static final class LongParseUnsignedLong {
@BeforeTemplate
long before(String string) {
return Refaster.anyOf(
UnsignedLongs.parseUnsignedLong(string), Long.parseUnsignedLong(string, 10));
}
@AfterTemplate
long after(String string) {
return Long.parseUnsignedLong(string);
}
}
/** Prefer JDK's {@link Integer#parseUnsignedInt(String, int)} over third-party alternatives. */
static final class IntegerParseUnsignedIntWithRadix {
@BeforeTemplate
int before(String string, int radix) {
return UnsignedInts.parseUnsignedInt(string, radix);
}
@AfterTemplate
int after(String string, int radix) {
return Integer.parseUnsignedInt(string, radix);
}
}
/** Prefer JDK's {@link Long#parseUnsignedLong(String, int)} over third-party alternatives. */
static final class LongParseUnsignedLongWithRadix {
@BeforeTemplate
long before(String string, int radix) {
return UnsignedLongs.parseUnsignedLong(string, radix);
}
@AfterTemplate
long after(String string, int radix) {
return Long.parseUnsignedLong(string, radix);
}
}
/**
* Prefer JDK's {@link Integer#toUnsignedString(int)} over third-party or more verbose
* alternatives.
*/
static final class IntegerToUnsignedString {
@BeforeTemplate
String before(int i) {
return Refaster.anyOf(UnsignedInts.toString(i), Integer.toUnsignedString(i, 10));
}
@AfterTemplate
String after(int i) {
return Integer.toUnsignedString(i);
}
}
/**
* Prefer JDK's {@link Long#toUnsignedString(long)} over third-party or more verbose alternatives.
*/
static final class LongToUnsignedString {
@BeforeTemplate
String before(long i) {
return Refaster.anyOf(UnsignedLongs.toString(i), Long.toUnsignedString(i, 10));
}
@AfterTemplate
String after(long i) {
return Long.toUnsignedString(i);
}
}
/**
* Prefer JDK's {@link Integer#toUnsignedString(int,int)} over third-party or more verbose
* alternatives.
*/
static final class IntegerToUnsignedStringWithRadix {
@BeforeTemplate
String before(int i, int radix) {
return UnsignedInts.toString(i, radix);
}
@AfterTemplate
String after(int i, int radix) {
return Integer.toUnsignedString(i, radix);
}
}
/**
* Prefer JDK's {@link Long#toUnsignedString(long,int)} over third-party or more verbose
* alternatives.
*/
static final class LongToUnsignedStringWithRadix {
@BeforeTemplate
String before(long i, int radix) {
return UnsignedLongs.toString(i, radix);
}
@AfterTemplate
String after(long i, int radix) {
return Long.toUnsignedString(i, radix);
}
}
}

View File

@@ -13,6 +13,7 @@ import static java.util.stream.Collectors.toCollection;
import static org.assertj.core.api.Assertions.assertThat;
import static reactor.function.TupleUtils.function;
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -1956,6 +1957,22 @@ final class ReactorRules {
}
}
/**
* Don't propagate {@link Mono} cancellations to an upstream cache value computation, as
* completion of such computations may benefit concurrent or subsequent cache usages.
*/
static final class MonoFromFutureAsyncLoadingCacheGet<K, V> {
@BeforeTemplate
Mono<V> before(AsyncLoadingCache<K, V> cache, K key) {
return Mono.fromFuture(() -> cache.get(key));
}
@AfterTemplate
Mono<V> after(AsyncLoadingCache<K, V> cache, K key) {
return Mono.fromFuture(() -> cache.get(key), /* suppressCancel= */ true);
}
}
/**
* Prefer {@link Flux#fromStream(Supplier)} over {@link Flux#fromStream(Stream)}, as the former
* yields a {@link Flux} that is more likely to behave as expected when subscribed to more than

View File

@@ -244,4 +244,105 @@ final class StringRules {
return Utf8.encodedLength(str);
}
}
/** Prefer {@link String#indexOf(int, int)} over less efficient alternatives. */
static final class StringIndexOfChar {
@BeforeTemplate
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
int before(String string, int ch, int fromIndex) {
return string.substring(fromIndex).indexOf(ch);
}
@AfterTemplate
int after(String string, int ch, int fromIndex) {
return Math.max(-1, string.indexOf(ch, fromIndex) - fromIndex);
}
}
/** Prefer {@link String#indexOf(String, int)} over less efficient alternatives. */
static final class StringIndexOfString {
@BeforeTemplate
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
int before(String string, String substring, int fromIndex) {
return string.substring(fromIndex).indexOf(substring);
}
@AfterTemplate
int after(String string, String substring, int fromIndex) {
return Math.max(-1, string.indexOf(substring, fromIndex) - fromIndex);
}
}
// XXX: Once we compile Refaster templates with JDK 21 also suggest `String#indexOf(int, int,
// int)` and `String#indexOf(String, int, int)`.
/** Prefer {@link String#lastIndexOf(int, int)} over less efficient alternatives. */
static final class StringLastIndexOfChar {
@BeforeTemplate
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
int before(String string, int ch, int fromIndex) {
return string.substring(fromIndex).lastIndexOf(ch);
}
@AfterTemplate
int after(String string, int ch, int fromIndex) {
return Math.max(-1, string.lastIndexOf(ch) - fromIndex);
}
}
/** Prefer {@link String#lastIndexOf(String, int)} over less efficient alternatives. */
static final class StringLastIndexOfString {
@BeforeTemplate
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
int before(String string, String substring, int fromIndex) {
return string.substring(fromIndex).lastIndexOf(substring);
}
@AfterTemplate
int after(String string, String substring, int fromIndex) {
return Math.max(-1, string.lastIndexOf(substring) - fromIndex);
}
}
/** Prefer {@link String#lastIndexOf(int, int)} over less efficient alternatives. */
static final class StringLastIndexOfCharWithIndex {
@BeforeTemplate
int before(String string, int ch, int fromIndex) {
return string.substring(0, fromIndex).lastIndexOf(ch);
}
@AfterTemplate
int after(String string, int ch, int fromIndex) {
return string.lastIndexOf(ch, fromIndex - 1);
}
}
/** Prefer {@link String#lastIndexOf(String, int)} over less efficient alternatives. */
// XXX: The replacement expression isn't fully equivalent: in case `substring` is empty, then
// the replacement yields `fromIndex - 1` rather than `fromIndex`.
static final class StringLastIndexOfStringWithIndex {
@BeforeTemplate
int before(String string, String substring, int fromIndex) {
return string.substring(0, fromIndex).lastIndexOf(substring);
}
@AfterTemplate
int after(String string, String substring, int fromIndex) {
return string.lastIndexOf(substring, fromIndex - 1);
}
}
/** Prefer {@link String#startsWith(String, int)} over less efficient alternatives. */
static final class StringStartsWith {
@BeforeTemplate
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
boolean before(String string, String prefix, int fromIndex) {
return string.substring(fromIndex).startsWith(prefix);
}
@AfterTemplate
boolean after(String string, String prefix, int fromIndex) {
return string.startsWith(prefix, fromIndex);
}
}
}

View File

@@ -0,0 +1,70 @@
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 ClassCastLambdaUsageTest {
@Test
void identification() {
CompilationTestHelper.newInstance(ClassCastLambdaUsage.class, getClass())
.addSourceLines(
"A.java",
"import com.google.common.collect.ImmutableSet;",
"import java.util.stream.IntStream;",
"import java.util.stream.Stream;",
"",
"class A {",
" void m() {",
" Number localVariable = 0;",
"",
" Stream.of(0).map(i -> i);",
" Stream.of(1).map(i -> i + 1);",
" Stream.of(2).map(Integer.class::cast);",
" Stream.of(3).map(i -> (Integer) 2);",
" Stream.of(4).map(i -> (Integer) localVariable);",
" // XXX: Ideally this case is also flagged. Pick this up in the context of merging the",
" // `ClassCastLambdaUsage` and `MethodReferenceUsage` checks, or introduce a separate check that",
" // simplifies unnecessary block lambda expressions.",
" Stream.of(5)",
" .map(",
" i -> {",
" return (Integer) i;",
" });",
" Stream.<ImmutableSet>of(ImmutableSet.of(5)).map(s -> (ImmutableSet<Number>) s);",
" Stream.of(ImmutableSet.of(6)).map(s -> (ImmutableSet<?>) s);",
" Stream.of(7).reduce((a, b) -> (Integer) a);",
" IntStream.of(8).mapToObj(i -> (char) i);",
"",
" // BUG: Diagnostic contains:",
" Stream.of(8).map(i -> (Integer) i);",
" }",
"}")
.doTest();
}
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(ClassCastLambdaUsage.class, getClass())
.addInputLines(
"A.java",
"import java.util.stream.Stream;",
"",
"class A {",
" void m() {",
" Stream.of(1).map(i -> (Integer) i);",
" }",
"}")
.addOutputLines(
"A.java",
"import java.util.stream.Stream;",
"",
"class A {",
" void m() {",
" Stream.of(1).map(Integer.class::cast);",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -0,0 +1,78 @@
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 ConstantNamingTest {
@Test
void identification() {
CompilationTestHelper.newInstance(ConstantNaming.class, getClass())
.addSourceLines(
"A.java",
"class A {",
" private static final long serialVersionUID = 1L;",
" private static final int FOO = 1;",
" // BUG: Diagnostic contains: consider renaming to 'BAR', though note that this is not a private",
" // constant",
" static final int bar = 2;",
" // BUG: Diagnostic contains:",
" private static final int baz = 3;",
" // BUG: Diagnostic contains: consider renaming to 'QUX_QUUX', though note that a variable with",
" // this name is already declared",
" private static final int qux_QUUX = 4;",
" // BUG: Diagnostic contains: consider renaming to 'QUUZ', though note that a variable with",
" // this name is already declared",
" private static final int quuz = 3;",
"",
" private final int foo = 4;",
" private final Runnable QUX_QUUX =",
" new Runnable() {",
" private static final int QUUZ = 1;",
"",
" @Override",
" public void run() {}",
" };",
"}")
.doTest();
}
@Test
void identificationWithCustomExemption() {
CompilationTestHelper.newInstance(ConstantNaming.class, getClass())
.setArgs("-XepOpt:CanonicalConstantNaming:ExemptedNames=foo,baz")
.addSourceLines(
"A.java",
"class A {",
" private static final long serialVersionUID = 1L;",
" private static final int foo = 1;",
" // BUG: Diagnostic contains:",
" private static final int bar = 2;",
" private static final int baz = 3;",
"}")
.doTest();
}
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(ConstantNaming.class, getClass())
.addInputLines(
"A.java",
"class A {",
" static final int foo = 1;",
" private static final int bar = 2;",
" private static final int baz = 3;",
" private static final int BAZ = 4;",
"}")
.addOutputLines(
"A.java",
"class A {",
" static final int foo = 1;",
" private static final int BAR = 2;",
" private static final int baz = 3;",
" private static final int BAZ = 4;",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -18,22 +18,23 @@ final class IsInstanceLambdaUsageTest {
" void m() {",
" Integer localVariable = 0;",
"",
" Stream.of(0).map(i -> i + 1);",
" Stream.of(1).filter(Integer.class::isInstance);",
" Stream.of(2).filter(i -> i.getClass() instanceof Class);",
" Stream.of(3).filter(i -> localVariable instanceof Integer);",
" Stream.of(0).map(i -> i);",
" Stream.of(1).map(i -> i + 1);",
" Stream.of(2).filter(Integer.class::isInstance);",
" Stream.of(3).filter(i -> i.getClass() instanceof Class);",
" Stream.of(4).filter(i -> localVariable instanceof Integer);",
" // XXX: Ideally this case is also flagged. Pick this up in the context of merging the",
" // `IsInstanceLambdaUsage` and `MethodReferenceUsage` checks, or introduce a separate check that",
" // simplifies unnecessary block lambda expressions.",
" Stream.of(4)",
" Stream.of(5)",
" .filter(",
" i -> {",
" return localVariable instanceof Integer;",
" return i instanceof Integer;",
" });",
" Flux.just(5, \"foo\").distinctUntilChanged(v -> v, (a, b) -> a instanceof Integer);",
" Flux.just(6, \"foo\").distinctUntilChanged(v -> v, (a, b) -> a instanceof Integer);",
"",
" // BUG: Diagnostic contains:",
" Stream.of(6).filter(i -> i instanceof Integer);",
" Stream.of(7).filter(i -> i instanceof Integer);",
" }",
"}")
.doTest();

View File

@@ -29,6 +29,10 @@ final class LexicographicalAnnotationAttributeListingTest {
" @interface Foo {",
" String[] value() default {};",
"",
" boolean[] bools() default {};",
"",
" char[] chars() default {};",
"",
" int[] ints() default {};",
"",
" Class<?>[] cls() default {};",
@@ -69,6 +73,32 @@ final class LexicographicalAnnotationAttributeListingTest {
" @Foo({\"a\", \"A\"})",
" A unsortedStringCaseInsensitiveWithTotalOrderFallback();",
"",
" @Foo(bools = {})",
" A noBools();",
"",
" @Foo(bools = {false})",
" A oneBool();",
"",
" @Foo(bools = {false, true})",
" A sortedBools();",
"",
" // BUG: Diagnostic contains:",
" @Foo(bools = {true, false})",
" A unsortedBools();",
"",
" @Foo(chars = {})",
" A noChars();",
"",
" @Foo(chars = {'a'})",
" A oneChar();",
"",
" @Foo(chars = {'a', 'b'})",
" A sortedChars();",
"",
" // BUG: Diagnostic contains:",
" @Foo(chars = {'b', 'a'})",
" A unsortedChars();",
"",
" @Foo(ints = {})",
" A noInts();",
"",
@@ -173,6 +203,10 @@ final class LexicographicalAnnotationAttributeListingTest {
" @interface Foo {",
" String[] value() default {};",
"",
" boolean[] bools() default {};",
"",
" char[] chars() default {};",
"",
" Class<?>[] cls() default {};",
"",
" RoundingMode[] enums() default {};",
@@ -185,7 +219,13 @@ final class LexicographicalAnnotationAttributeListingTest {
" }",
"",
" @Foo({\" \", \"\", \"b\", \"a\"})",
" A unsortedString();",
" A unsortedStrings();",
"",
" @Foo(bools = {true, false})",
" A unsortedBooleans();",
"",
" @Foo(chars = {'b', 'a'})",
" A unsortedChars();",
"",
" @Foo(cls = {long.class, int.class})",
" A unsortedClasses();",
@@ -210,6 +250,10 @@ final class LexicographicalAnnotationAttributeListingTest {
" @interface Foo {",
" String[] value() default {};",
"",
" boolean[] bools() default {};",
"",
" char[] chars() default {};",
"",
" Class<?>[] cls() default {};",
"",
" RoundingMode[] enums() default {};",
@@ -222,7 +266,13 @@ final class LexicographicalAnnotationAttributeListingTest {
" }",
"",
" @Foo({\"\", \" \", \"a\", \"b\"})",
" A unsortedString();",
" A unsortedStrings();",
"",
" @Foo(bools = {false, true})",
" A unsortedBooleans();",
"",
" @Foo(chars = {'a', 'b'})",
" A unsortedChars();",
"",
" @Foo(cls = {int.class, long.class})",
" A unsortedClasses();",

View File

@@ -5,14 +5,15 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class OptionalOrElseTest {
final class OptionalOrElseGetTest {
@Test
void identification() {
CompilationTestHelper.newInstance(OptionalOrElse.class, getClass())
CompilationTestHelper.newInstance(OptionalOrElseGet.class, getClass())
.addSourceLines(
"A.java",
"import com.google.errorprone.refaster.Refaster;",
"import java.util.Optional;",
"import java.util.function.Supplier;",
"",
"class A {",
" private final Optional<Object> optional = Optional.empty();",
@@ -27,6 +28,7 @@ final class OptionalOrElseTest {
" optional.orElse(string);",
" optional.orElse(this.string);",
" optional.orElse(Refaster.anyOf(\"constant\", \"another\"));",
" Optional.<Supplier<String>>empty().orElse(() -> \"constant\");",
"",
" // BUG: Diagnostic contains:",
" Optional.empty().orElse(string + \"constant\");",
@@ -67,7 +69,7 @@ final class OptionalOrElseTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(OptionalOrElse.class, getClass())
BugCheckerRefactoringTestHelper.newInstance(OptionalOrElseGet.class, getClass())
.addInputLines(
"A.java",
"import java.util.Optional;",

View File

@@ -0,0 +1,219 @@
package tech.picnic.errorprone.bugpatterns;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class Slf4jLoggerDeclarationTest {
@Test
void identification() {
CompilationTestHelper.newInstance(Slf4jLoggerDeclaration.class, getClass())
.addSourceLines(
"A.java",
"import static java.lang.Class.forName;",
"",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"",
"class A {",
" private static final long serialVersionUID = 1L;",
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
"",
" abstract static class DynamicLogger {",
" private final Logger log = LoggerFactory.getLogger(getClass());",
" }",
"",
" abstract static class DynamicLoggerWithExplicitThis {",
" private final Logger log = LoggerFactory.getLogger(this.getClass());",
" }",
"",
" static final class StaticLogger {",
" private static final Logger LOG = LoggerFactory.getLogger(StaticLogger.class);",
" }",
"",
" static final class StaticLoggerWithCustomIdentifier {",
" private static final Logger LOG = LoggerFactory.getLogger(\"custom-identifier\");",
" }",
"",
" interface StaticLoggerForInterface {",
" Logger LOG = LoggerFactory.getLogger(StaticLoggerForInterface.class);",
" }",
"",
" abstract static class DynamicLoggerForWrongTypeWithoutReceiver {",
" // BUG: Diagnostic contains:",
" private final Logger log = LoggerFactory.getLogger(forName(\"A.class\"));",
"",
" DynamicLoggerForWrongTypeWithoutReceiver() throws ClassNotFoundException {}",
" }",
"",
" abstract static class DynamicLoggerForWrongTypeWithoutSymbol {",
" // BUG: Diagnostic contains:",
" private final Logger log = LoggerFactory.getLogger(\"foo\".getClass());",
" }",
"",
" abstract static class DynamicLoggerForWrongTypeWithSymbol {",
" // BUG: Diagnostic contains:",
" private final Logger log = LoggerFactory.getLogger(new A().getClass());",
" }",
"",
" static final class NonAbstractDynamicLogger {",
" // BUG: Diagnostic contains:",
" private final Logger log = LoggerFactory.getLogger(getClass());",
" }",
"",
" abstract static class DynamicLoggerWithMissingModifier {",
" // BUG: Diagnostic contains:",
" final Logger log = LoggerFactory.getLogger(getClass());",
" }",
"",
" abstract static class DynamicLoggerWithExcessModifier {",
" // BUG: Diagnostic contains:",
" private final transient Logger log = LoggerFactory.getLogger(getClass());",
" }",
"",
" abstract static class MisnamedDynamicLogger {",
" // BUG: Diagnostic contains:",
" private final Logger LOG = LoggerFactory.getLogger(getClass());",
" }",
"",
" static final class StaticLoggerWithMissingModifier {",
" // BUG: Diagnostic contains:",
" static final Logger LOG = LoggerFactory.getLogger(StaticLoggerWithMissingModifier.class);",
" }",
"",
" static final class StaticLoggerWithExcessModifier {",
" // BUG: Diagnostic contains:",
" private static final transient Logger LOG =",
" LoggerFactory.getLogger(StaticLoggerWithExcessModifier.class);",
" }",
"",
" static final class MisnamedStaticLogger {",
" // BUG: Diagnostic contains:",
" private static final Logger log = LoggerFactory.getLogger(MisnamedStaticLogger.class);",
" }",
"",
" static final class StaticLoggerWithIncorrectIdentifier {",
" // BUG: Diagnostic contains:",
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
" }",
"",
" static final class StaticLoggerWithCustomIdentifierAndMissingModifier {",
" // BUG: Diagnostic contains:",
" static final Logger LOG = LoggerFactory.getLogger(\"custom-identifier\");",
" }",
"",
" static final class StaticLoggerWithCustomIdentifierAndExcessModifier {",
" // BUG: Diagnostic contains:",
" private static final transient Logger LOG = LoggerFactory.getLogger(\"custom-identifier\");",
" }",
"",
" static final class MisnamedStaticLoggerWithCustomIdentifier {",
" // BUG: Diagnostic contains:",
" private static final Logger log = LoggerFactory.getLogger(\"custom-identifier\");",
" }",
"",
" interface StaticLoggerForInterfaceWithExcessModifier {",
" // BUG: Diagnostic contains:",
" static Logger LOG = LoggerFactory.getLogger(StaticLoggerForInterfaceWithExcessModifier.class);",
" }",
"",
" interface MisnamedStaticLoggerForInterface {",
" // BUG: Diagnostic contains:",
" Logger log = LoggerFactory.getLogger(MisnamedStaticLoggerForInterface.class);",
" }",
"",
" interface StaticLoggerForInterfaceWithIncorrectIdentifier {",
" // BUG: Diagnostic contains:",
" Logger LOG = LoggerFactory.getLogger(A.class);",
" }",
"}")
.doTest();
}
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(Slf4jLoggerDeclaration.class, getClass())
.addInputLines(
"A.java",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"",
"class A {",
" static Logger foo = LoggerFactory.getLogger(Logger.class);",
"",
" abstract static class DynamicLogger {",
" transient Logger BAR = LoggerFactory.getLogger(getClass());",
" }",
"",
" static final class StaticLogger {",
" transient Logger baz = LoggerFactory.getLogger(LoggerFactory.class);",
" }",
"",
" static final class StaticLoggerWithCustomIdentifier {",
" transient Logger qux = LoggerFactory.getLogger(\"custom-identifier\");",
" }",
"",
" interface StaticLoggerForInterface {",
" public static final Logger quux = LoggerFactory.getLogger(A.class);",
" }",
"}")
.addOutputLines(
"A.java",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"",
"class A {",
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
"",
" abstract static class DynamicLogger {",
" private final Logger log = LoggerFactory.getLogger(getClass());",
" }",
"",
" static final class StaticLogger {",
" private static final Logger LOG = LoggerFactory.getLogger(StaticLogger.class);",
" }",
"",
" static final class StaticLoggerWithCustomIdentifier {",
" private static final Logger LOG = LoggerFactory.getLogger(\"custom-identifier\");",
" }",
"",
" interface StaticLoggerForInterface {",
" Logger LOG = LoggerFactory.getLogger(StaticLoggerForInterface.class);",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
@Test
void replacementWithCustomLoggerName() {
BugCheckerRefactoringTestHelper.newInstance(Slf4jLoggerDeclaration.class, getClass())
.setArgs(ImmutableList.of("-XepOpt:Slf4jLogDeclaration:CanonicalStaticLoggerName=FOO_BAR"))
.addInputLines(
"A.java",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"",
"class A {",
" transient Logger LOG = LoggerFactory.getLogger(Logger.class);",
"",
" abstract static class DynamicLogger {",
" transient Logger log = LoggerFactory.getLogger(getClass());",
" }",
"}")
.addOutputLines(
"A.java",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"",
"class A {",
" private static final Logger FOO_BAR = LoggerFactory.getLogger(A.class);",
"",
" abstract static class DynamicLogger {",
" private final Logger fooBar = LoggerFactory.getLogger(getClass());",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -43,6 +43,7 @@ final class RefasterRulesTest {
EqualityRules.class,
FileRules.class,
InputStreamRules.class,
ImmutableEnumSetRules.class,
ImmutableListRules.class,
ImmutableListMultimapRules.class,
ImmutableMapRules.class,
@@ -58,6 +59,7 @@ final class RefasterRulesTest {
LongStreamRules.class,
MapEntryRules.class,
MapRules.class,
MicrometerRules.class,
MockitoRules.class,
MultimapRules.class,
NullRules.class,

View File

@@ -1,10 +1,7 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.BoundType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@@ -24,8 +21,7 @@ final class AssortedRulesTest implements RefasterRuleCollectionTestCase {
Preconditions.class,
Sets.class,
Splitter.class,
Streams.class,
toImmutableSet());
Streams.class);
}
int testCheckIndex() {
@@ -38,10 +34,6 @@ final class AssortedRulesTest implements RefasterRuleCollectionTestCase {
}
}
ImmutableSet<BoundType> testStreamToImmutableEnumSet() {
return Stream.of(BoundType.OPEN).collect(toImmutableSet());
}
ImmutableSet<String> testIteratorGetNextOrDefault() {
return ImmutableSet.of(
ImmutableList.of("a").iterator().hasNext()

View File

@@ -1,12 +1,9 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Sets.toImmutableEnumSet;
import static java.util.Objects.checkIndex;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.BoundType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@@ -27,8 +24,7 @@ final class AssortedRulesTest implements RefasterRuleCollectionTestCase {
Preconditions.class,
Sets.class,
Splitter.class,
Streams.class,
toImmutableSet());
Streams.class);
}
int testCheckIndex() {
@@ -39,10 +35,6 @@ final class AssortedRulesTest implements RefasterRuleCollectionTestCase {
checkIndex(1, 2);
}
ImmutableSet<BoundType> testStreamToImmutableEnumSet() {
return Stream.of(BoundType.OPEN).collect(toImmutableEnumSet());
}
ImmutableSet<String> testIteratorGetNextOrDefault() {
return ImmutableSet.of(
Iterators.getNext(ImmutableList.of("a").iterator(), "foo"),

View File

@@ -5,6 +5,7 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
import com.google.errorprone.bugpatterns.BugChecker;
import com.sun.tools.javac.util.Convert;
import javax.lang.model.element.Name;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class BugCheckerRulesTest implements RefasterRuleCollectionTestCase {
@@ -31,4 +32,10 @@ final class BugCheckerRulesTest implements RefasterRuleCollectionTestCase {
String testConstantsFormat() {
return String.format("\"%s\"", Convert.quote("foo"));
}
ImmutableSet<Boolean> testNameContentEquals() {
return ImmutableSet.of(
((Name) null).toString().equals("foo".subSequence(0, 1).toString()),
((com.sun.tools.javac.util.Name) null).toString().equals("bar"));
}
}

View File

@@ -6,6 +6,7 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
import com.google.errorprone.bugpatterns.BugChecker;
import com.sun.tools.javac.util.Constants;
import com.sun.tools.javac.util.Convert;
import javax.lang.model.element.Name;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class BugCheckerRulesTest implements RefasterRuleCollectionTestCase {
@@ -30,4 +31,10 @@ final class BugCheckerRulesTest implements RefasterRuleCollectionTestCase {
String testConstantsFormat() {
return Constants.format("foo");
}
ImmutableSet<Boolean> testNameContentEquals() {
return ImmutableSet.of(
((Name) null).contentEquals("foo".subSequence(0, 1)),
((com.sun.tools.javac.util.Name) null).contentEquals("bar"));
}
}

View File

@@ -24,10 +24,6 @@ final class ClassRulesTest implements RefasterRuleCollectionTestCase {
return s -> clazz.isInstance(s);
}
Function<Number, Integer> testClassLiteralCast() {
return i -> (Integer) i;
}
Function<Number, Integer> testClassReferenceCast() {
return i -> Integer.class.cast(i);
}

View File

@@ -24,10 +24,6 @@ final class ClassRulesTest implements RefasterRuleCollectionTestCase {
return clazz::isInstance;
}
Function<Number, Integer> testClassLiteralCast() {
return Integer.class::cast;
}
Function<Number, Integer> testClassReferenceCast() {
return Integer.class::cast;
}

View File

@@ -119,8 +119,9 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableSet.of(1).asList().toArray(Integer[]::new);
}
Iterator<Integer> testImmutableCollectionIterator() {
return ImmutableSet.of(1).asList().iterator();
ImmutableSet<Iterator<Integer>> testCollectionIterator() {
return ImmutableSet.of(
ImmutableSet.of(1).stream().iterator(), ImmutableSet.of(2).asList().iterator());
}
ImmutableSet<Optional<Integer>> testOptionalFirstCollectionElement() {

View File

@@ -109,8 +109,8 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableSet.of(1).toArray(Integer[]::new);
}
Iterator<Integer> testImmutableCollectionIterator() {
return ImmutableSet.of(1).iterator();
ImmutableSet<Iterator<Integer>> testCollectionIterator() {
return ImmutableSet.of(ImmutableSet.of(1).iterator(), ImmutableSet.of(2).iterator());
}
ImmutableSet<Optional<Integer>> testOptionalFirstCollectionElement() {

View File

@@ -107,11 +107,21 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
Comparator.<String>reverseOrder().compare("baz", "qux"));
}
void testCollectionsSort() {
Collections.sort(ImmutableList.of("foo", "bar"), naturalOrder());
}
ImmutableSet<String> testCollectionsMin() {
return ImmutableSet.of(
Collections.min(ImmutableList.of("foo"), naturalOrder()),
Collections.max(ImmutableList.of("bar"), reverseOrder()));
}
String testMinOfArray() {
return Arrays.stream(new String[0]).min(naturalOrder()).orElseThrow();
}
String testMinOfCollection() {
String testCollectionsMinWithComparator() {
return ImmutableSet.of("foo", "bar").stream().min(naturalOrder()).orElseThrow();
}
@@ -143,11 +153,17 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
Collections.min(ImmutableSet.of("a", "b"), (a, b) -> 1));
}
ImmutableSet<String> testCollectionsMax() {
return ImmutableSet.of(
Collections.max(ImmutableList.of("foo"), naturalOrder()),
Collections.min(ImmutableList.of("bar"), reverseOrder()));
}
String testMaxOfArray() {
return Arrays.stream(new String[0]).max(naturalOrder()).orElseThrow();
}
String testMaxOfCollection() {
String testCollectionsMaxWithComparator() {
return ImmutableSet.of("foo", "bar").stream().max(naturalOrder()).orElseThrow();
}

View File

@@ -98,11 +98,20 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableSet.of("foo".compareTo("bar"), "qux".compareTo("baz"));
}
void testCollectionsSort() {
Collections.sort(ImmutableList.of("foo", "bar"));
}
ImmutableSet<String> testCollectionsMin() {
return ImmutableSet.of(
Collections.min(ImmutableList.of("foo")), Collections.min(ImmutableList.of("bar")));
}
String testMinOfArray() {
return Collections.min(Arrays.asList(new String[0]), naturalOrder());
}
String testMinOfCollection() {
String testCollectionsMinWithComparator() {
return Collections.min(ImmutableSet.of("foo", "bar"), naturalOrder());
}
@@ -134,11 +143,16 @@ final class ComparatorRulesTest implements RefasterRuleCollectionTestCase {
Comparators.min("a", "b", (a, b) -> 1));
}
ImmutableSet<String> testCollectionsMax() {
return ImmutableSet.of(
Collections.max(ImmutableList.of("foo")), Collections.max(ImmutableList.of("bar")));
}
String testMaxOfArray() {
return Collections.max(Arrays.asList(new String[0]), naturalOrder());
}
String testMaxOfCollection() {
String testCollectionsMaxWithComparator() {
return Collections.max(ImmutableSet.of("foo", "bar"), naturalOrder());
}

View File

@@ -0,0 +1,96 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import com.google.common.collect.BoundType;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import java.math.RoundingMode;
import java.util.EnumSet;
import java.util.stream.Stream;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class ImmutableEnumSetRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(EnumSet.class, toImmutableSet());
}
ImmutableSet<ImmutableSet<RoundingMode>> testSetsImmutableEnumSetIterable() {
return ImmutableSet.of(
ImmutableSet.copyOf(Iterables.cycle(RoundingMode.UP)),
ImmutableSet.copyOf(EnumSet.allOf(RoundingMode.class)));
}
ImmutableSet<RoundingMode> testSetsImmutableEnumSetArraysAsList() {
return ImmutableSet.copyOf(RoundingMode.values());
}
ImmutableSet<ImmutableSet<RoundingMode>> testSetsImmutableEnumSet1() {
return ImmutableSet.of(
ImmutableSet.of(RoundingMode.UP), ImmutableSet.copyOf(EnumSet.of(RoundingMode.UP)));
}
ImmutableSet<ImmutableSet<RoundingMode>> testSetsImmutableEnumSet2() {
return ImmutableSet.of(
ImmutableSet.of(RoundingMode.UP, RoundingMode.DOWN),
ImmutableSet.copyOf(EnumSet.of(RoundingMode.UP, RoundingMode.DOWN)));
}
ImmutableSet<ImmutableSet<RoundingMode>> testSetsImmutableEnumSet3() {
return ImmutableSet.of(
ImmutableSet.of(RoundingMode.UP, RoundingMode.DOWN, RoundingMode.CEILING),
ImmutableSet.copyOf(EnumSet.of(RoundingMode.UP, RoundingMode.DOWN, RoundingMode.CEILING)));
}
ImmutableSet<ImmutableSet<RoundingMode>> testSetsImmutableEnumSet4() {
return ImmutableSet.of(
ImmutableSet.of(
RoundingMode.UP, RoundingMode.DOWN, RoundingMode.CEILING, RoundingMode.FLOOR),
ImmutableSet.copyOf(
EnumSet.of(
RoundingMode.UP, RoundingMode.DOWN, RoundingMode.CEILING, RoundingMode.FLOOR)));
}
ImmutableSet<ImmutableSet<RoundingMode>> testSetsImmutableEnumSet5() {
return ImmutableSet.of(
ImmutableSet.of(
RoundingMode.UP,
RoundingMode.DOWN,
RoundingMode.CEILING,
RoundingMode.FLOOR,
RoundingMode.UNNECESSARY),
ImmutableSet.copyOf(
EnumSet.of(
RoundingMode.UP,
RoundingMode.DOWN,
RoundingMode.CEILING,
RoundingMode.FLOOR,
RoundingMode.UNNECESSARY)));
}
ImmutableSet<RoundingMode> testSetsImmutableEnumSet6() {
return ImmutableSet.of(
RoundingMode.UP,
RoundingMode.DOWN,
RoundingMode.CEILING,
RoundingMode.FLOOR,
RoundingMode.UNNECESSARY,
RoundingMode.HALF_EVEN);
}
ImmutableSet<RoundingMode> testSetsImmutableEnumSetVarArgs() {
return ImmutableSet.copyOf(
EnumSet.of(
RoundingMode.UP,
RoundingMode.DOWN,
RoundingMode.CEILING,
RoundingMode.FLOOR,
RoundingMode.UNNECESSARY,
RoundingMode.HALF_EVEN));
}
ImmutableSet<BoundType> testStreamToImmutableEnumSet() {
return Stream.of(BoundType.OPEN).collect(toImmutableSet());
}
}

View File

@@ -0,0 +1,96 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Sets.toImmutableEnumSet;
import com.google.common.collect.BoundType;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.stream.Stream;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class ImmutableEnumSetRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(EnumSet.class, toImmutableSet());
}
ImmutableSet<ImmutableSet<RoundingMode>> testSetsImmutableEnumSetIterable() {
return ImmutableSet.of(
Sets.immutableEnumSet(Iterables.cycle(RoundingMode.UP)),
Sets.immutableEnumSet(EnumSet.allOf(RoundingMode.class)));
}
ImmutableSet<RoundingMode> testSetsImmutableEnumSetArraysAsList() {
return Sets.immutableEnumSet(Arrays.asList(RoundingMode.values()));
}
ImmutableSet<ImmutableSet<RoundingMode>> testSetsImmutableEnumSet1() {
return ImmutableSet.of(
Sets.immutableEnumSet(RoundingMode.UP), Sets.immutableEnumSet(RoundingMode.UP));
}
ImmutableSet<ImmutableSet<RoundingMode>> testSetsImmutableEnumSet2() {
return ImmutableSet.of(
Sets.immutableEnumSet(RoundingMode.UP, RoundingMode.DOWN),
Sets.immutableEnumSet(RoundingMode.UP, RoundingMode.DOWN));
}
ImmutableSet<ImmutableSet<RoundingMode>> testSetsImmutableEnumSet3() {
return ImmutableSet.of(
Sets.immutableEnumSet(RoundingMode.UP, RoundingMode.DOWN, RoundingMode.CEILING),
Sets.immutableEnumSet(RoundingMode.UP, RoundingMode.DOWN, RoundingMode.CEILING));
}
ImmutableSet<ImmutableSet<RoundingMode>> testSetsImmutableEnumSet4() {
return ImmutableSet.of(
Sets.immutableEnumSet(
RoundingMode.UP, RoundingMode.DOWN, RoundingMode.CEILING, RoundingMode.FLOOR),
Sets.immutableEnumSet(
RoundingMode.UP, RoundingMode.DOWN, RoundingMode.CEILING, RoundingMode.FLOOR));
}
ImmutableSet<ImmutableSet<RoundingMode>> testSetsImmutableEnumSet5() {
return ImmutableSet.of(
Sets.immutableEnumSet(
RoundingMode.UP,
RoundingMode.DOWN,
RoundingMode.CEILING,
RoundingMode.FLOOR,
RoundingMode.UNNECESSARY),
Sets.immutableEnumSet(
RoundingMode.UP,
RoundingMode.DOWN,
RoundingMode.CEILING,
RoundingMode.FLOOR,
RoundingMode.UNNECESSARY));
}
ImmutableSet<RoundingMode> testSetsImmutableEnumSet6() {
return Sets.immutableEnumSet(
RoundingMode.UP,
RoundingMode.DOWN,
RoundingMode.CEILING,
RoundingMode.FLOOR,
RoundingMode.UNNECESSARY,
RoundingMode.HALF_EVEN);
}
ImmutableSet<RoundingMode> testSetsImmutableEnumSetVarArgs() {
return Sets.immutableEnumSet(
RoundingMode.UP,
RoundingMode.DOWN,
RoundingMode.CEILING,
RoundingMode.FLOOR,
RoundingMode.UNNECESSARY,
RoundingMode.HALF_EVEN);
}
ImmutableSet<BoundType> testStreamToImmutableEnumSet() {
return Stream.of(BoundType.OPEN).collect(toImmutableEnumSet());
}
}

View File

@@ -26,6 +26,7 @@ final class ImmutableMultisetRulesTest implements RefasterRuleCollectionTestCase
Stream.<Integer>empty().collect(toImmutableMultiset()));
}
@SuppressWarnings("unchecked")
ImmutableMultiset<ImmutableMultiset<Integer>> testIterableToImmutableMultiset() {
return ImmutableMultiset.of(
ImmutableList.of(1).stream().collect(toImmutableMultiset()),

View File

@@ -24,6 +24,7 @@ final class ImmutableMultisetRulesTest implements RefasterRuleCollectionTestCase
return ImmutableMultiset.of(ImmutableMultiset.of(), ImmutableMultiset.of());
}
@SuppressWarnings("unchecked")
ImmutableMultiset<ImmutableMultiset<Integer>> testIterableToImmutableMultiset() {
return ImmutableMultiset.of(
ImmutableMultiset.copyOf(ImmutableList.of(1)),

View File

@@ -37,6 +37,7 @@ final class ImmutableSortedMultisetRulesTest implements RefasterRuleCollectionTe
Stream.<Integer>empty().collect(toImmutableSortedMultiset(naturalOrder())));
}
@SuppressWarnings("unchecked")
ImmutableMultiset<ImmutableSortedMultiset<Integer>> testIterableToImmutableSortedMultiset() {
return ImmutableMultiset.of(
ImmutableSortedMultiset.copyOf(naturalOrder(), ImmutableList.of(1)),

View File

@@ -35,6 +35,7 @@ final class ImmutableSortedMultisetRulesTest implements RefasterRuleCollectionTe
return ImmutableMultiset.of(ImmutableSortedMultiset.of(), ImmutableSortedMultiset.of());
}
@SuppressWarnings("unchecked")
ImmutableMultiset<ImmutableSortedMultiset<Integer>> testIterableToImmutableSortedMultiset() {
return ImmutableMultiset.of(
ImmutableSortedMultiset.copyOf(ImmutableList.of(1)),

View File

@@ -0,0 +1,57 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import io.micrometer.core.instrument.Tag;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class MicrometerRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(ImmutableList.class);
}
ImmutableSet<Iterable<Tag>> testTagsOf1() {
return ImmutableSet.of(
ImmutableSet.of(Tag.of("foo", "v1")), ImmutableList.of(Tag.of("bar", "v2")));
}
ImmutableSet<Iterable<Tag>> testTagsOf2() {
return ImmutableSet.of(
ImmutableSet.of(Tag.of("foo", "v1"), Tag.of("bar", "v2")),
ImmutableList.of(Tag.of("baz", "v3"), Tag.of("qux", "v4")));
}
ImmutableSet<Iterable<Tag>> testTagsOf3() {
return ImmutableSet.of(
ImmutableSet.of(Tag.of("foo", "v1"), Tag.of("bar", "v2"), Tag.of("baz", "v3")),
ImmutableList.of(Tag.of("qux", "v4"), Tag.of("quux", "v5"), Tag.of("corge", "v6")));
}
ImmutableSet<Iterable<Tag>> testTagsOf4() {
return ImmutableSet.of(
ImmutableSet.of(
Tag.of("foo", "v1"), Tag.of("bar", "v2"), Tag.of("baz", "v3"), Tag.of("qux", "v4")),
ImmutableList.of(
Tag.of("quux", "v5"),
Tag.of("corge", "v6"),
Tag.of("grault", "v7"),
Tag.of("garply", "v8")));
}
ImmutableSet<Iterable<Tag>> testTagsOf5() {
return ImmutableSet.of(
ImmutableSet.of(
Tag.of("foo", "v1"),
Tag.of("bar", "v2"),
Tag.of("baz", "v3"),
Tag.of("qux", "v4"),
Tag.of("quux", "v5")),
ImmutableList.of(
Tag.of("corge", "v6"),
Tag.of("grault", "v7"),
Tag.of("garply", "v8"),
Tag.of("waldo", "v9"),
Tag.of("fred", "v10")));
}
}

View File

@@ -0,0 +1,56 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class MicrometerRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(ImmutableList.class);
}
ImmutableSet<Iterable<Tag>> testTagsOf1() {
return ImmutableSet.of(Tags.of(Tag.of("foo", "v1")), Tags.of(Tag.of("bar", "v2")));
}
ImmutableSet<Iterable<Tag>> testTagsOf2() {
return ImmutableSet.of(
Tags.of(Tag.of("foo", "v1"), Tag.of("bar", "v2")),
Tags.of(Tag.of("baz", "v3"), Tag.of("qux", "v4")));
}
ImmutableSet<Iterable<Tag>> testTagsOf3() {
return ImmutableSet.of(
Tags.of(Tag.of("foo", "v1"), Tag.of("bar", "v2"), Tag.of("baz", "v3")),
Tags.of(Tag.of("qux", "v4"), Tag.of("quux", "v5"), Tag.of("corge", "v6")));
}
ImmutableSet<Iterable<Tag>> testTagsOf4() {
return ImmutableSet.of(
Tags.of(Tag.of("foo", "v1"), Tag.of("bar", "v2"), Tag.of("baz", "v3"), Tag.of("qux", "v4")),
Tags.of(
Tag.of("quux", "v5"),
Tag.of("corge", "v6"),
Tag.of("grault", "v7"),
Tag.of("garply", "v8")));
}
ImmutableSet<Iterable<Tag>> testTagsOf5() {
return ImmutableSet.of(
Tags.of(
Tag.of("foo", "v1"),
Tag.of("bar", "v2"),
Tag.of("baz", "v3"),
Tag.of("qux", "v4"),
Tag.of("quux", "v5")),
Tags.of(
Tag.of("corge", "v6"),
Tag.of("grault", "v7"),
Tag.of("garply", "v8"),
Tag.of("waldo", "v9"),
Tag.of("fred", "v10")));
}
}

View File

@@ -83,11 +83,9 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
return Optional.of("foo").orElseGet(() -> Optional.of("bar").orElseThrow());
}
ImmutableSet<String> testOptionalOrElseGet() {
ImmutableSet<String> testOptionalOrElse() {
return ImmutableSet.of(
Optional.of("foo").orElse("bar"),
Optional.of("baz").orElse(toString()),
Optional.of("qux").orElse(String.valueOf(true)));
Optional.of("foo").orElseGet(() -> "bar"), Optional.of("baz").orElseGet(() -> toString()));
}
ImmutableSet<Object> testStreamFlatMapOptional() {

View File

@@ -80,11 +80,9 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
return Optional.of("foo").or(() -> Optional.of("bar")).orElseThrow();
}
ImmutableSet<String> testOptionalOrElseGet() {
ImmutableSet<String> testOptionalOrElse() {
return ImmutableSet.of(
Optional.of("foo").orElse("bar"),
Optional.of("baz").orElse(toString()),
Optional.of("qux").orElseGet(() -> String.valueOf(true)));
Optional.of("foo").orElse("bar"), Optional.of("baz").orElseGet(() -> toString()));
}
ImmutableSet<Object> testStreamFlatMapOptional() {

View File

@@ -9,6 +9,8 @@ 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.common.primitives.UnsignedInts;
import com.google.common.primitives.UnsignedLongs;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
@@ -22,7 +24,9 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
Floats.class,
Ints.class,
Longs.class,
Shorts.class);
Shorts.class,
UnsignedInts.class,
UnsignedLongs.class);
}
ImmutableSet<Boolean> testLessThan() {
@@ -105,34 +109,6 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
return Doubles.hashCode(1);
}
int testBooleanCompare() {
return Booleans.compare(false, true);
}
int testCharacterCompare() {
return Chars.compare('a', 'b');
}
int testShortCompare() {
return Shorts.compare((short) 1, (short) 2);
}
int testIntegerCompare() {
return Ints.compare(1, 2);
}
int testLongCompare() {
return Longs.compare(1, 2);
}
int testFloatCompare() {
return Floats.compare(1, 2);
}
int testDoubleCompare() {
return Doubles.compare(1, 2);
}
int testCharacterBytes() {
return Chars.BYTES;
}
@@ -190,4 +166,60 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableSet.of(
Long.signum(1L) < 0, Long.signum(2L) <= -1, Long.signum(3L) >= 0, Long.signum(4L) > -1);
}
int testIntegerCompareUnsigned() {
return UnsignedInts.compare(1, 2);
}
long testLongCompareUnsigned() {
return UnsignedLongs.compare(1, 2);
}
int testIntegerDivideUnsigned() {
return UnsignedInts.divide(1, 2);
}
long testLongDivideUnsigned() {
return UnsignedLongs.divide(1, 2);
}
int testIntegerRemainderUnsigned() {
return UnsignedInts.remainder(1, 2);
}
long testLongRemainderUnsigned() {
return UnsignedLongs.remainder(1, 2);
}
ImmutableSet<Integer> testIntegerParseUnsignedInt() {
return ImmutableSet.of(UnsignedInts.parseUnsignedInt("1"), Integer.parseUnsignedInt("2", 10));
}
ImmutableSet<Long> testLongParseUnsignedLong() {
return ImmutableSet.of(UnsignedLongs.parseUnsignedLong("1"), Long.parseUnsignedLong("2", 10));
}
int testIntegerParseUnsignedIntWithRadix() {
return UnsignedInts.parseUnsignedInt("1", 2);
}
long testLongParseUnsignedLongWithRadix() {
return UnsignedLongs.parseUnsignedLong("1", 2);
}
ImmutableSet<String> testIntegerToUnsignedString() {
return ImmutableSet.of(UnsignedInts.toString(1), Integer.toUnsignedString(2, 10));
}
ImmutableSet<String> testLongToUnsignedString() {
return ImmutableSet.of(UnsignedLongs.toString(1), Long.toUnsignedString(2, 10));
}
String testIntegerToUnsignedStringWithRadix() {
return UnsignedInts.toString(1, 2);
}
String testLongToUnsignedStringWithRadix() {
return UnsignedLongs.toString(1, 2);
}
}

View File

@@ -9,6 +9,8 @@ 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.common.primitives.UnsignedInts;
import com.google.common.primitives.UnsignedLongs;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
@@ -22,7 +24,9 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
Floats.class,
Ints.class,
Longs.class,
Shorts.class);
Shorts.class,
UnsignedInts.class,
UnsignedLongs.class);
}
ImmutableSet<Boolean> testLessThan() {
@@ -105,34 +109,6 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
return Double.hashCode(1);
}
int testBooleanCompare() {
return Boolean.compare(false, true);
}
int testCharacterCompare() {
return Character.compare('a', 'b');
}
int testShortCompare() {
return Short.compare((short) 1, (short) 2);
}
int testIntegerCompare() {
return Integer.compare(1, 2);
}
int testLongCompare() {
return Long.compare(1, 2);
}
int testFloatCompare() {
return Float.compare(1, 2);
}
int testDoubleCompare() {
return Double.compare(1, 2);
}
int testCharacterBytes() {
return Character.BYTES;
}
@@ -190,4 +166,60 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableSet.of(
Long.signum(1L) == -1, Long.signum(2L) == -1, Long.signum(3L) != -1, Long.signum(4L) != -1);
}
int testIntegerCompareUnsigned() {
return Integer.compareUnsigned(1, 2);
}
long testLongCompareUnsigned() {
return Long.compareUnsigned(1, 2);
}
int testIntegerDivideUnsigned() {
return Integer.divideUnsigned(1, 2);
}
long testLongDivideUnsigned() {
return Long.divideUnsigned(1, 2);
}
int testIntegerRemainderUnsigned() {
return Integer.remainderUnsigned(1, 2);
}
long testLongRemainderUnsigned() {
return Long.remainderUnsigned(1, 2);
}
ImmutableSet<Integer> testIntegerParseUnsignedInt() {
return ImmutableSet.of(Integer.parseUnsignedInt("1"), Integer.parseUnsignedInt("2"));
}
ImmutableSet<Long> testLongParseUnsignedLong() {
return ImmutableSet.of(Long.parseUnsignedLong("1"), Long.parseUnsignedLong("2"));
}
int testIntegerParseUnsignedIntWithRadix() {
return Integer.parseUnsignedInt("1", 2);
}
long testLongParseUnsignedLongWithRadix() {
return Long.parseUnsignedLong("1", 2);
}
ImmutableSet<String> testIntegerToUnsignedString() {
return ImmutableSet.of(Integer.toUnsignedString(1), Integer.toUnsignedString(2));
}
ImmutableSet<String> testLongToUnsignedString() {
return ImmutableSet.of(Long.toUnsignedString(1), Long.toUnsignedString(2));
}
String testIntegerToUnsignedStringWithRadix() {
return Integer.toUnsignedString(1, 2);
}
String testLongToUnsignedStringWithRadix() {
return Long.toUnsignedString(1, 2);
}
}

View File

@@ -10,6 +10,7 @@ import static java.util.stream.Collectors.minBy;
import static java.util.stream.Collectors.toCollection;
import static org.assertj.core.api.Assertions.assertThat;
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -664,6 +665,10 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return Mono.fromFuture(CompletableFuture.completedFuture(null), true);
}
Mono<String> testMonoFromFutureAsyncLoadingCacheGet() {
return Mono.fromFuture(() -> ((AsyncLoadingCache<Integer, String>) null).get(0));
}
Flux<Integer> testFluxFromStreamSupplier() {
return Flux.fromStream(Stream.of(1));
}

View File

@@ -12,6 +12,7 @@ import static java.util.stream.Collectors.toCollection;
import static org.assertj.core.api.Assertions.assertThat;
import static reactor.function.TupleUtils.function;
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -644,6 +645,10 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return Mono.fromFuture(() -> CompletableFuture.completedFuture(null), true);
}
Mono<String> testMonoFromFutureAsyncLoadingCacheGet() {
return Mono.fromFuture(() -> ((AsyncLoadingCache<Integer, String>) null).get(0), true);
}
Flux<Integer> testFluxFromStreamSupplier() {
return Flux.fromStream(() -> Stream.of(1));
}

View File

@@ -96,4 +96,32 @@ final class StringRulesTest implements RefasterRuleCollectionTestCase {
int testUtf8EncodedLength() {
return "foo".getBytes(UTF_8).length;
}
int testStringIndexOfChar() {
return "foo".substring(1).indexOf('a');
}
int testStringIndexOfString() {
return "foo".substring(1).indexOf("bar");
}
int testStringLastIndexOfChar() {
return "foo".substring(1).lastIndexOf('a');
}
int testStringLastIndexOfString() {
return "foo".substring(1).lastIndexOf("bar");
}
int testStringLastIndexOfCharWithIndex() {
return "foo".substring(0, 2).lastIndexOf('a');
}
int testStringLastIndexOfStringWithIndex() {
return "foo".substring(0, 2).lastIndexOf("bar");
}
boolean testStringStartsWith() {
return "foo".substring(1).startsWith("bar");
}
}

View File

@@ -96,4 +96,32 @@ final class StringRulesTest implements RefasterRuleCollectionTestCase {
int testUtf8EncodedLength() {
return Utf8.encodedLength("foo");
}
int testStringIndexOfChar() {
return Math.max(-1, "foo".indexOf('a', 1) - 1);
}
int testStringIndexOfString() {
return Math.max(-1, "foo".indexOf("bar", 1) - 1);
}
int testStringLastIndexOfChar() {
return Math.max(-1, "foo".lastIndexOf('a') - 1);
}
int testStringLastIndexOfString() {
return Math.max(-1, "foo".lastIndexOf("bar") - 1);
}
int testStringLastIndexOfCharWithIndex() {
return "foo".lastIndexOf('a', 2 - 1);
}
int testStringLastIndexOfStringWithIndex() {
return "foo".lastIndexOf("bar", 2 - 1);
}
boolean testStringStartsWith() {
return "foo".startsWith("bar", 1);
}
}

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.18.1-SNAPSHOT</version>
<version>0.19.2-SNAPSHOT</version>
</parent>
<artifactId>error-prone-experimental</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.18.1-SNAPSHOT</version>
<version>0.19.2-SNAPSHOT</version>
</parent>
<artifactId>error-prone-guidelines</artifactId>
@@ -118,9 +118,6 @@
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs combine.children="append">
<arg>-Xplugin:DocumentationGenerator -XoutputDirectory=${project.build.directory}/docs</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.18.1-SNAPSHOT</version>
<version>0.19.2-SNAPSHOT</version>
</parent>
<artifactId>error-prone-utils</artifactId>

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.utils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.ErrorProneFlags;
/** Helper methods for working with {@link ErrorProneFlags}. */
@@ -19,4 +20,19 @@ public final class Flags {
ImmutableList<String> list = errorProneFlags.getListOrEmpty(name);
return list.equals(ImmutableList.of("")) ? ImmutableList.of() : list;
}
/**
* Returns the set of (comma-separated) arguments passed using the given Error Prone flag.
*
* @param errorProneFlags The full set of flags provided.
* @param name The name of the flag of interest.
* @return A non-{@code null} set of provided arguments; this set is empty if the flag was not
* provided, or if the flag's value is the empty string.
* @implNote This method does not delegate to {@link ErrorProneFlags#getSetOrEmpty(String)}, as
* that method wouldn't allow us to identify a non-singleton set of empty strings; such a set
* should not be treated as empty.
*/
public static ImmutableSet<String> getSet(ErrorProneFlags errorProneFlags, String name) {
return ImmutableSet.copyOf(getList(errorProneFlags, name));
}
}

View File

@@ -78,4 +78,16 @@ public final class MoreASTHelpers {
public static boolean areSameType(Tree treeA, Tree treeB, VisitorState state) {
return ASTHelpers.isSameType(ASTHelpers.getType(treeA), ASTHelpers.getType(treeB), state);
}
/**
* Tells whether the given tree is of type {@link String}.
*
* @param tree The tree of interest.
* @param state The {@link VisitorState} describing the context in which the given tree was found.
* @return Whether the specified tree has the same type as {@link
* com.sun.tools.javac.code.Symtab#stringType}.
*/
public static boolean isStringTyped(Tree tree, VisitorState state) {
return ASTHelpers.isSameType(ASTHelpers.getType(tree), state.getSymtab().stringType, state);
}
}

View File

@@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.ErrorProneOptions;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
@@ -11,21 +12,29 @@ import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
final class FlagsTest {
private static Stream<Arguments> getListTestCases() {
/* { args, flag, expected } */
private static Stream<Arguments> getCollectionTestCases() {
/* { args, flag, listed } */
return Stream.of(
arguments(ImmutableList.of(), "Foo", ImmutableList.of()),
arguments(ImmutableList.of("-XepOpt:Foo=bar,baz"), "Qux", ImmutableList.of()),
arguments(ImmutableList.of("-XepOpt:Foo="), "Foo", ImmutableList.of()),
arguments(ImmutableList.of("-XepOpt:Foo=bar"), "Foo", ImmutableList.of("bar")),
arguments(ImmutableList.of("-XepOpt:Foo=bar,bar"), "Foo", ImmutableList.of("bar", "bar")),
arguments(ImmutableList.of("-XepOpt:Foo=bar,baz"), "Foo", ImmutableList.of("bar", "baz")),
arguments(ImmutableList.of("-XepOpt:Foo=,"), "Foo", ImmutableList.of("", "")));
}
@MethodSource("getListTestCases")
@MethodSource("getCollectionTestCases")
@ParameterizedTest
void getList(ImmutableList<String> args, String flag, ImmutableList<String> expected) {
void getList(ImmutableList<String> args, String flag, ImmutableList<String> listed) {
assertThat(Flags.getList(ErrorProneOptions.processArgs(args).getFlags(), flag))
.containsExactlyElementsOf(expected);
.containsExactlyElementsOf(listed);
}
@MethodSource("getCollectionTestCases")
@ParameterizedTest
void getSet(ImmutableList<String> args, String flag, ImmutableList<String> listed) {
assertThat(Flags.getSet(ErrorProneOptions.processArgs(args).getFlags(), flag))
.containsExactlyElementsOf(ImmutableSet.copyOf(listed));
}
}

View File

@@ -9,10 +9,13 @@ import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.ExpressionStatementTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.ReturnTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.VariableTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.sun.source.tree.ExpressionStatementTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ReturnTree;
import com.sun.source.tree.Tree;
@@ -137,6 +140,25 @@ final class MoreASTHelpersTest {
.doTest();
}
@Test
void isStringTyped() {
CompilationTestHelper.newInstance(IsStringTypedTestChecker.class, getClass())
.addSourceLines(
"A.java",
"class A {",
" void m() {",
" int foo = 1;",
" // BUG: Diagnostic contains:",
" String s = \"foo\";",
"",
" hashCode();",
" // BUG: Diagnostic contains:",
" toString();",
" }",
"}")
.doTest();
}
private static String createMethodSearchDiagnosticsMessage(
BiFunction<String, VisitorState, Object> valueFunction, VisitorState state) {
return Maps.toMap(ImmutableSet.of("foo", "bar", "baz"), key -> valueFunction.apply(key, state))
@@ -224,4 +246,28 @@ final class MoreASTHelpersTest {
: Description.NO_MATCH;
}
}
/**
* A {@link BugChecker} that delegates to {@link MoreASTHelpers#isStringTyped(Tree,
* VisitorState)}.
*/
@BugPattern(summary = "Interacts with `MoreASTHelpers` for testing purposes", severity = ERROR)
public static final class IsStringTypedTestChecker extends BugChecker
implements MethodInvocationTreeMatcher, VariableTreeMatcher {
private static final long serialVersionUID = 1L;
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
return getDescription(tree, state);
}
@Override
public Description matchVariable(VariableTree tree, VisitorState state) {
return getDescription(tree, state);
}
private Description getDescription(Tree tree, VisitorState state) {
return MoreASTHelpers.isStringTyped(tree, state) ? describeMatch(tree) : Description.NO_MATCH;
}
}
}

View File

@@ -73,7 +73,7 @@ final class ThirdPartyLibraryTest {
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
@ValueSource(booleans = {false, true})
void isIntroductionAllowedIgnoreClasspathCompat(boolean ignoreClassPath) {
CompilationTestHelper.newInstance(IsIntroductionAllowedTestChecker.class, getClass())
.setArgs("-XepOpt:ErrorProneSupport:IgnoreClasspathCompat=" + ignoreClassPath)

View File

@@ -9,8 +9,8 @@ revision='checkstyle-10.14.0'
# XXX: Configure Renovate to manage the AssertJ version declared here.
additional_build_flags='-Dassertj.version=3.24.2'
additional_source_directories='${project.basedir}${file.separator}src${file.separator}it${file.separator}java,${project.basedir}${file.separator}src${file.separator}xdocs-examples${file.separator}java'
patch_error_prone_flags=''
validation_error_prone_flags=''
patch_error_prone_flags='-XepOpt:Refaster:NamePattern=.*Workshop.*"'
validation_error_prone_flags='-XepOpt:Refaster:NamePattern=.*Workshop.*"'
# Validation skips some tests:
# - The `metadataFilesGenerationAllFiles` test is skipped because it makes line
# number assertions that will fail when the code is formatted or patched.

308
mvnw vendored Executable file
View File

@@ -0,0 +1,308 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.2.0
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "$(uname)" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
else
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=$(java-config --jre-home)
fi
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="$(which javac)"
if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=$(which readlink)
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
if $darwin ; then
javaHome="$(dirname "\"$javaExecutable\"")"
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
else
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
fi
javaHome="$(dirname "\"$javaExecutable\"")"
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=$(cd "$wdir/.." || exit 1; pwd)
fi
# end of workaround
done
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
# Remove \r in case we run on Windows within Git Bash
# and check out the repository with auto CRLF management
# enabled. Otherwise, we may read lines that are delimited with
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
# splitting rules.
tr -s '\r\n' ' ' < "$1"
fi
}
log() {
if [ "$MVNW_VERBOSE" = true ]; then
printf '%s\n' "$1"
fi
}
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
log "$MAVEN_PROJECTBASEDIR"
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
if [ -r "$wrapperJarPath" ]; then
log "Found $wrapperJarPath"
else
log "Couldn't find $wrapperJarPath, downloading it ..."
if [ -n "$MVNW_REPOURL" ]; then
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
else
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
fi
while IFS="=" read -r key value; do
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
safeValue=$(echo "$value" | tr -d '\r')
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
log "Downloading from: $wrapperUrl"
if $cygwin; then
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
fi
if command -v wget > /dev/null; then
log "Found wget ... using wget"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
log "Found curl ... using curl"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
else
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
fi
else
log "Falling back to using Java to download"
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaSource=$(cygpath --path --windows "$javaSource")
javaClass=$(cygpath --path --windows "$javaClass")
fi
if [ -e "$javaSource" ]; then
if [ ! -e "$javaClass" ]; then
log " - Compiling MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/javac" "$javaSource")
fi
if [ -e "$javaClass" ]; then
log " - Running MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
wrapperSha256Sum=""
while IFS="=" read -r key value; do
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
if [ -n "$wrapperSha256Sum" ]; then
wrapperSha256Result=false
if command -v sha256sum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
elif command -v shasum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
exit 1
fi
if [ $wrapperSha256Result = false ]; then
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
exit 1
fi
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
# shellcheck disable=SC2086 # safe args
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

205
mvnw.cmd vendored Normal file
View File

@@ -0,0 +1,205 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.2.0
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %WRAPPER_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
SET WRAPPER_SHA_256_SUM=""
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
)
IF NOT %WRAPPER_SHA_256_SUM%=="" (
powershell -Command "&{"^
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
" exit 1;"^
"}"^
"}"
if ERRORLEVEL 1 goto error
)
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%

208
pom.xml
View File

@@ -4,7 +4,7 @@
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.18.1-SNAPSHOT</version>
<version>0.19.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Picnic :: Error Prone Support</name>
@@ -48,6 +48,7 @@
<module>refaster-runner</module>
<module>refaster-support</module>
<module>refaster-test-support</module>
<module>workshop</module>
</modules>
<scm child.scm.developerConnection.inherit.append.path="false" child.scm.url.inherit.append.path="false">
@@ -111,6 +112,9 @@
but also prevents excessive memory usage by heavily parallelized
local builds. -->
-Xmx${argLine.xmx}
<!-- Configure the Byte Buddy Java agent used by Mockito to create
mocks. -->
-javaagent:${org.mockito:mockito-core:jar}
<!-- This argument cannot be set through Surefire's
'systemPropertyVariables' configuration setting. Setting the file
encoding is necessary because forked unit test invocations
@@ -148,7 +152,7 @@
<groupId.error-prone>com.google.errorprone</groupId.error-prone>
<!-- The build timestamp is derived from the most recent commit
timestamp in support of reproducible builds. -->
<project.build.outputTimestamp>2024-08-11T13:05:54Z</project.build.outputTimestamp>
<project.build.outputTimestamp>2024-11-03T15:58:19Z</project.build.outputTimestamp>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Glob pattern identifying Refaster rule definition files. These
Java classes don't contain "regular" code, and thus require special
@@ -208,16 +212,16 @@
<version.auto-value>1.11.0</version.auto-value>
<version.error-prone>${version.error-prone-orig}</version.error-prone>
<version.error-prone-fork>v${version.error-prone-orig}-picnic-1</version.error-prone-fork>
<version.error-prone-orig>2.30.0</version.error-prone-orig>
<version.error-prone-slf4j>0.1.25</version.error-prone-slf4j>
<version.error-prone-orig>2.35.1</version.error-prone-orig>
<version.error-prone-slf4j>0.1.28</version.error-prone-slf4j>
<version.guava-beta-checker>1.0</version.guava-beta-checker>
<version.jdk>17</version.jdk>
<version.maven>3.9.8</version.maven>
<version.mockito>5.12.0</version.mockito>
<version.maven>3.9.9</version.maven>
<version.mockito>5.14.2</version.mockito>
<version.nopen-checker>1.0.1</version.nopen-checker>
<version.nullaway>0.11.2</version.nullaway>
<version.nullaway>0.12.1</version.nullaway>
<version.pitest-git>1.1.4</version.pitest-git>
<version.rewrite-templating>1.13.0</version.rewrite-templating>
<version.rewrite-templating>1.16.3</version.rewrite-templating>
<version.surefire>3.2.3</version.surefire>
</properties>
@@ -296,10 +300,15 @@
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.17.2</version>
<version>2.18.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>
<dependency>
<groupId>com.google.auto</groupId>
<artifactId>auto-common</artifactId>
@@ -328,7 +337,7 @@
<dependency>
<groupId>com.google.googlejavaformat</groupId>
<artifactId>google-java-format</artifactId>
<version>1.23.0</version>
<version>1.24.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
@@ -338,7 +347,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-bom</artifactId>
<version>33.2.1-jre</version>
<version>33.3.1-jre</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -357,10 +366,15 @@
<artifactId>nullaway</artifactId>
<version>${version.nullaway}</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>1.13.6</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-bom</artifactId>
<version>2023.0.9</version>
<version>2023.0.11</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -377,7 +391,7 @@
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.2.22</version>
<version>2.2.25</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
@@ -412,15 +426,7 @@
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.14.19</version>
</dependency>
<!-- Specified so that Renovate will file Maven upgrade PRs, which
subsequently will cause `maven-enforcer-plugin` to require that
developers build the project using the latest Maven release. -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>${version.maven}</version>
<version>1.15.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
@@ -437,7 +443,7 @@
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>3.46.0</version>
<version>3.48.2</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
@@ -457,7 +463,7 @@
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.11.0</version>
<version>5.11.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -471,7 +477,14 @@
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-core</artifactId>
<version>5.1.3</version>
<version>5.2.0</version>
</dependency>
<!-- XXX: Drop this `rewrite-java-17` version declaration once
`rewrite-recipe-bom` pulls in version 8.39.1 or greater. -->
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-java-17</artifactId>
<version>8.38.1</version>
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
@@ -481,7 +494,7 @@
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-recipe-bom</artifactId>
<version>2.16.0</version>
<version>2.21.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -495,19 +508,19 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>6.1.11</version>
<version>6.1.14</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>3.3.2</version>
<version>3.3.5</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-bom</artifactId>
<version>6.3.1</version>
<version>6.3.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -518,6 +531,17 @@
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- To avoid repetition of fairly complex configuration, Surefire
is configured to always load Mockito's Java agent. Declaring the
associated dependency here avoids additional repetition, and prevents
a slightly obscure error that happens if the dependency is absent. -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
@@ -562,7 +586,7 @@
<plugin>
<groupId>com.spotify.fmt</groupId>
<artifactId>fmt-maven-plugin</artifactId>
<version>2.22.1</version>
<version>2.25</version>
<configuration>
<additionalSourceDirectories>
<additionalSourceDirectory>${basedir}/src/test/resources</additionalSourceDirectory>
@@ -582,7 +606,7 @@
<plugin>
<groupId>de.thetaphi</groupId>
<artifactId>forbiddenapis</artifactId>
<version>3.7</version>
<version>3.8</version>
<configuration>
<bundledSignatures>
<bundledSignature>jdk-internal</bundledSignature>
@@ -648,7 +672,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.4.0</version>
<version>3.6.0</version>
<configuration>
<checkstyleRules>
<!-- We only enable rules that are not enforced by
@@ -897,7 +921,7 @@
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>10.17.0</version>
<version>10.20.0</version>
</dependency>
<dependency>
<groupId>io.spring.nohttp</groupId>
@@ -1001,7 +1025,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.7.1</version>
<version>3.8.1</version>
<configuration>
<!-- XXX: Drop `ignoreAllNonTestScoped` once
https://issues.apache.org/jira/browse/MNG-6058 is
@@ -1012,11 +1036,19 @@
<ignoreDirect>false</ignoreDirect>
<ignoreNonCompile>true</ignoreNonCompile>
</configuration>
<executions>
<execution>
<id>set-additional-properties</id>
<goals>
<goal>properties</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.2</version>
<version>3.1.3</version>
<configuration>
<retryFailedDeploymentCount>3</retryFailedDeploymentCount>
</configuration>
@@ -1082,9 +1114,6 @@
<requireMatchingCoordinates>
<moduleNameMustMatchArtifactId>true</moduleNameMustMatchArtifactId>
</requireMatchingCoordinates>
<requireMavenVersion>
<version>${version.maven}</version>
</requireMavenVersion>
<requireNoRepositories />
<requirePluginVersions />
<requireUpperBoundDeps />
@@ -1094,7 +1123,7 @@
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>extra-enforcer-rules</artifactId>
<version>1.8.0</version>
<version>1.9.0</version>
</dependency>
</dependencies>
<executions>
@@ -1109,7 +1138,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.2.5</version>
<version>3.2.7</version>
<executions>
<execution>
<id>sign-artifacts</id>
@@ -1122,7 +1151,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>3.1.2</version>
<version>3.1.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -1188,7 +1217,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.8.0</version>
<version>3.10.1</version>
<configuration>
<additionalJOptions>
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</additionalJOption>
@@ -1239,7 +1268,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.12.1</version>
<version>3.21.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -1258,7 +1287,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.3.1</version>
<version>3.5.2</version>
<configuration>
<includes>
<include>**/*Test.java</include>
@@ -1423,37 +1452,6 @@
<updateBuildOutputTimestampPolicy>never</updateBuildOutputTimestampPolicy>
</configuration>
</plugin>
<plugin>
<groupId>org.gaul</groupId>
<artifactId>modernizer-maven-plugin</artifactId>
<version>2.9.0</version>
<configuration>
<exclusionPatterns>
<!-- The plugin suggests replacing usages of
Guava's `Iterables` class with
`java.util.stream.Stream` equivalents, but the
alternative is often more verbose, requiring
unnecessary conversions to and from streams. -->
<exclusionPattern>com/google/common/collect/Iterables\..*</exclusionPattern>
</exclusionPatterns>
<failOnViolations>false</failOnViolations>
<ignorePackages>
<!-- Some Refaster rules purposefully use outdated
patterns in their `@BeforeTemplate` methods. -->
<ignorePackage>tech.picnic.errorprone.refasterrules</ignorePackage>
</ignorePackages>
<javaVersion>${version.jdk}</javaVersion>
</configuration>
<executions>
<execution>
<id>run-modernizer</id>
<goals>
<goal>modernizer</goal>
</goals>
<phase>process-test-classes</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
@@ -1466,28 +1464,10 @@
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.kordamp.maven</groupId>
<artifactId>pomchecker-maven-plugin</artifactId>
<version>1.11.0</version>
<configuration>
<failOnError>false</failOnError>
<release>false</release>
</configuration>
<executions>
<execution>
<id>check-maven-central-compliance</id>
<goals>
<goal>check-maven-central</goal>
</goals>
<phase>verify</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.pitest</groupId>
<artifactId>pitest-maven</artifactId>
<version>1.16.1</version>
<version>1.17.0</version>
<configuration>
<excludedClasses>
<!-- AutoValue generated classes. -->
@@ -1550,6 +1530,10 @@
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
@@ -1834,14 +1818,6 @@
<groupId>org.codehaus.mojo</groupId>
<artifactId>tidy-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.gaul</groupId>
<artifactId>modernizer-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.kordamp.maven</groupId>
<artifactId>pomchecker-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
@@ -1929,6 +1905,12 @@
<!-- XXX: Enable this once we open-source
this library. -->
-Xep:BetaApi:OFF
<!-- This check flags bad member names, but
also type names that we may want to accept.
Consider contributing a flag to toggle this
behavior. See
https://github.com/google/error-prone/issues/4616 -->
-Xep:IdentifierName:OFF
<!-- We don't target JDK 7. -->
-Xep:Java7ApiChecker:OFF
<!-- We don't target JDK 8. -->
@@ -2062,21 +2044,6 @@
<failOnMissing>true</failOnMissing>
</configuration>
</plugin>
<plugin>
<groupId>org.gaul</groupId>
<artifactId>modernizer-maven-plugin</artifactId>
<configuration>
<failOnViolations>true</failOnViolations>
</configuration>
</plugin>
<plugin>
<groupId>org.kordamp.maven</groupId>
<artifactId>pomchecker-maven-plugin</artifactId>
<configuration>
<failOnError>true</failOnError>
<failOnWarning>true</failOnWarning>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
@@ -2148,13 +2115,6 @@
<artifactId>maven-gpg-plugin</artifactId>
</plugin>
<?SORTPOM RESUME?>
<plugin>
<groupId>org.kordamp.maven</groupId>
<artifactId>pomchecker-maven-plugin</artifactId>
<configuration>
<release>true</release>
</configuration>
</plugin>
</plugins>
</build>
</profile>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.18.1-SNAPSHOT</version>
<version>0.19.2-SNAPSHOT</version>
</parent>
<artifactId>refaster-compiler</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.18.1-SNAPSHOT</version>
<version>0.19.2-SNAPSHOT</version>
</parent>
<artifactId>refaster-runner</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.18.1-SNAPSHOT</version>
<version>0.19.2-SNAPSHOT</version>
</parent>
<artifactId>refaster-support</artifactId>
@@ -88,10 +88,5 @@
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -2,6 +2,7 @@ package tech.picnic.errorprone.refaster.matchers;
import com.google.errorprone.VisitorState;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ArrayAccessTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
@@ -9,34 +10,19 @@ import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberReferenceTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.tree.UnaryTree;
/** A matcher of expressions that likely require little to no computation. */
public final class IsLikelyTrivialComputation implements Matcher<ExpressionTree> {
/** A matcher of expressions that may a non-trivial amount of computation. */
public final class RequiresComputation implements Matcher<ExpressionTree> {
private static final long serialVersionUID = 1L;
/** Instantiates a new {@link IsLikelyTrivialComputation} instance. */
public IsLikelyTrivialComputation() {}
/** Instantiates a new {@link RequiresComputation} instance. */
public RequiresComputation() {}
@Override
public boolean matches(ExpressionTree expressionTree, VisitorState state) {
if (expressionTree instanceof MethodInvocationTree methodInvocation) {
// XXX: Method invocations are generally *not* trivial computations, but we make an exception
// for nullary method invocations on the result of a trivial computation. This exception
// allows this `Matcher` to by the `OptionalOrElseGet` Refaster rule, such that it does not
// suggest the introduction of lambda expressions that are better expressed as method
// references. Once the `MethodReferenceUsage` bug checker is production-ready, this exception
// should be removed. (But at that point, instead defining a `RequiresComputation` matcher may
// be more appropriate.)
if (methodInvocation.getArguments().isEmpty()
&& matches(methodInvocation.getMethodSelect())) {
return true;
}
}
return matches(expressionTree);
}
@@ -44,11 +30,11 @@ public final class IsLikelyTrivialComputation implements Matcher<ExpressionTree>
// Depending on feedback such trees may be matched in the future.
private static boolean matches(ExpressionTree expressionTree) {
if (expressionTree instanceof ArrayAccessTree arrayAccess) {
return matches(arrayAccess.getExpression()) && matches(arrayAccess.getIndex());
return matches(arrayAccess.getExpression()) || matches(arrayAccess.getIndex());
}
if (expressionTree instanceof LiteralTree) {
return true;
return false;
}
if (expressionTree instanceof LambdaExpressionTree) {
@@ -56,11 +42,14 @@ public final class IsLikelyTrivialComputation implements Matcher<ExpressionTree>
* Lambda expressions encapsulate computations, but their definition does not involve
* significant computation.
*/
return true;
return false;
}
if (expressionTree instanceof IdentifierTree) {
return true;
// XXX: Generally identifiers don't by themselves represent a computation, though they may be
// a stand-in for one if they are a Refaster template method argument. Can we identify such
// cases, also when the `Matcher` is invoked by Refaster?
return false;
}
if (expressionTree instanceof MemberReferenceTree memberReference) {
@@ -80,11 +69,11 @@ public final class IsLikelyTrivialComputation implements Matcher<ExpressionTree>
}
if (expressionTree instanceof UnaryTree unary) {
// XXX: Arguably side-effectful options such as pre- and post-increment and -decrement are not
// trivial.
// XXX: Arguably side-effectful options such as pre- and post-increment and -decrement
// represent non-trivial computations.
return matches(unary.getExpression());
}
return false;
return ASTHelpers.constValue(expressionTree) == null;
}
}

View File

@@ -8,54 +8,69 @@ import com.google.errorprone.bugpatterns.BugChecker;
import com.sun.source.tree.ReturnTree;
import org.junit.jupiter.api.Test;
final class IsLikelyTrivialComputationTest {
final class RequiresComputationTest {
@Test
void matches() {
CompilationTestHelper.newInstance(MatcherTestChecker.class, getClass())
.addSourceLines(
"A.java",
"import java.io.OutputStream;",
"import java.util.Comparator;",
"import java.util.function.Predicate;",
"",
"class A {",
" String negative1() {",
" return String.valueOf(1);",
" int negative1() {",
" int[] arr = new int[0];",
" return arr[0];",
" }",
"",
" String negative2() {",
" return toString().toString();",
" return null;",
" }",
"",
" String negative3() {",
" return \"foo\" + toString();",
" boolean negative3() {",
" return false;",
" }",
"",
" byte negative4() {",
" return \"foo\".getBytes()[0];",
" int negative4() {",
" return 0;",
" }",
"",
" int negative5() {",
" int[] arr = new int[0];",
" return arr[hashCode()];",
" String negative5() {",
" return \"foo\" + \"bar\";",
" }",
"",
" int negative6() {",
" return 1 * 2;",
" Predicate<String> negative6() {",
" return v -> \"foo\".equals(v);",
" }",
"",
" Predicate<String> negative7() {",
" return toString()::equals;",
" A negative7() {",
" return this;",
" }",
"",
" String negative8() {",
" return (toString());",
" Predicate<String> negative8() {",
" return \"foo\"::equals;",
" }",
"",
" Object negative9() {",
" return (Object) toString();",
" OutputStream negative9() {",
" return System.out;",
" }",
"",
" int negative10() {",
" return -hashCode();",
" A negative10() {",
" return (this);",
" }",
"",
" Object negative11() {",
" return (Object) this;",
" }",
"",
" boolean negative12() {",
" boolean[] arr = new boolean[0];",
" return !arr[0];",
" }",
"",
" String negative13() {",
" return \"foo\" + 0;",
" }",
"",
" String positive1() {",
@@ -68,68 +83,63 @@ final class IsLikelyTrivialComputationTest {
" return this.toString();",
" }",
"",
" int positive3() {",
" int[] arr = new int[0];",
" String positive3() {",
" // BUG: Diagnostic contains:",
" return arr[0];",
" return String.valueOf(1);",
" }",
"",
" String positive4() {",
" // BUG: Diagnostic contains:",
" return null;",
" return toString().toString();",
" }",
"",
" boolean positive5() {",
" String positive5() {",
" // BUG: Diagnostic contains:",
" return false;",
" return \"foo\" + toString();",
" }",
"",
" int positive6() {",
" byte positive6() {",
" // BUG: Diagnostic contains:",
" return 0;",
" return \"foo\".getBytes()[0];",
" }",
"",
" String positive7() {",
" int positive7() {",
" int[] arr = new int[0];",
" // BUG: Diagnostic contains:",
" return \"foo\" + \"bar\";",
" return arr[hashCode()];",
" }",
"",
" Predicate<String> positive8() {",
" // BUG: Diagnostic contains:",
" return v -> \"foo\".equals(v);",
" return toString()::equals;",
" }",
"",
" A positive9() {",
" Comparator<String> positive9() {",
" // BUG: Diagnostic contains:",
" return this;",
" return toString().CASE_INSENSITIVE_ORDER;",
" }",
"",
" Predicate<String> positive10() {",
" String positive10() {",
" // BUG: Diagnostic contains:",
" return \"foo\"::equals;",
" return (toString());",
" }",
"",
" A positive11() {",
" Object positive11() {",
" // BUG: Diagnostic contains:",
" return (this);",
" return (Object) toString();",
" }",
"",
" Object positive12() {",
" int positive12() {",
" // BUG: Diagnostic contains:",
" return (Object) this;",
" }",
"",
" boolean positive13() {",
" // BUG: Diagnostic contains:",
" return !false;",
" return -hashCode();",
" }",
"}")
.doTest();
}
/** A {@link BugChecker} that simply delegates to {@link IsLikelyTrivialComputation}. */
/** A {@link BugChecker} that simply delegates to {@link RequiresComputation}. */
@BugPattern(
summary = "Flags return statement expressions matched by `IsLikelyTrivialComputation`",
summary = "Flags return statement expressions matched by `RequiresComputation`",
severity = ERROR)
public static final class MatcherTestChecker extends AbstractMatcherTestChecker {
private static final long serialVersionUID = 1L;
@@ -141,7 +151,7 @@ final class IsLikelyTrivialComputationTest {
super(
(expressionTree, state) ->
state.getPath().getParentPath().getLeaf() instanceof ReturnTree
&& new IsLikelyTrivialComputation().matches(expressionTree, state));
&& new RequiresComputation().matches(expressionTree, state));
}
}
}

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.18.1-SNAPSHOT</version>
<version>0.19.2-SNAPSHOT</version>
</parent>
<artifactId>refaster-test-support</artifactId>

View File

@@ -61,6 +61,9 @@ import tech.picnic.errorprone.refaster.runner.Refaster;
// XXX: This check currently only validates that one `Refaster.anyOf` branch in one
// `@BeforeTemplate` method is covered by a test. Review how we can make sure that _all_
// `@BeforeTemplate` methods and `Refaster.anyOf` branches are covered.
// XXX: Look into replacing this setup with another that allows test cases to be co-located
// with/nested within the rules. This way any rule change only requires modifications in a single
// place, rather than in three.
@BugPattern(summary = "Exercises a Refaster rule collection", linkType = NONE, severity = ERROR)
@SuppressWarnings("java:S2160" /* Super class equality definition suffices. */)
public final class RefasterRuleCollection extends BugChecker implements CompilationUnitTreeMatcher {

View File

@@ -3,6 +3,9 @@
releases:
- version: 0.18.0
compatible:
- "2.33.0"
- "2.32.0"
- "2.31.0"
- "2.30.0"
- version: 0.17.0
compatible:

View File

@@ -12,7 +12,7 @@ set -e -u -o pipefail
# Currently all released Error Prone Support versions are compatible with Java
# 17.
java_version=17.0.10-tem
java_version=17.0.13-tem
(set +u && echo n | sdk install java "${java_version}")
sdk use java "${java_version}"

104
workshop/README.md Normal file
View File

@@ -0,0 +1,104 @@
# Error Prone Workshop
Download the slides [here][eps-workshop-slides].
## Initial setup of the workshop
1. Start by cloning this repository locally from GitHub.
2. Checkout the `workshop` branch.
3. Make sure to run `mvn clean install` in the root of this repository.
4. Open your code editor and familiarize yourself with the project structure.
In IntelliJ IDEA (or your preferred editor), try to run the
`WorkshopRefasterRulesTest` test. You might see the following error:
```
java: exporting a package from system module jdk.compiler is not allowed with --release
```
If this happens, go to _File -> Settings... -> Build, Execution, Deployment ->
Compiler -> Java Compiler_ and deselect the option _Use '--release' option for
cross-compilation (Java 9 and later)_.
If you encounter any other bugs when running `mvn clean install`, make sure you
are using a recent release of JDK 17.
Now the project is ready for the workshop.
## Part 1: Writing Refaster rules
During this part of the workshop we will implement multiple Refaster rules.
Go to the `workshop` module and navigate to the
`tech.picnic.errorprone.workshop.refasterrules` package. There you can find one
example and five different exercises to do. Make sure to open the
`WorkshopRefasterRulesTest` class where you can enable tests. Per assignment
there is a test in this class that you can enable (by dropping the `@Disabled`
annotation) to validate your changes. The goal is to implement or improve the
Refaster rules such that the enabled tests pass.
Tips:
* Go through the exercises in the proposed order.
* The `XXX:` comments explain what needs to happen.
* Find the test case for each Refaster rule by looking for the name of the
Refaster rule, prefixed with `test`. For example, the
`WorkshopAssignment0Rules.java` rule collection has a Refaster rule named
`ExampleStringIsEmpty`. In the `WorkshopAssignment0RulesTestInput.java` and
`WorkshopAssignment0RulesTestOutput.java` files there is a
`testExampleStringIsEmpty` method that shows the input and output to test the
Refaster rule.
## Part 2: Writing Error Prone checks
During this part of the workshop we will implement parts of multiple Error
Prone `BugChecker`s. Each of these classes contain `XXX` comments explaining
what needs to be implemented. However, before diving in, make sure to first
navigate to a check's associated test class to drop the class-level `@Disabled`
annotation. Upon initial execution the tests will fail; the goal is to get then
to pass.
Some utility classes that you can use:
* `com.google.errorprone.util.ASTHelpers`: contains many common operations on
the Abstract Syntax Tree.
* `com.google.errorprone.fixes.SuggestedFixes`: contains helper methods for
creating `Fix`es.
### Tips and tricks
If you are working from the command line, it is suggested to run commands in
the `workshop` directory. To do this, navigate to the root of the repository
and run:
```bash
cd workshop
```
In case you want to do a fast build to just compile the project, run:
```bash
mvn clean install -DskipTests -Dverification.skip
```
In case you want to run one test from the command line, run the following
commands:
```bash
mvn clean install -Dtest=WorkshopRefasterRulesTest -Dverification.skip
```
### Troubleshooting
Make sure to use the latest version of JDK 21 or 17.
In some specific cases the `-Dverification.skip` is not correctly processed. In
that case, use `-D"verification.skip"`.
In case there is an error mentioning the `BetaApi` check, please rebuild or try
to `Reload All Maven projects`.
[eps-github]: https://github.com/PicnicSupermarket/error-prone-support
[eps-workshop-slides]: https://docs.google.com/presentation/d/1XQJohAUGSmDyOywJCZWZBjmsRRt1FQ5ykE0F-NaY6Lo/edit?usp=sharing

160
workshop/pom.xml Normal file
View File

@@ -0,0 +1,160 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.19.2-SNAPSHOT</version>
</parent>
<artifactId>workshop</artifactId>
<name>Picnic :: Error Prone Support :: Workshop</name>
<description>Project for the Error Prone Workshop.</description>
<url>https://error-prone.picnic.tech</url>
<dependencies>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotation</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_check_api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_test_helpers</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>error-prone-utils</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-compiler</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-support</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-test-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-java</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-java-11</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-templating</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths combine.children="append">
<path>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-compiler</artifactId>
<version>${project.version}</version>
</path>
<path>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-support</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs combine.children="append">
<arg>-Xplugin:RefasterRuleCompiler</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@@ -0,0 +1,38 @@
package tech.picnic.errorprone.workshop.bugpatterns;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.MethodTree;
/** A {@link BugChecker} that flags empty methods that seemingly can simply be deleted. */
@AutoService(BugChecker.class)
@BugPattern(
summary = "Empty method can likely be deleted",
severity = WARNING,
tags = SIMPLIFICATION)
public final class Assignment0DeleteEmptyMethod extends BugChecker implements MethodTreeMatcher {
private static final long serialVersionUID = 1L;
/** Instantiates a new {@link Assignment0DeleteEmptyMethod} instance. */
public Assignment0DeleteEmptyMethod() {}
@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
if (tree.getBody() == null
|| !tree.getBody().getStatements().isEmpty()
|| ASTHelpers.hasAnnotation(tree, Override.class.getName(), state)) {
return Description.NO_MATCH;
}
return describeMatch(tree, SuggestedFix.delete(tree));
}
}

View File

@@ -0,0 +1,59 @@
package tech.picnic.errorprone.workshop.bugpatterns;
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.argument;
import static com.google.errorprone.matchers.Matchers.argumentCount;
import static com.google.errorprone.matchers.Matchers.instanceMethod;
import static com.google.errorprone.matchers.Matchers.nullLiteral;
import 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.sun.source.tree.MethodInvocationTree;
/**
* A {@link BugChecker} that flags AssertJ {@code isEqualTo(null)} checks for simplification.
*
* <p>This bug checker cannot be replaced with a simple Refaster rule, as the Refaster approach
* would require that all overloads of {@link org.assertj.core.api.Assert#isEqualTo(Object)} (such
* as {@link org.assertj.core.api.AbstractStringAssert#isEqualTo(String)}) are explicitly
* enumerated. This bug checker generically matches all such current and future overloads.
*/
@BugPattern(
summary = "Prefer `.isNull()` over `.isEqualTo(null)`",
severity = SUGGESTION,
tags = SIMPLIFICATION)
public final class Assignment1AssertJIsNullMethod extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<MethodInvocationTree> ASSERT_IS_EQUAL_TO_NULL =
allOf(
instanceMethod().onDescendantOf("org.assertj.core.api.Assert").named("isEqualTo"),
argumentCount(1),
argument(0, nullLiteral()));
/** Instantiates a new {@link Assignment1AssertJIsNullMethod} instance. */
public Assignment1AssertJIsNullMethod() {}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
// This statement filters out `MethodInvocation`s that are *not* `assertThat().isEqualTo(null)`
// statements.
if (!ASSERT_IS_EQUAL_TO_NULL.matches(tree, state)) {
return Description.NO_MATCH;
}
SuggestedFix.Builder fix =
SuggestedFix.builder().merge(SuggestedFixes.renameMethodInvocation(tree, "isNull", state));
tree.getArguments().forEach(arg -> fix.merge(SuggestedFix.delete(arg)));
return describeMatch(tree, fix.build());
}
}

View File

@@ -0,0 +1,60 @@
package tech.picnic.errorprone.workshop.bugpatterns;
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
import static com.google.errorprone.matchers.Matchers.staticMethod;
import com.google.common.collect.Iterables;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import java.util.List;
import tech.picnic.errorprone.utils.SourceCode;
/**
* A {@link BugChecker} that flags method invocations for which all arguments are wrapped using
* {@link org.mockito.Mockito#eq}; this is redundant.
*/
@BugPattern(
summary = "Don't unnecessarily use Mockito's `eq(...)`",
severity = SUGGESTION,
tags = SIMPLIFICATION)
public final class Assignment2DropMockitoEq extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> MOCKITO_EQ_METHOD =
staticMethod().onClass("org.mockito.ArgumentMatchers").named("eq");
/** Instantiates a new {@link Assignment2DropMockitoEq} instance. */
public Assignment2DropMockitoEq() {}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
// XXX: Make sure to return `Description.NO_MATCH` if the `tree` doesn't have arguments, or if
// the `isEqInvocation` method below returns `false` for at least one of the arguments.
List<? extends ExpressionTree> arguments = tree.getArguments();
if (arguments.isEmpty() || !arguments.stream().allMatch(arg -> isEqInvocation(arg, state))) {
return Description.NO_MATCH;
}
SuggestedFix.Builder suggestedFix = SuggestedFix.builder();
for (ExpressionTree arg : arguments) {
suggestedFix.replace(
arg,
SourceCode.treeToString(
Iterables.getOnlyElement(((MethodInvocationTree) arg).getArguments()), state));
}
return describeMatch(tree, suggestedFix.build());
}
private static boolean isEqInvocation(ExpressionTree tree, VisitorState state) {
return tree instanceof MethodInvocationTree && MOCKITO_EQ_METHOD.matches(tree, state);
}
}

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