Compare commits

...

82 Commits

Author SHA1 Message Date
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
75 changed files with 2342 additions and 274 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

@@ -10,16 +10,16 @@ jobs:
strategy:
matrix:
os: [ ubuntu-22.04 ]
jdk: [ 17.0.10, 21.0.2, 22.0.2 ]
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 }}

View File

@@ -36,7 +36,7 @@ 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.9
- name: Initialize CodeQL

View File

@@ -25,7 +25,7 @@ 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.9
- name: Run Pitest

View File

@@ -33,7 +33,7 @@ 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.9
- name: Download Pitest analysis artifact

View File

@@ -36,7 +36,7 @@ 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.9
- name: Install project to local Maven repository

View File

@@ -38,7 +38,7 @@ 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.9
- name: Create missing `test` directory

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.1</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.1</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>
@@ -87,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>
@@ -201,7 +211,7 @@
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-java-11</artifactId>
<artifactId>rewrite-java-17</artifactId>
<scope>test</scope>
</dependency>
<dependency>
@@ -292,6 +302,55 @@
<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

@@ -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

@@ -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

@@ -196,34 +196,6 @@ final class PrimitiveRules {
}
}
/** Prefer {@link Float#compare(float, float)} over the Guava alternative. */
// XXX: Drop this rule once https://github.com/google/guava/pull/7371 is released.
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. */
// XXX: Drop this rule once https://github.com/google/guava/pull/7371 is released.
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

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

@@ -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

@@ -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

@@ -109,14 +109,6 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
return Doubles.hashCode(1);
}
int testFloatCompare() {
return Floats.compare(1, 2);
}
int testDoubleCompare() {
return Doubles.compare(1, 2);
}
int testCharacterBytes() {
return Chars.BYTES;
}

View File

@@ -109,14 +109,6 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
return Double.hashCode(1);
}
int testFloatCompare() {
return Float.compare(1, 2);
}
int testDoubleCompare() {
return Double.compare(1, 2);
}
int testCharacterBytes() {
return Character.BYTES;
}

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.1</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.1</version>
</parent>
<artifactId>error-prone-guidelines</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.1</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)

114
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.1</version>
<packaging>pom</packaging>
<name>Picnic :: Error Prone Support</name>
@@ -52,7 +52,7 @@
<scm child.scm.developerConnection.inherit.append.path="false" child.scm.url.inherit.append.path="false">
<developerConnection>scm:git:git@github.com:PicnicSupermarket/error-prone-support.git</developerConnection>
<tag>HEAD</tag>
<tag>v0.19.1</tag>
<url>https://github.com/PicnicSupermarket/error-prone-support</url>
</scm>
<issueManagement>
@@ -111,6 +111,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 +151,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:57:02Z</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 +211,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.31.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.9</version.maven>
<version.mockito>5.13.0</version.mockito>
<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.14.0</version.rewrite-templating>
<version.rewrite-templating>1.16.3</version.rewrite-templating>
<version.surefire>3.2.3</version.surefire>
</properties>
@@ -296,7 +299,7 @@
<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>
@@ -333,7 +336,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>
@@ -343,7 +346,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-bom</artifactId>
<version>33.3.0-jre</version>
<version>33.3.1-jre</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -362,10 +365,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>
@@ -382,7 +390,7 @@
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.2.23</version>
<version>2.2.25</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
@@ -417,7 +425,7 @@
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.15.1</version>
<version>1.15.7</version>
</dependency>
<!-- Specified so that Renovate will file Maven upgrade PRs, which
subsequently will cause `maven-enforcer-plugin` to require that
@@ -442,7 +450,7 @@
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>3.46.0</version>
<version>3.48.1</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
@@ -462,7 +470,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>
@@ -476,7 +484,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>
@@ -486,7 +501,7 @@
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-recipe-bom</artifactId>
<version>2.17.0</version>
<version>2.21.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -500,19 +515,19 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>6.1.12</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.3</version>
<version>3.3.5</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-bom</artifactId>
<version>6.3.3</version>
<version>6.3.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -523,6 +538,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>
@@ -567,7 +593,7 @@
<plugin>
<groupId>com.spotify.fmt</groupId>
<artifactId>fmt-maven-plugin</artifactId>
<version>2.24</version>
<version>2.25</version>
<configuration>
<additionalSourceDirectories>
<additionalSourceDirectory>${basedir}/src/test/resources</additionalSourceDirectory>
@@ -587,7 +613,7 @@
<plugin>
<groupId>de.thetaphi</groupId>
<artifactId>forbiddenapis</artifactId>
<version>3.7</version>
<version>3.8</version>
<configuration>
<bundledSignatures>
<bundledSignature>jdk-internal</bundledSignature>
@@ -653,7 +679,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.5.0</version>
<version>3.6.0</version>
<configuration>
<checkstyleRules>
<!-- We only enable rules that are not enforced by
@@ -902,7 +928,7 @@
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>10.18.0</version>
<version>10.20.0</version>
</dependency>
<dependency>
<groupId>io.spring.nohttp</groupId>
@@ -1006,7 +1032,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
@@ -1017,11 +1043,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>
@@ -1099,7 +1133,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>
@@ -1114,7 +1148,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>
@@ -1127,7 +1161,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>
@@ -1193,7 +1227,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>
@@ -1244,7 +1278,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>
@@ -1263,7 +1297,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.3.1</version>
<version>3.5.1</version>
<configuration>
<includes>
<include>**/*Test.java</include>
@@ -1474,7 +1508,7 @@
<plugin>
<groupId>org.kordamp.maven</groupId>
<artifactId>pomchecker-maven-plugin</artifactId>
<version>1.11.0</version>
<version>1.14.0</version>
<configuration>
<failOnError>false</failOnError>
<release>false</release>
@@ -1492,7 +1526,7 @@
<plugin>
<groupId>org.pitest</groupId>
<artifactId>pitest-maven</artifactId>
<version>1.16.1</version>
<version>1.17.0</version>
<configuration>
<excludedClasses>
<!-- AutoValue generated classes. -->
@@ -1555,6 +1589,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>
@@ -1934,6 +1972,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. -->

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.1</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.1</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.1</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

@@ -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.1</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,8 +3,10 @@
releases:
- version: 0.18.0
compatible:
- "2.30.0"
- "2.33.0"
- "2.32.0"
- "2.31.0"
- "2.30.0"
- version: 0.17.0
compatible:
- "2.29.2"

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}"