Compare commits

...

223 Commits

Author SHA1 Message Date
Benedek Halasi
92071c854c Merge fixes of inner type decls, enable sorting nested types in one-go 2025-03-26 06:47:22 +01:00
Rick Ossendrijver
06028a0729 Pair programming 2025-03-26 06:47:21 +01:00
Rick Ossendrijver
1112cef15c Cleanup 2025-03-26 06:47:21 +01:00
Benedek Halasi
7eb5479f98 Fix & enable TypeMemberOrderEnumTest 2025-03-26 06:47:21 +01:00
Benedek Halasi
8d3fd072d3 Move TypeMemberOrder BugChecker into experimental module 2025-03-26 06:47:21 +01:00
Rick Ossendrijver
0ccea3c3c2 Final suggestions, but three failing tests 2025-03-26 06:47:21 +01:00
Rick Ossendrijver
d9607bde04 Try to implement new thing 2025-03-26 06:47:21 +01:00
Rick Ossendrijver
6b766891d2 Suggestions part 1 2025-03-26 06:47:21 +01:00
Rick Ossendrijver
a2c92b54dd Suggestions 2025-03-26 06:47:20 +01:00
Benedek Halasi
8c4b8c27d2 Fix syntactically wrong replecement with annotations containing LBRACE 2025-03-26 06:47:20 +01:00
Benedek Halasi
bdb37a553d Reproduce syntactically wrong replacement with annotations containing LBRACE 2025-03-26 06:47:20 +01:00
Rick Ossendrijver
56f58a45b4 Improve more tests 2025-03-26 06:47:20 +01:00
Rick Ossendrijver
bf98ce2f28 Suggestions 2025-03-26 06:47:20 +01:00
Benedek Halasi
6bd1f577ce Tweaks 2025-03-26 06:47:20 +01:00
Benedek Halasi
cd0a54c486 Test abstract and default methods 2025-03-26 06:47:20 +01:00
Rick Ossendrijver
4f139830ef Pair programming session 2025-03-26 06:47:20 +01:00
Rick Ossendrijver
173b43b0ad Post rebase fix 2025-03-26 06:47:19 +01:00
Benedek Halasi
7ec43faf6a Support classes, interfaces and enums 2025-03-26 06:47:19 +01:00
Benedek Halasi
46490b83fc Rename TypeMemberWithComments to TypeMember 2025-03-26 06:47:19 +01:00
Benedek Halasi
01c04007d4 Stop sorting unsorted members annotated w/ SuppressWarnings - all or TypeMemberOrdering 2025-03-26 06:47:19 +01:00
Benedek Halasi
4b9de605e7 Test not-ordering unordered members annotated w/ SuppressWarnings 2025-03-26 06:47:19 +01:00
Benedek Halasi
4ba51ac275 Implement inner type ordering 2025-03-26 06:47:19 +01:00
Benedek Halasi
58d21a5ea5 Test inner type ordering 2025-03-26 06:47:19 +01:00
Benedek Halasi
92181f3320 Implement initializer block ordering 2025-03-26 06:47:18 +01:00
Benedek Halasi
9b7e5a2da7 Test initializer block ordering 2025-03-26 06:47:18 +01:00
Benedek Halasi
9476e78fec Suggestions 2025-03-26 06:47:18 +01:00
Benedek Halasi
0eb4c8d93b Verify that SupressWarnings("all" or "TypeMemberOrdering") are not ordered 2025-03-26 06:47:18 +01:00
Benedek Halasi
1652a24bd2 Add identification test for an empty class, sync identifaction test w/ new error message 2025-03-26 06:47:18 +01:00
Benedek Halasi
69fe9bea95 Rename *class* to *type* 2025-03-26 06:47:18 +01:00
Benedek Halasi
778e2439ae List all remarks as TODOs, or address them 2025-03-26 06:47:18 +01:00
Stephan Schroevers
38adfa0507 Suggestions 2025-03-26 06:47:17 +01:00
Benedek Halasi
5a7995a598 Reproduce error-prone token start - end position overlap 2025-03-26 06:47:17 +01:00
Benedek Halasi
9a9a86cb60 Suggestions on naming, minor organizational changes, rewordings 2025-03-26 06:47:17 +01:00
Benedek Halasi
d7fc940a99 Assume that only generated members' end position is unavailable. 2025-03-26 06:47:17 +01:00
Benedek Halasi
a4dd402172 "Fix" previous token detection 2025-03-26 06:47:17 +01:00
Benedek Halasi
32bd39a4c3 Simplifing logic, killing mutants 2025-03-26 06:47:17 +01:00
Gijs de Jong
4453d9f122 Remove stray unused import 2025-03-26 06:47:17 +01:00
Gijs de Jong
253a35fc93 suggestions 2025-03-26 06:47:16 +01:00
Benedek Halasi
b531cd3fc9 Checkstyle 2025-03-26 06:47:16 +01:00
Benedek Halasi
dec75a841e Suggestions 2025-03-26 06:47:16 +01:00
Rick Ossendrijver
91f11dea71 Revert "Drop replaceIncludingComments"
This reverts commit 133c85da4f86a3d3965384acf4cdc24b3f50677b.
2025-03-26 06:47:16 +01:00
Rick Ossendrijver
01a31c6585 Drop replaceIncludingComments 2025-03-26 06:47:16 +01:00
Rick Ossendrijver
bd08fd30e1 Suggestions 2025-03-26 06:47:16 +01:00
Benedek Halasi
c8b7c7cdbf Apply my suggestions
- phrasing: last , -> and
- Reasonably concise `COMPARATOR` and its docs
2025-03-26 06:47:16 +01:00
Benedek Halasi
305cedd02f Cannot verify whitespace related requirements as TestMode.TEXT_MATCH formats the sources 2025-03-26 06:47:16 +01:00
Benedek Halasi
1bb8b85421 Code clean-up, formatting, docs, TEMPORARY supress warnings 2025-03-26 06:47:15 +01:00
Benedek Halasi
78cf52beff Test if error message contains crucial information 2025-03-26 06:47:15 +01:00
Benedek Halasi
f61539d266 Prepare the code for others to see
- kinda cleanup `replaceIncludingComments` copy
 - mark unsure parts of the code
 - list remaining tasks
2025-03-26 06:47:15 +01:00
Benedek Halasi
1a6f6ec04c Consider comments 2025-03-26 06:47:15 +01:00
Benedek Halasi
f8d6ae147b Naive impl 2025-03-26 06:47:15 +01:00
Stephan Schroevers
92e4d74e4b Update Error Prone compatibility matrix (#1613) 2025-03-25 18:56:11 +01:00
Stephan Schroevers
4a0bc0c210 [maven-release-plugin] prepare for next development iteration 2025-03-24 11:41:07 +01:00
Stephan Schroevers
53152499d6 [maven-release-plugin] prepare release v0.21.0 2025-03-24 11:41:07 +01:00
Picnic-DevPla-Bot
61d92fb33f Upgrade CodeQL v3.28.10 -> v3.28.12 (#1611)
See:
- https://github.com/github/codeql-action/blob/main/CHANGELOG.md
- https://github.com/github/codeql-action/releases/tag/v3.28.11
- https://github.com/github/codeql-action/releases/tag/v3.28.12
- https://github.com/github/codeql-action/compare/v3.28.10...v3.28.12
2025-03-24 10:44:45 +01:00
Picnic-DevPla-Bot
8690f49c35 Upgrade Byte Buddy 1.17.3 -> 1.17.4 (#1610)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.17.4
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.17.3...byte-buddy-1.17.4
2025-03-24 10:30:40 +01:00
Picnic-DevPla-Bot
d808f664ec Upgrade ruby/setup-ruby v1.221.0 -> v1.227.0 (#1612)
See:
- https://github.com/ruby/setup-ruby/releases/tag/v1.227.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.226.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.225.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.224.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.223.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.222.0
2025-03-24 09:39:01 +01:00
Picnic-DevPla-Bot
7f5f4820e9 Upgrade Pitest Git plugins 2.1.0 -> 2.2.0 (#1607) 2025-03-23 17:54:05 +01:00
Picnic-DevPla-Bot
f7a4427852 Upgrade Arcmutate 1.3.2 -> 1.4.0 (#1606) 2025-03-23 17:24:51 +01:00
Picnic-DevPla-Bot
0e956bcd44 Upgrade MongoDB driver 5.3.1 -> 5.4.0 (#1605)
See:
- https://jira.mongodb.org/issues/?jql=project%20%3D%20JAVA%20AND%20fixVersion%20%3E%205.3.1%20AND%20fixVersion%20%3C%3D%205.4.0
- https://github.com/mongodb/mongo-java-driver/releases/tag/r5.4.0-alpha0
- https://github.com/mongodb/mongo-java-driver/releases/tag/r5.4.0
- https://github.com/mongodb/mongo-java-driver/compare/r5.3.1...r5.4.0
2025-03-23 15:53:47 +01:00
Picnic-DevPla-Bot
5395fc60e1 Upgrade Spring Boot 3.4.3 -> 3.4.4 (#1604)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v3.4.4
- https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4
2025-03-23 15:13:32 +01:00
Picnic-DevPla-Bot
d8cae04a98 Upgrade Byte Buddy 1.17.2 -> 1.17.3 (#1603)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.17.3
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.17.2...byte-buddy-1.17.3
2025-03-23 11:59:48 +01:00
Julian Broudy
7b14711ecf Extend FluxJust Refaster rule (#1581) 2025-03-23 11:26:19 +01:00
Tim te Beek
c3351b9ee1 Introduce two FileRules Refaster rules (#1596)
Invoking `File#mkdirs` before testing for existence of the specified
path, rather than the other way around, avoids a subtle concurrency
issue.

See also openrewrite/rewrite#5189.
2025-03-23 11:13:24 +01:00
Picnic-DevPla-Bot
40b3c87b72 Upgrade Spring 6.2.4 -> 6.2.5 (#1602)
See:
- https://github.com/spring-projects/spring-framework/releases/tag/v6.2.5
- https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5
2025-03-22 12:54:04 +01:00
Picnic-DevPla-Bot
726db5ce5b Upgrade Error Prone 2.36.0 -> 2.37.0 (#1601)
Resolves #1597.

See:
- https://github.com/google/error-prone/releases/tag/v2.37.0
- https://github.com/google/error-prone/compare/v2.36.0...v2.37.0
- https://github.com/PicnicSupermarket/error-prone/compare/v2.36.0-picnic-4...v2.37.0-picnic-1
2025-03-21 09:47:13 +01:00
Picnic-DevPla-Bot
99d359c548 Upgrade OpenRewrite Templating 1.24.0 -> 1.24.1 (#1599)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.24.1
- https://github.com/openrewrite/rewrite-templating/compare/v1.24.0...v1.24.1
2025-03-20 16:42:24 +01:00
Picnic-DevPla-Bot
4744113411 Upgrade actions/upload-artifact v4.6.1 -> v4.6.2 (#1600)
See:
- https://github.com/actions/upload-artifact/releases/tag/v4.6.2
2025-03-20 12:40:33 +01:00
Picnic-DevPla-Bot
ae8f940f55 Upgrade AspectJ 1.9.22.1 -> 1.9.23 (#1587)
See:
- https://github.com/eclipse-aspectj/aspectj/releases/tag/V1_9_23
- https://github.com/eclipse-aspectj/aspectj/compare/V1_9_22_1...V1_9_23
2025-03-19 21:28:29 +01:00
Picnic-DevPla-Bot
8d7633df2d Upgrade pitest-maven-plugin 1.18.2 -> 1.19.0 (#1593)
See:
- https://github.com/hcoles/pitest/releases/tag/1.19.0
- https://github.com/hcoles/pitest/compare/1.18.2...1.19.0
2025-03-19 21:16:34 +01:00
Picnic-DevPla-Bot
4a351cfbe3 Upgrade JUnit 5 5.12.0 -> 5.12.1 (#1589)
See:
- https://junit.org/junit5/docs/current/release-notes/
- https://github.com/junit-team/junit5/releases/tag/r5.12.1
- https://github.com/junit-team/junit5/compare/r5.12.0...r5.12.1
2025-03-19 09:30:32 +01:00
Stephan Schroevers
bb01447760 Relocate Arcmutate license file (#1594)
This former location is deprecated.
2025-03-18 15:31:18 +01:00
Picnic-DevPla-Bot
f12ed17b96 Upgrade Spring Security 6.4.3 -> 6.4.4 (#1592)
See:
- https://github.com/spring-projects/spring-security/releases/tag/6.4.4
- https://github.com/spring-projects/spring-security/compare/6.4.3...6.4.4
2025-03-18 12:06:47 +01:00
Stephan Schroevers
854e05f044 Upgrade Arcmutate plugins (#1586)
These upgrades were not picked up automatically due to a groupId change.

Summary of changes:
- Upgrade Pitest Git plugins 1.1.4 -> 2.1.0
- Upgrade Arcmutate 1.2.2 -> 1.3.2
- Upgrade pitest-accelerator-junit5 1.0.6 -> 1.2.0
2025-03-18 11:47:21 +01:00
Picnic-DevPla-Bot
82c22682c0 Upgrade Mockito 5.16.0 -> 5.16.1 (#1590)
See:
- https://github.com/mockito/mockito/releases/tag/v5.16.1
- https://github.com/mockito/mockito/compare/v5.16.0...v5.16.1
2025-03-18 11:16:24 +01:00
Stephan Schroevers
25b3817876 Upgrade Error Prone fork 2.36.0-picnic-2 -> 2.36.0-picnic-4 (#1591)
See:
- https://github.com/PicnicSupermarket/error-prone/releases/tag/v2.36.0-picnic-3
- https://github.com/PicnicSupermarket/error-prone/releases/tag/v2.36.0-picnic-4
- https://github.com/PicnicSupermarket/error-prone/compare/v2.36.0-picnic-2...v2.36.0-picnic-4
2025-03-18 09:10:40 +01:00
Picnic-DevPla-Bot
e2a2b086ad Upgrade Spring 6.2.3 -> 6.2.4 (#1588)
See:
- https://github.com/spring-projects/spring-framework/releases/tag/v6.2.4
- https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4
2025-03-15 13:39:30 +01:00
Picnic-DevPla-Bot
5f9a0c5e17 Upgrade Project Reactor 2024.0.3 -> 2024.0.4 (#1583)
See:
- https://github.com/reactor/reactor/releases/tag/2024.0.4
- https://github.com/reactor/reactor/compare/2024.0.3...2024.0.4
2025-03-15 11:37:49 +01:00
Picnic-DevPla-Bot
2a0ae0af13 Upgrade OpenRewrite Templating 1.23.0 -> 1.24.0 (#1585)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.24.0
- https://github.com/openrewrite/rewrite-templating/compare/v1.23.0...v1.24.0
2025-03-14 17:35:28 +01:00
Picnic-DevPla-Bot
7a051146b6 Upgrade Micrometer 1.14.4 -> 1.14.5 (#1582)
See:
- https://github.com/micrometer-metrics/micrometer/releases/tag/v1.14.5
- https://github.com/micrometer-metrics/micrometer/compare/v1.14.4...v1.14.5
2025-03-13 08:49:57 +01:00
Picnic-DevPla-Bot
d9398001cb Upgrade OpenRewrite 3.2.0 -> 3.4.0 (#1557)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v3.3.0
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v3.4.0
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v3.2.0...v3.4.0
2025-03-12 15:06:25 +01:00
Picnic-DevPla-Bot
0be99cb12a Upgrade Swagger 2.2.28 -> 2.2.29 (#1584)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.29
- https://github.com/swagger-api/swagger-core/compare/v2.2.28...v2.2.29
2025-03-12 13:07:45 +01:00
Picnic-DevPla-Bot
70e2d3d43f Upgrade tidy-maven-plugin 1.3.0 -> 1.4.0 (#1580)
See:
- https://github.com/mojohaus/tidy-maven-plugin/releases/tag/1.4.0
- https://github.com/mojohaus/tidy-maven-plugin/compare/tidy-maven-plugin-1.3.0...1.4.0
2025-03-12 09:30:27 +01:00
Picnic-DevPla-Bot
441258f9f2 Upgrade maven-deploy-plugin 3.1.3 -> 3.1.4 (#1573)
See:
- https://github.com/apache/maven-deploy-plugin/releases/tag/maven-deploy-plugin-3.1.4
2025-03-10 14:37:20 +01:00
Picnic-DevPla-Bot
b96efb533c Upgrade Checkstyle 10.21.3 -> 10.21.4 (#1579)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.21.4
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.21.3...checkstyle-10.21.4
2025-03-09 12:35:28 +01:00
Picnic-DevPla-Bot
5af067cc23 Upgrade Mockito 5.15.2 -> 5.16.0 (#1578)
See:
- https://github.com/mockito/mockito/releases/tag/v5.16.0
- https://github.com/mockito/mockito/compare/v5.15.2...v5.16.0
2025-03-09 12:05:58 +01:00
Picnic-DevPla-Bot
0c84665e38 Upgrade maven-install-plugin 3.1.3 -> 3.1.4 (#1574)
See:
- https://github.com/apache/maven-install-plugin/releases/tag/maven-install-plugin-3.1.4
- https://github.com/apache/maven-install-plugin/compare/maven-install-plugin-3.1.3...maven-install-plugin-3.1.4
2025-03-09 11:52:58 +01:00
Picnic-DevPla-Bot
9316d81fe3 Upgrade Jackson 2.18.2 -> 2.18.3 (#1575)
See:
- https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.18.3
- https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.18.2...jackson-bom-2.18.3
2025-03-09 11:43:33 +01:00
Picnic-DevPla-Bot
ec96c044d5 Upgrade NullAway 0.12.3 -> 0.12.4 (#1572)
See:
- https://github.com/uber/NullAway/blob/master/CHANGELOG.md
- https://github.com/uber/NullAway/releases/tag/v0.12.4
- https://github.com/uber/NullAway/compare/v0.12.3...v0.12.4
2025-03-09 11:33:22 +01:00
Picnic-DevPla-Bot
ae4ed9273f Upgrade Checker Framework Annotations 3.49.0 -> 3.49.1 (#1577)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.49.1
- https://github.com/typetools/checker-framework/compare/checker-framework-3.49.0...checker-framework-3.49.1
2025-03-09 11:24:08 +01:00
Picnic-DevPla-Bot
0ec882e62e Upgrade Byte Buddy 1.17.1 -> 1.17.2 (#1576)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.17.2
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.17.1...byte-buddy-1.17.2
2025-03-09 09:47:31 +01:00
Picnic-DevPla-Bot
fef9ba23f4 Upgrade org.pitest:pitest-junit5-plugin 1.2.1 -> 1.2.2 (#1570)
See:
- https://github.com/pitest/pitest-junit5-plugin/releases/tag/1.2.2
- https://github.com/pitest/pitest-junit5-plugin/compare/1.2.1...1.2.2
2025-03-04 09:25:13 +01:00
Picnic-DevPla-Bot
3bd415b910 Upgrade SLF4J 2.0.16 -> 2.0.17 (#1571)
See:
- https://www.slf4j.org/news.html
- https://github.com/qos-ch/slf4j/compare/v_2.0.16...v_2.0.17
2025-02-27 10:12:40 +01:00
Picnic-DevPla-Bot
2128624261 Upgrade ruby/setup-ruby v1.213.0 -> v1.221.0 (#1568)
See:
- https://github.com/ruby/setup-ruby/releases/tag/v1.221.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.220.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.219.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.218.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.217.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.216.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.215.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.214.0
2025-02-24 12:53:58 +01:00
Picnic-DevPla-Bot
c53fcace76 Upgrade CodeQL v3.28.5 -> v3.28.10 (#1567)
See:
- https://github.com/github/codeql-action/blob/main/CHANGELOG.md
- https://github.com/github/codeql-action/releases/tag/v3.28.6
- https://github.com/github/codeql-action/releases/tag/v3.28.7
- https://github.com/github/codeql-action/releases/tag/v3.28.8
- https://github.com/github/codeql-action/releases/tag/v3.28.9
- https://github.com/github/codeql-action/releases/tag/v3.28.10
- https://github.com/github/codeql-action/compare/v3.28.5...v3.28.10
2025-02-24 10:56:49 +01:00
Picnic-DevPla-Bot
ff3759984c Upgrade Checkstyle 10.21.2 -> 10.21.3 (#1566)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.21.3
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.21.2...checkstyle-10.21.3
2025-02-24 09:58:19 +01:00
Phil Werli
6358d1e2e1 Extend MonoSingleOptional Refaster rule (#1553) 2025-02-24 09:25:30 +01:00
Mohamed Sameh
db53631e76 Extend MonoThen Refaster rule (#1556) 2025-02-24 09:09:27 +01:00
Picnic-DevPla-Bot
86b2d929ab Upgrade OpenRewrite Templating 1.22.1 -> 1.23.0 (#1558)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.23.0
- https://github.com/openrewrite/rewrite-templating/compare/v1.22.1...v1.23.0
2025-02-23 22:22:38 +01:00
Picnic-DevPla-Bot
0db4462a64 Upgrade ossf/scorecard-action v2.4.0 -> v2.4.1 (#1563)
See:
- https://github.com/ossf/scorecard-action/releases/tag/v2.4.1
2025-02-23 22:12:53 +01:00
Picnic-DevPla-Bot
f8667fc655 Upgrade pitest-maven-plugin 1.18.1 -> 1.18.2 (#1552)
See:
- https://github.com/hcoles/pitest/releases/tag/1.18.2
- https://github.com/hcoles/pitest/compare/1.18.1...1.18.2
2025-02-23 18:06:49 +01:00
Picnic-DevPla-Bot
d91f94e879 Upgrade actions/upload-artifact v4.6.0 -> v4.6.1 (#1562)
See:
- https://github.com/actions/upload-artifact/releases/tag/v4.6.1
2025-02-23 17:43:58 +01:00
Picnic-DevPla-Bot
ad6c8593f2 Upgrade JUnit 5 5.11.4 -> 5.12.0 (#1564)
See:
- https://junit.org/junit5/docs/current/release-notes/
- https://github.com/junit-team/junit5/releases/tag/r5.12.0-M1
- https://github.com/junit-team/junit5/releases/tag/r5.12.0-RC1
- https://github.com/junit-team/junit5/releases/tag/r5.12.0-RC2
- https://github.com/junit-team/junit5/releases/tag/r5.12.0
- https://github.com/junit-team/junit5/compare/r5.11.4...r5.12.0
2025-02-23 17:30:14 +01:00
Picnic-DevPla-Bot
873ee47e8d Upgrade maven-compiler-plugin 3.13.0 -> 3.14.0 (#1565)
See:
- https://github.com/apache/maven-compiler-plugin/releases/tag/maven-compiler-plugin-3.14.0
- https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.13.0...maven-compiler-plugin-3.14.0
2025-02-23 17:13:46 +01:00
Picnic-DevPla-Bot
fed281dc2b Upgrade Spring Boot 3.4.2 -> 3.4.3 (#1561)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v3.4.3
- https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3
2025-02-22 13:36:10 +01:00
Picnic-DevPla-Bot
277d30467d Upgrade Spring Security 6.4.2 -> 6.4.3 (#1554)
See:
- https://github.com/spring-projects/spring-security/releases/tag/6.4.3
- https://github.com/spring-projects/spring-security/compare/6.4.2...6.4.3
2025-02-21 14:55:42 +01:00
Picnic-DevPla-Bot
064e0bbc8c Upgrade maven-clean-plugin 3.4.0 -> 3.4.1 (#1555)
See:
- https://github.com/apache/maven-clean-plugin/releases/tag/maven-clean-plugin-3.4.1
- https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.4.0...maven-clean-plugin-3.4.1
2025-02-20 11:29:43 +01:00
Stephan Schroevers
3beadeec50 Introduce ArraysCompareUnsigned{Byte,Int,Long}s Refaster rules (#1542)
While there, document possible `StreamFlatMapOptional` improvement.
2025-02-18 19:24:21 +01:00
Phil Werli
c2a26ed12f Introduce MonoFromFutureAsyncLoadingCacheGetAll Refaster rule (#1551) 2025-02-18 19:04:11 +01:00
Picnic-DevPla-Bot
2419dad433 Upgrade step-security/harden-runner v2.10.4 -> v2.11.0 (#1550)
See:
- https://github.com/step-security/harden-runner/releases/tag/v2.11.0
2025-02-17 09:48:16 +01:00
Picnic-DevPla-Bot
900d6eaf78 Upgrade TestNG 7.10.2 -> 7.11.0 (#1546)
See:
- https://github.com/testng-team/testng/releases/tag/7.11.0
- https://github.com/testng-team/testng/compare/7.10.2...7.11.0
2025-02-17 08:57:36 +01:00
Picnic-DevPla-Bot
33d205e11f Upgrade Byte Buddy 1.17.0 -> 1.17.1 (#1549)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.17.1
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.17.0...byte-buddy-1.17.1
2025-02-16 13:47:53 +01:00
Picnic-DevPla-Bot
ee0651d9d3 Upgrade pitest-maven-plugin 1.18.0 -> 1.18.1 (#1545)
See:
- https://github.com/hcoles/pitest/releases/tag/1.18.1
- https://github.com/hcoles/pitest/compare/1.18.0...1.18.1
2025-02-15 19:12:33 +01:00
Picnic-DevPla-Bot
fb22039fd3 Upgrade Spring 6.2.2 -> 6.2.3 (#1548)
See:
- https://github.com/spring-projects/spring-framework/releases/tag/v6.2.3
- https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3
2025-02-15 17:08:11 +01:00
Picnic-DevPla-Bot
cf294b8964 Upgrade Project Reactor 2024.0.2 -> 2024.0.3 (#1544)
See:
- https://github.com/reactor/reactor/releases/tag/2024.0.3
- https://github.com/reactor/reactor/compare/2024.0.2...2024.0.3
2025-02-13 14:04:35 +01:00
Picnic-DevPla-Bot
0004c8bc44 Upgrade Micrometer 1.14.3 -> 1.14.4 (#1543)
See:
- https://github.com/micrometer-metrics/micrometer/releases/tag/v1.14.4
- https://github.com/micrometer-metrics/micrometer/compare/v1.14.3...v1.14.4
2025-02-13 09:02:32 +01:00
Stephan Schroevers
aedeeb0943 Fix integration test step-security/harden-runner configuration (#1541)
Resolves #1534.
2025-02-09 20:54:57 +01:00
Picnic-DevPla-Bot
a68e5ae150 Upgrade OpenRewrite 3.1.0 -> 3.2.0 (#1540)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v3.2.0
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v3.1.0...v3.2.0
2025-02-08 15:52:40 +01:00
Picnic-DevPla-Bot
6a6354eb55 Upgrade OpenRewrite Templating 1.22.0 -> 1.22.1 (#1539)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.22.1
- https://github.com/openrewrite/rewrite-templating/compare/v1.22.0...v1.22.1
2025-02-08 15:18:39 +01:00
Picnic-DevPla-Bot
ab0a844f26 Upgrade pitest-maven-plugin 1.17.4 -> 1.18.0 (#1538)
See:
- https://github.com/hcoles/pitest/releases/tag/1.18.0
- https://github.com/hcoles/pitest/compare/1.17.4...1.18.0
2025-02-08 14:59:53 +01:00
Picnic-DevPla-Bot
5105cb684f Upgrade Checker Framework Annotations 3.48.4 -> 3.49.0 (#1537)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.49.0
- https://github.com/typetools/checker-framework/compare/checker-framework-3.48.4...checker-framework-3.49.0
2025-02-08 13:38:40 +01:00
Stephan Schroevers
f128a7975f Update Error Prone compatibility matrix (#1536) 2025-02-03 09:04:46 +01:00
Stephan Schroevers
e572a3641d [maven-release-plugin] prepare for next development iteration 2025-02-02 13:19:40 +01:00
Stephan Schroevers
2b467832fb [maven-release-plugin] prepare release v0.20.0 2025-02-02 13:19:40 +01:00
Rick Ossendrijver
77557c80b1 Extend size-related AssertJEnumerableRules Refaster rules (#1512)
By also matching expressions ending in `.returnToIterable()`.
2025-02-02 12:46:39 +01:00
Picnic-DevPla-Bot
9088369f89 Upgrade Byte Buddy 1.16.1 -> 1.17.0 (#1535)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.17.0
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.16.1...byte-buddy-1.17.0
2025-02-01 16:41:07 +01:00
Picnic-DevPla-Bot
36ef967f0e Upgrade Checkstyle 10.21.1 -> 10.21.2 (#1531)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.21.2
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.21.1...checkstyle-10.21.2
2025-01-27 14:07:12 +01:00
Picnic-DevPla-Bot
dd190ee056 Upgrade CodeQL v3.28.0 -> v3.28.5 (#1532)
See:
- https://github.com/github/codeql-action/blob/main/CHANGELOG.md
- https://github.com/github/codeql-action/releases/tag/v3.28.1
- https://github.com/github/codeql-action/releases/tag/v3.28.2
- https://github.com/github/codeql-action/releases/tag/v3.28.3
- https://github.com/github/codeql-action/releases/tag/v3.28.4
- https://github.com/github/codeql-action/releases/tag/v3.28.5
- https://github.com/github/codeql-action/compare/v3.28.0...v3.28.5
2025-01-27 13:40:57 +01:00
Picnic-DevPla-Bot
4c32d3ce7a Upgrade ruby/setup-ruby v1.206.0 -> v1.213.0 (#1533)
See:
- https://github.com/ruby/setup-ruby/releases/tag/v1.213.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.212.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.211.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.210.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.209.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.208.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.207.0
2025-01-27 10:13:25 +01:00
Picnic-DevPla-Bot
4608c00140 Upgrade OpenRewrite 3.0.2 -> 3.1.0 (#1529)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v3.1.0
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v3.0.2...v3.1.0
2025-01-25 13:50:19 +01:00
Picnic-DevPla-Bot
708317f314 Upgrade Spring Boot 3.4.1 -> 3.4.2 (#1528)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v3.4.2
- https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2
2025-01-25 13:22:19 +01:00
Picnic-DevPla-Bot
636c1993d5 Upgrade OpenRewrite Templating 1.21.0 -> 1.22.0 (#1530)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.22.0
- https://github.com/openrewrite/rewrite-templating/compare/v1.21.0...v1.22.0
2025-01-25 13:11:33 +01:00
Picnic-DevPla-Bot
895875c849 Upgrade MongoDB driver 5.3.0 -> 5.3.1 (#1527)
See:
- https://github.com/mongodb/mongo-java-driver/releases/tag/r5.3.1
- https://github.com/mongodb/mongo-java-driver/compare/r5.3.0...r5.3.1
2025-01-24 10:11:42 +01:00
Picnic-DevPla-Bot
1aea710c53 Upgrade OpenRewrite 3.0.1 -> 3.0.2 (#1519)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v3.0.2
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v3.0.1...v3.0.2
2025-01-21 19:41:23 +01:00
Picnic-DevPla-Bot
5085eb2f25 Upgrade AssertJ 3.27.2 -> 3.27.3 (#1523)
See:
- https://github.com/assertj/assertj/releases/tag/assertj-build-3.27.3
- https://github.com/assertj/assertj/compare/assertj-build-3.27.2...assertj-build-3.27.3
2025-01-21 07:38:00 +01:00
Picnic-DevPla-Bot
ad5b1c9917 Upgrade Modernizer Maven Plugin 3.0.0 -> 3.1.0 (#1526)
See:
- https://github.com/gaul/modernizer-maven-plugin/releases/tag/modernizer-maven-plugin-3.1.0
- https://github.com/gaul/modernizer-maven-plugin/compare/modernizer-maven-plugin-3.0.0...modernizer-maven-plugin-3.1.0
2025-01-20 12:41:38 +01:00
Picnic-DevPla-Bot
34cab33b76 Upgrade step-security/harden-runner v2.10.3 -> v2.10.4 (#1525)
See:
- https://github.com/step-security/harden-runner/releases/tag/v2.10.4
2025-01-20 09:50:54 +01:00
Picnic-DevPla-Bot
c836f1147f Upgrade Byte Buddy 1.16.0 -> 1.16.1 (#1524)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.16.1
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.16.0...byte-buddy-1.16.1
2025-01-20 09:28:43 +01:00
Picnic-DevPla-Bot
f43921f344 Upgrade Caffeine 3.1.8 -> 3.2.0 (#1522)
See:
- https://github.com/ben-manes/caffeine/releases/tag/v3.2.0
- https://github.com/ben-manes/caffeine/compare/v3.1.8...v3.2.0
2025-01-19 21:19:32 +01:00
Picnic-DevPla-Bot
811e70ae3a Upgrade Swagger 2.2.27 -> 2.2.28 (#1520)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.28
- https://github.com/swagger-api/swagger-core/compare/v2.2.27...v2.2.28
2025-01-19 21:09:43 +01:00
Picnic-DevPla-Bot
5fd50e6181 Upgrade Byte Buddy 1.15.11 -> 1.16.0 (#1521)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.16.0
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.15.11...byte-buddy-1.16.0
2025-01-18 11:08:24 +01:00
Picnic-DevPla-Bot
3767341605 Upgrade swagger-annotations 1.6.14 -> 1.6.15 (#1517)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v1.6.15
- https://github.com/swagger-api/swagger-core/compare/v1.6.14...v1.6.15
2025-01-17 13:05:37 +01:00
Picnic-DevPla-Bot
9f83eec36b Upgrade jOOQ 3.19.17 -> 3.19.18 (#1518)
See:
- https://www.jooq.org/notes
- https://github.com/jOOQ/jOOQ/releases/tag/version-3.19.18
- https://github.com/jOOQ/jOOQ/compare/version-3.19.17...version-3.19.18
2025-01-17 12:40:07 +01:00
Picnic-DevPla-Bot
126159878c Upgrade Spring 6.2.1 -> 6.2.2 (#1516)
See:
- https://github.com/spring-projects/spring-framework/releases/tag/v6.2.2
- https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2
2025-01-17 11:33:55 +01:00
Picnic-DevPla-Bot
78141b2528 Upgrade Project Reactor 2024.0.1 -> 2024.0.2 (#1515)
See:
- https://github.com/reactor/reactor/releases/tag/2024.0.2
- https://github.com/reactor/reactor/compare/2024.0.1...2024.0.2
2025-01-16 18:40:35 +01:00
Picnic-DevPla-Bot
56db8adb5a Upgrade OpenRewrite 3.0.0 -> 3.0.1 (#1514)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v3.0.1
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v3.0.0...v3.0.1
2025-01-15 16:44:33 +01:00
Picnic-DevPla-Bot
90b4baba12 Upgrade Micrometer 1.14.2 -> 1.14.3 (#1513)
See:
- https://github.com/micrometer-metrics/micrometer/releases/tag/v1.14.3
- https://github.com/micrometer-metrics/micrometer/compare/v1.14.2...v1.14.3
2025-01-15 16:20:12 +01:00
Picnic-DevPla-Bot
54c56a4cc8 Upgrade OpenRewrite 2.23.2 -> 3.0.0 (#1511)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v3.0.0
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.23.2...v3.0.0
2025-01-13 14:17:40 +01:00
Picnic-DevPla-Bot
b123282778 Upgrade OpenRewrite Templating 1.20.2 -> 1.21.0 (#1510)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.21.0
- https://github.com/openrewrite/rewrite-templating/compare/v1.20.2...v1.21.0
2025-01-13 09:51:58 +01:00
Picnic-DevPla-Bot
f4562c7106 Upgrade MongoDB driver 5.2.1 -> 5.3.0 (#1509)
See:
- https://jira.mongodb.org/issues/?jql=project%20%3D%20JAVA%20AND%20fixVersion%20%3E%205.2.1%20AND%20fixVersion%20%3C%3D%205.3.0
- https://github.com/mongodb/mongo-java-driver/releases/tag/r5.3.0-beta0
- https://github.com/mongodb/mongo-java-driver/releases/tag/r5.3.0
- https://github.com/mongodb/mongo-java-driver/compare/r5.2.1...r5.3.0
2025-01-12 12:31:32 +01:00
Picnic-DevPla-Bot
6211e7e651 Upgrade pitest-maven-plugin 1.17.3 -> 1.17.4 (#1508)
See:
- https://github.com/hcoles/pitest/releases/tag/1.17.4
- https://github.com/hcoles/pitest/compare/1.17.3...1.17.4
2025-01-12 11:54:41 +01:00
Stephan Schroevers
3e4a01a673 Have TimeZoneUsage check also flag illegal method references (#1507)
While there, reorder a few things.
2025-01-11 14:54:28 +01:00
Stephan Schroevers
a42353b41a Introduce EagerStringFormatting check (#1139)
This new check flags code that can be simplified and/or optimized by
deferring certain string formatting operations.
2025-01-11 14:42:22 +01:00
Picnic-DevPla-Bot
d2ce18e33e Upgrade actions/upload-artifact v4.5.0 -> v4.6.0 (#1506)
See:
- https://github.com/actions/upload-artifact/releases/tag/v4.6.0
2025-01-11 12:36:15 +01:00
Stephan Schroevers
7b81dc44e5 Upgrade Error Prone fork 2.36.0-picnic-1 -> 2.36.0-picnic-2 (#1499)
This new release is published using GitHub Packages rather than
JitPack. This comes with a simpler release setup and a (hopefully) more
stable package repository. A minor downside is that GitHub Packages
require authenticated access, even for read access. The documentation
has been updated accordingly.

The new release is no longer published using a custom groupId, enabling
some build simplifications.

While there, some obsolete fork-related documentation and configuration
is dropped.

See:
- https://github.com/PicnicSupermarket/error-prone/releases/tag/v2.36.0-picnic-2
- https://github.com/PicnicSupermarket/error-prone/compare/v2.36.0-picnic-1...v2.36.0-picnic-2
2025-01-10 16:40:59 +01:00
Phil Werli
5e81719cea Disallow static imports for InstantSource class and EPOCH constants (#1503) 2025-01-10 15:06:24 +01:00
Picnic-DevPla-Bot
2f3788fc63 Upgrade jOOQ 3.19.16 -> 3.19.17 (#1504)
See:
- https://www.jooq.org/notes
- https://github.com/jOOQ/jOOQ/releases/tag/version-3.19.17
- https://github.com/jOOQ/jOOQ/compare/version-3.19.16...version-3.19.17
2025-01-10 13:12:35 +01:00
Picnic-DevPla-Bot
a98780cef2 Upgrade step-security/harden-runner v2.10.2 -> v2.10.3 (#1505)
See:
- https://github.com/step-security/harden-runner/releases/tag/v2.10.3
2025-01-10 11:51:58 +01:00
Stephan Schroevers
ff0e30c885 Make Mockito setup IntelliJ IDEA-compatible (#1500)
See https://github.com/mockito/mockito/issues/3037#issuecomment-2569680756
2025-01-09 09:20:39 +01:00
Stephan Schroevers
4aacc58382 Extend JUnitToAssertJRules Refaster rule collection (#1484)
By migrating all remaining `assertArrayEquals` methods.
2025-01-08 09:35:09 +01:00
Stephan Schroevers
dec22b8633 Introduce assorted Iterator and Iterable Refaster rules (#1487) 2025-01-07 19:15:54 +01:00
Picnic-DevPla-Bot
37d8ea92c6 Upgrade NullAway 0.12.2 -> 0.12.3 (#1502)
See:
- https://github.com/uber/NullAway/blob/master/CHANGELOG.md
- https://github.com/uber/NullAway/releases/tag/v0.12.3
- https://github.com/uber/NullAway/compare/v0.12.2...v0.12.3
2025-01-07 18:01:31 +01:00
Picnic-DevPla-Bot
7eb46dd218 Upgrade Checker Framework Annotations 3.48.3 -> 3.48.4 (#1497)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.48.4
- https://github.com/typetools/checker-framework/compare/checker-framework-3.48.3...checker-framework-3.48.4
2025-01-06 08:54:01 +01:00
Picnic-DevPla-Bot
623bdd7344 Upgrade Mockito 5.14.2 -> 5.15.2 (#1498)
See:
- https://github.com/mockito/mockito/releases/tag/v5.15.0
- https://github.com/mockito/mockito/releases/tag/v5.15.1
- https://github.com/mockito/mockito/releases/tag/v5.15.2
- https://github.com/mockito/mockito/compare/v5.14.2...v5.15.2
2025-01-05 17:27:38 +01:00
Picnic-DevPla-Bot
c3ae1b5b92 Upgrade AssertJ 3.27.1 -> 3.27.2 (#1501)
See:
- https://github.com/assertj/assertj/releases/tag/assertj-build-3.27.2
- https://github.com/assertj/assertj/compare/assertj-build-3.27.1...assertj-build-3.27.2
2025-01-05 13:28:00 +01:00
Stephan Schroevers
19d3ba0505 Extend ImmutableMapRules Refaster rule collection (#1488)
Resolves #1223.
2025-01-03 11:53:24 +01:00
Stephan Schroevers
9493f2d59a Introduce ImmutableTableRules Refaster rule collection (#1489) 2025-01-03 11:18:47 +01:00
Picnic-DevPla-Bot
68ecd652f7 Upgrade AssertJ 3.27.0 -> 3.27.1 (#1496)
See:
- https://github.com/assertj/assertj/releases/tag/assertj-build-3.27.1
- https://github.com/assertj/assertj/compare/assertj-build-3.27.0...assertj-build-3.27.1
2025-01-03 08:53:48 +01:00
Phil Werli
e67800e4e2 Introduce FluxMapNotNull{,Transformation}OrElse Refaster rules (#1493) 2025-01-02 12:42:25 +01:00
Picnic-DevPla-Bot
7f90f26830 Upgrade s4u/setup-maven-action v1.17.0 -> v1.18.0 (#1495)
See:
- https://github.com/s4u/setup-maven-action/releases/tag/v1.18.0
2025-01-02 07:40:34 +01:00
Stephan Schroevers
6477c1899d Extend TestNGToAssertJRules Refaster rule collection (#1483)
By migrating all remaining `assertEquals` methods.
2025-01-01 11:39:30 +01:00
Stephan Schroevers
03514f9f6a Generalize assorted Refaster rules (#1481) 2025-01-01 01:27:07 +01:00
Phil Werli
d11ac09bda Extend {Is,Non}NullFunction Refaster rules (#1494)
While there, simplify the associated tests.
2024-12-31 16:30:17 +01:00
Stephan Schroevers
667f83fd2f Run GitHub Actions workflows on macos-15 and windows-2025 (#1490)
See:
- https://github.com/actions/runner-images/blob/main/README.md#available-images
- https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md
- https://github.com/actions/runner-images/blob/main/images/windows/Windows2025-Readme.md
2024-12-31 09:49:40 +01:00
Picnic-DevPla-Bot
15ba59a9aa Upgrade CodeQL v3.27.5 -> v3.28.0 (#1491)
See:
- https://github.com/github/codeql-action/blob/main/CHANGELOG.md
- https://github.com/github/codeql-action/releases/tag/v3.27.6
- https://github.com/github/codeql-action/releases/tag/v3.27.7
- https://github.com/github/codeql-action/releases/tag/v3.27.9
- https://github.com/github/codeql-action/releases/tag/v3.28.0
- https://github.com/github/codeql-action/compare/v3.27.5...v3.28.0
2024-12-31 08:38:03 +01:00
Picnic-DevPla-Bot
a46d6dc146 Upgrade ruby/setup-ruby v1.202.0 -> v1.206.0 (#1492)
See:
- https://github.com/ruby/setup-ruby/releases/tag/v1.206.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.205.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.204.0
- https://github.com/ruby/setup-ruby/releases/tag/v1.203.0
2024-12-30 08:16:01 +01:00
Picnic-DevPla-Bot
cf523bc811 Upgrade Checkstyle 10.21.0 -> 10.21.1 (#1485)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.21.1
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.21.0...checkstyle-10.21.1
2024-12-29 21:41:34 +01:00
Stephan Schroevers
9bd8095e0b Sync Dropwizard Metrics integration test (#1482) 2024-12-29 20:30:59 +01:00
Picnic-DevPla-Bot
6cfa9a0dfe Upgrade Modernizer Maven Plugin 2.9.0 -> 3.0.0 (#1486)
See:
- https://github.com/gaul/modernizer-maven-plugin/releases/tag/modernizer-maven-plugin-3.0.0
- https://github.com/gaul/modernizer-maven-plugin/compare/modernizer-maven-plugin-2.9.0...modernizer-maven-plugin-3.0.0
2024-12-29 20:05:43 +01:00
Rick Ossendrijver
7daa39a0b5 Update comments referencing Refaster rule limitation (#46)
Issue google/error-prone#2706 has been resolved; we have yet to decide
how to proceed.
2024-12-27 05:30:30 +01:00
Rick Ossendrijver
5fc7bc29ee Introduce CharSequenceRules Refaster rule collection (#1480)
Resolves #1394.
2024-12-26 17:47:57 +01:00
Stephan Schroevers
83f3f8bedc Introduce AssertThatString{Contains,DoesNotContain} Refaster rules (#1479)
While there, extend `AssertThatIterableIsEmpty`.
2024-12-25 13:40:22 +01:00
Picnic-DevPla-Bot
12585a8969 Upgrade AssertJ 3.26.3 -> 3.27.0 (#1472)
See:
- https://github.com/assertj/assertj/releases/tag/assertj-build-3.27.0
- https://github.com/assertj/assertj/compare/assertj-build-3.26.3...assertj-build-3.27.0
2024-12-25 11:29:40 +01:00
Picnic-DevPla-Bot
72b701cae3 Upgrade OpenRewrite Templating 1.20.1 -> 1.20.2 (#1478)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.20.2
- https://github.com/openrewrite/rewrite-templating/compare/v1.20.1...v1.20.2
2024-12-24 13:48:48 +01:00
Rick Ossendrijver
24e3251eb0 Introduce Prometheus Java Client integration test (#1468)
And document some possible future improvements.
2024-12-24 10:36:23 +01:00
Stephan Schroevers
f124749a4f Generalize AssertThatThrownBy Refaster rule (#1477)
By replacing it with the `AssertThatThrownByAsInstanceOfThrowable` and
`AssertThatThrownByIsInstanceOf` rules that are slightly more type-safe.
2024-12-23 17:33:42 +01:00
Stephan Schroevers
2a3d9c8dd5 Improve ExplicitArgumentEnumeration check (#1475)
Summary of changes:
- Also drop unnecessary `Immutable{List,Multiset,Set}#copyOf(E[])`
  invocations.
- Don't suggest simplifications that are likely to introduce unbounded
  recursion.
2024-12-23 10:15:27 +01:00
Philipp Zeipert
0fa59a5cec Introduce StreamMapFilter Refaster rule (#1467) 2024-12-23 09:38:43 +01:00
Stephan Schroevers
d316e8ac70 Improve JUnitMethodDeclaration method rename heuristics (#1476)
These changes make it less likely that the check suggests a method
rename that would yield invalid code.
2024-12-23 09:17:45 +01:00
Picnic-DevPla-Bot
53fe15c356 Upgrade OpenRewrite Templating 1.20.0 -> 1.20.1 (#1474)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.20.1
- https://github.com/openrewrite/rewrite-templating/compare/v1.20.0...v1.20.1
2024-12-22 13:35:25 +01:00
Rick Ossendrijver
acb8b651b7 Introduce AssertThatThrownBy*ExceptionRootCauseHasMessage Refaster rules (#1471) 2024-12-22 12:36:52 +01:00
Picnic-DevPla-Bot
ace6bc2813 Upgrade Spring Boot 3.4.0 -> 3.4.1 (#1473)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v3.4.1
- https://github.com/spring-projects/spring-boot/compare/v3.4.0...v3.4.1
2024-12-21 11:24:35 +01:00
Picnic-DevPla-Bot
f69fef2f52 Upgrade OpenRewrite 2.23.1 -> 2.23.2 (#1469)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v2.23.2
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.23.1...v2.23.2
2024-12-20 14:10:44 +01:00
Picnic-DevPla-Bot
d033b84b5b Upgrade Spring 6.2.0 -> 6.2.1 (#1459)
See:
- https://github.com/spring-projects/spring-framework/releases/tag/v6.2.1
- https://github.com/spring-projects/spring-framework/compare/v6.2.0...v6.2.1
2024-12-20 08:49:06 +01:00
Picnic-DevPla-Bot
3e702733bc Upgrade OpenRewrite Templating 1.19.1 -> 1.20.0 (#1470)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.20.0
- https://github.com/openrewrite/rewrite-templating/compare/v1.19.1...v1.20.0
2024-12-20 08:23:37 +01:00
Picnic-DevPla-Bot
f212e9476d Upgrade Guava 33.3.1-jre -> 33.4.0-jre (#1462)
See:
- https://guava.dev/releases/33.4.0-jre/api/diffs/
- https://github.com/google/guava/releases/tag/v33.4.0
- https://github.com/google/guava/compare/v33.3.1...v33.4.0
2024-12-19 12:46:23 +01:00
Picnic-DevPla-Bot
da00863e34 Upgrade pitest-maven-plugin 1.17.2 -> 1.17.3 (#1465)
See:
- https://github.com/hcoles/pitest/releases/tag/1.17.3
- https://github.com/hcoles/pitest/compare/1.17.2...1.17.3
2024-12-18 10:13:18 +01:00
Picnic-DevPla-Bot
476916f381 Upgrade JUnit 5 5.11.3 -> 5.11.4 (#1463)
See:
- https://junit.org/junit5/docs/current/release-notes/
- https://github.com/junit-team/junit5/releases/tag/r5.11.4
- https://github.com/junit-team/junit5/compare/r5.11.3...r5.11.4
2024-12-18 09:51:10 +01:00
Picnic-DevPla-Bot
67bf5b98a8 Upgrade actions/upload-artifact v4.4.3 -> v4.5.0 (#1466)
See:
- https://github.com/actions/upload-artifact/releases/tag/v4.5.0
2024-12-18 09:14:26 +01:00
Picnic-DevPla-Bot
db7757c556 Upgrade Spring Security 6.4.1 -> 6.4.2 (#1464)
See:
- https://github.com/spring-projects/spring-security/releases/tag/6.4.2
- https://github.com/spring-projects/spring-security/compare/6.4.1...6.4.2
2024-12-18 08:51:49 +01:00
Picnic-DevPla-Bot
bfd309800b Upgrade Checkstyle 10.20.2 -> 10.21.0 (#1460)
While there, also update the integration test.

See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.21.0
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.20.2...checkstyle-10.21.0
2024-12-18 08:41:28 +01:00
Picnic-DevPla-Bot
0121f7b33c Upgrade NullAway 0.12.1 -> 0.12.2 (#1458)
See:
- https://github.com/uber/NullAway/blob/master/CHANGELOG.md
- https://github.com/uber/NullAway/releases/tag/v0.12.2
- https://github.com/uber/NullAway/compare/v0.12.1...v0.12.2
2024-12-17 09:12:46 +01:00
Mohamed Sameh
8dbff50a8b Ignore type variable casts in ClassCastLambdaUsage check (#1449) 2024-12-16 15:47:36 +01:00
Picnic-DevPla-Bot
aec56ce025 Upgrade Byte Buddy 1.15.10 -> 1.15.11 (#1461)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.15.11
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.15.10...byte-buddy-1.15.11
2024-12-16 14:37:00 +01:00
Stephan Schroevers
1b8ffd86b9 Introduce ExplicitArgumentEnumeration check (#985) 2024-12-16 09:15:30 +01:00
Picnic-DevPla-Bot
ee7be7e3b2 Upgrade Swagger 2.2.26 -> 2.2.27 (#1457)
See:
- https://github.com/swagger-api/swagger-core/releases/tag/v2.2.27
- https://github.com/swagger-api/swagger-core/compare/v2.2.26...v2.2.27
2024-12-13 09:49:29 +01:00
Picnic-DevPla-Bot
57825decf0 Upgrade Google Java Format 1.25.1 -> 1.25.2 (#1456)
See:
- https://github.com/google/google-java-format/releases/tag/v1.25.2
- https://github.com/google/google-java-format/compare/v1.25.1...v1.25.2
2024-12-13 09:34:46 +01:00
Stephan Schroevers
2b3e11bd24 Make integration tests Error Prone 2.36.0-compatible (#1455) 2024-12-12 09:12:23 +01:00
Picnic-DevPla-Bot
23cb29b6fc Upgrade Project Reactor 2024.0.0 -> 2024.0.1 (#1454)
See:
- https://github.com/reactor/reactor/releases/tag/2024.0.1
- https://github.com/reactor/reactor/compare/2024.0.0...2024.0.1
2024-12-11 14:46:21 +01:00
Picnic-DevPla-Bot
ba1dd2cd08 Upgrade Micrometer 1.14.1 -> 1.14.2 (#1453)
See:
- https://github.com/micrometer-metrics/micrometer/releases/tag/v1.14.2
- https://github.com/micrometer-metrics/micrometer/compare/v1.14.1...v1.14.2
2024-12-11 11:58:48 +01:00
Picnic-DevPla-Bot
75a9786f8f Upgrade Google Java Format 1.25.0 -> 1.25.1 (#1452)
See:
- https://github.com/google/google-java-format/releases/tag/v1.25.1
- https://github.com/google/google-java-format/compare/v1.25.0...v1.25.1
2024-12-10 14:16:55 +01:00
Picnic-DevPla-Bot
0b4fd8ddd1 Upgrade org.codehaus.mojo:license-maven-plugin 2.4.0 -> 2.5.0 (#1445)
See:
- https://github.com/mojohaus/license-maven-plugin/releases/tag/2.5.0
- https://github.com/mojohaus/license-maven-plugin/compare/2.4.0...2.5.0
2024-12-10 08:52:57 +01:00
Phil Werli
2eda393c03 Introduce PublisherProbeAssertWas{,Not}{Subscribed,Cancelled,Requested} Refaster rules (#1423)
While there, fix a typo in `README.md`.
2024-12-09 14:25:06 +01:00
Picnic-DevPla-Bot
512a3bebad Upgrade Error Prone 2.35.1 -> 2.36.0 (#1429)
And update the compatibility matrix and its generation script.

See:
- https://github.com/google/error-prone/releases/tag/v2.36.0
- https://github.com/google/error-prone/compare/v2.35.1...v2.36.0
- https://github.com/PicnicSupermarket/error-prone/compare/v2.35.1-picnic-1...v2.36.0-picnic-1
2024-12-09 12:20:52 +01:00
Stephan Schroevers
db18d6a1fc Drop the JavaKeywords class (#1442)
Instead:
- Move its sole used method `#isValidIdentifier` to the `SourceCode`
  class.
- Delegate most of said method's implementation to
  `javax.lang.model.SourceVersion`.
2024-12-09 09:26:56 +01:00
Picnic-DevPla-Bot
75ca3030f7 Upgrade OpenRewrite Templating 1.17.1 -> 1.19.1 (#1437)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.18.0
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.19.0
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.19.1
- https://github.com/openrewrite/rewrite-templating/compare/v1.17.1...v1.19.1
2024-12-09 08:49:31 +01:00
Picnic-DevPla-Bot
f484c3f10b Upgrade Micrometer 1.14.0 -> 1.14.1 (#1439)
See:
- https://github.com/micrometer-metrics/micrometer/releases/tag/v1.14.1
- https://github.com/micrometer-metrics/micrometer/compare/v1.14.0...v1.14.1
2024-12-08 20:23:43 +01:00
Picnic-DevPla-Bot
0753de05d1 Upgrade Checkstyle 10.20.1 -> 10.20.2 (#1443)
While there, also update the integration test.

See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.20.2
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.20.1...checkstyle-10.20.2
2024-12-08 19:21:16 +01:00
Picnic-DevPla-Bot
ab0847e49f Upgrade Checker Framework Annotations 3.48.2 -> 3.48.3 (#1448)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.48.3
- https://github.com/typetools/checker-framework/compare/checker-framework-3.48.2...checker-framework-3.48.3
2024-12-08 17:00:18 +01:00
Picnic-DevPla-Bot
f9383e4e94 Upgrade OpenRewrite 2.22.0 -> 2.23.1 (#1440)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v2.23.0
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v2.23.1
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.22.0...v2.23.1
2024-12-08 16:08:46 +01:00
Picnic-DevPla-Bot
50b6c31675 Upgrade Jackson 2.18.1 -> 2.18.2 (#1441)
See:
- https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.18.2
- https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.18.1...jackson-bom-2.18.2
2024-12-08 15:35:42 +01:00
Picnic-DevPla-Bot
63e78933ac Upgrade pitest-maven-plugin 1.17.1 -> 1.17.2 (#1444)
See:
- https://github.com/hcoles/pitest/releases/tag/1.17.2
- https://github.com/hcoles/pitest/compare/1.17.1...1.17.2
2024-12-08 15:22:23 +01:00
Picnic-DevPla-Bot
cd0e962ef7 Upgrade s4u/setup-maven-action v1.16.0 -> v1.17.0 (#1451)
See:
- https://github.com/s4u/setup-maven-action/releases/tag/v1.17.0
2024-12-08 15:04:27 +01:00
Picnic-DevPla-Bot
107d135894 Upgrade maven-javadoc-plugin 3.11.1 -> 3.11.2 (#1450)
See:
- https://issues.apache.org/jira/issues/?jql=project%20%3D%20MJAVADOC%20AND%20fixVersion%20%3E%203.11.1%20AND%20fixVersion%20%3C%3D%203.11.2
- https://github.com/apache/maven-javadoc-plugin/releases/tag/maven-javadoc-plugin-3.11.2
- https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.1...maven-javadoc-plugin-3.11.2
2024-12-08 14:51:46 +01:00
137 changed files with 17791 additions and 3359 deletions

View File

@@ -14,11 +14,11 @@ jobs:
distribution: [ temurin ]
experimental: [ false ]
include:
- os: macos-14
- os: macos-15
jdk: 17.0.13
distribution: temurin
experimental: false
- os: windows-2022
- os: windows-2025
jdk: 17.0.13
distribution: temurin
experimental: false
@@ -26,14 +26,16 @@ jobs:
continue-on-error: ${{ matrix.experimental }}
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
with:
disable-sudo: true
egress-policy: block
allowed-endpoints: >
api.adoptium.net:443
github.com:443
github-registry-files.githubusercontent.com:443
jitpack.io:443
maven.pkg.github.com:443
objects.githubusercontent.com:443
repo.maven.apache.org:443
# We run the build twice for each supported JDK: once against the
@@ -42,7 +44,7 @@ jobs:
# additionally enabling all checks defined in this project and any Error
# Prone checks available only from other artifact repositories.
- name: Check out code and set up JDK and Maven
uses: s4u/setup-maven-action@382542f77617f34e56bf83868920a4d45b7451e7 # v1.16.0
uses: s4u/setup-maven-action@4f7fb9d9675e899ca81c6161dadbba0189a4ebb1 # v1.18.0
with:
java-version: ${{ matrix.jdk }}
java-distribution: ${{ matrix.distribution }}
@@ -53,6 +55,8 @@ jobs:
run: mvn -T1C install javadoc:jar
- name: Build project with self-check against Error Prone fork
run: mvn -T1C clean verify -Perror-prone-fork -Pnon-maven-central -Pself-check -s settings.xml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Remove installed project artifacts
run: mvn dependency:purge-local-repository -DmanualInclude='${project.groupId}' -DresolutionFuzziness=groupId

View File

@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
with:
disable-sudo: true
egress-policy: block
@@ -34,19 +34,19 @@ jobs:
repo.maven.apache.org:443
uploads.github.com:443
- name: Check out code and set up JDK and Maven
uses: s4u/setup-maven-action@382542f77617f34e56bf83868920a4d45b7451e7 # v1.16.0
uses: s4u/setup-maven-action@4f7fb9d9675e899ca81c6161dadbba0189a4ebb1 # v1.18.0
with:
java-version: 17.0.13
java-distribution: temurin
maven-version: 3.9.9
- name: Initialize CodeQL
uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
uses: github/codeql-action/init@5f8171a638ada777af81d42b55959a643bb29017 # v3.28.12
with:
languages: ${{ matrix.language }}
- name: Perform minimal build
if: matrix.language == 'java'
run: mvn -T1C clean package -DskipTests -Dverification.skip
- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
uses: github/codeql-action/analyze@5f8171a638ada777af81d42b55959a643bb29017 # v3.28.12
with:
category: /language:${{ matrix.language }}

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
with:
disable-sudo: true
egress-policy: block
@@ -21,6 +21,7 @@ jobs:
api.github.com:443
bestpractices.coreinfrastructure.org:443
blog.picnic.nl:443
docs.github.com:443
errorprone.info:443
github.com:443
img.shields.io:443
@@ -42,7 +43,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: ruby/setup-ruby@a2bbe5b1b236842c1cb7dd11e8e3b51e0a616acc # v1.202.0
- uses: ruby/setup-ruby@1a615958ad9d422dd932dc1d5823942ee002799f # v1.227.0
with:
working-directory: ./website
bundler-cache: true
@@ -74,7 +75,7 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
with:
disable-sudo: true
egress-policy: block

View File

@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
with:
disable-sudo: true
egress-policy: block
@@ -42,12 +42,12 @@ jobs:
with:
persist-credentials: false
- name: Run OpenSSF Scorecard analysis
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
with:
results_file: results.sarif
results_format: sarif
publish_results: ${{ github.ref == 'refs/heads/master' }}
- name: Update GitHub's code scanning dashboard
uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
uses: github/codeql-action/upload-sarif@5f8171a638ada777af81d42b55959a643bb29017 # v3.28.12
with:
sarif_file: results.sarif

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
with:
disable-sudo: true
egress-policy: block
@@ -22,7 +22,7 @@ jobs:
objects.githubusercontent.com:443
repo.maven.apache.org:443
- name: Check out code and set up JDK and Maven
uses: s4u/setup-maven-action@382542f77617f34e56bf83868920a4d45b7451e7 # v1.16.0
uses: s4u/setup-maven-action@4f7fb9d9675e899ca81c6161dadbba0189a4ebb1 # v1.18.0
with:
checkout-fetch-depth: 2
java-version: 17.0.13
@@ -38,7 +38,7 @@ jobs:
- name: Aggregate Pitest reports
run: mvn pitest-git:aggregate -DkilledEmoji=":tada:" -DmutantEmoji=":zombie:" -DtrailingText="Mutation testing report by [Pitest](https://pitest.org/). Review any surviving mutants by inspecting the line comments under [_Files changed_](${{ github.event.number }}/files)."
- name: Upload Pitest reports as artifact
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: pitest-reports
path: ./target/pit-reports-ci

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
with:
disable-sudo: true
egress-policy: block
@@ -31,7 +31,7 @@ jobs:
objects.githubusercontent.com:443
repo.maven.apache.org:443
- name: Check out code and set up JDK and Maven
uses: s4u/setup-maven-action@382542f77617f34e56bf83868920a4d45b7451e7 # v1.16.0
uses: s4u/setup-maven-action@4f7fb9d9675e899ca81c6161dadbba0189a4ebb1 # v1.18.0
with:
java-version: 17.0.13
java-distribution: temurin

View File

@@ -19,25 +19,31 @@ jobs:
runs-on: ubuntu-24.04
strategy:
matrix:
integration-test: [ "checkstyle", "metrics" ]
integration-test: [ "checkstyle", "metrics", "prometheus-java-client" ]
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
with:
disable-sudo: true
egress-policy: block
# XXX: After updating the validation build flags in
# `integration-tests/prometheus-java-client.sh`, review whether the
# Docker domains specified here can be dropped.
allowed-endpoints: >
api.adoptium.net:443
auth.docker.io:443
checkstyle.org:443
example.com:80
github.com:443
objects.githubusercontent.com:443
oss.sonatype.org:443
production.cloudflare.docker.com:443
raw.githubusercontent.com:443
registry-1.docker.io:443
repo.maven.apache.org:443
repository.sonatype.org:443
- name: Check out code and set up JDK and Maven
uses: s4u/setup-maven-action@382542f77617f34e56bf83868920a4d45b7451e7 # v1.16.0
uses: s4u/setup-maven-action@4f7fb9d9675e899ca81c6161dadbba0189a4ebb1 # v1.18.0
with:
checkout-ref: "refs/pull/${{ github.event.issue.number }}/head"
java-version: 17.0.13
@@ -49,7 +55,7 @@ jobs:
run: xvfb-run "./integration-tests/${{ matrix.integration-test }}.sh" "${{ runner.temp }}/artifacts"
- name: Upload artifacts on failure
if: ${{ failure() }}
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: "integration-test-${{ matrix.integration-test }}"
path: "${{ runner.temp }}/artifacts"

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
with:
disable-sudo: true
egress-policy: block
@@ -35,7 +35,7 @@ jobs:
*.sonarcloud.io:443
sonarcloud.io:443
- name: Check out code and set up JDK and Maven
uses: s4u/setup-maven-action@382542f77617f34e56bf83868920a4d45b7451e7 # v1.16.0
uses: s4u/setup-maven-action@4f7fb9d9675e899ca81c6161dadbba0189a4ebb1 # v1.18.0
with:
checkout-fetch-depth: 0
java-version: 17.0.13

View File

@@ -205,19 +205,20 @@ Relevant Maven build parameters:
version of Error Prone. This is useful e.g. when testing a locally built
Error Prone SNAPSHOT.
- `-Perror-prone-fork` runs the build using Picnic's [Error Prone
fork][error-prone-fork-repo], hosted on [Jitpack][error-prone-fork-jitpack].
This fork generally contains a few changes on top of the latest Error Prone
release.
fork][error-prone-fork-repo], hosted using [GitHub
Packages][error-prone-fork-packages]. This fork generally contains a few
changes on top of the latest Error Prone release. Using this profile
generally requires passing `-s settings.xml`, with [suitably
configured][github-packages-auth] `GITHUB_ACTOR` and `GITHUB_TOKEN`
environment variables.
- `-Pself-check` runs the checks defined by this project against itself.
Pending a release of [google/error-prone#3301][error-prone-pull-3301], this
flag must currently be used in combination with `-Perror-prone-fork`.
Other highly relevant commands:
- `mvn fmt:format` formats the code using
[`google-java-format`][google-java-format].
- [`./run-full-build.sh`][script-run-full-build] builds the project twice,
where the second pass validates compatbility with Picnic's [Error Prone
where the second pass validates compatibility with Picnic's [Error Prone
fork][error-prone-fork-repo] and compliance of the code with any rules
defined within this project. (Consider running this before [opening a pull
request][contributing-pull-request], as the PR checks also perform this
@@ -235,8 +236,9 @@ Other highly relevant commands:
against _all_ code in the current working directory. For more information
check the [PIT Maven plugin][pitest-maven].
When running the project's tests in IntelliJ IDEA, you might see the following
error:
Opening the project in IntelliJ IDEA may require running `mvn clean install`
first. Additionally, when running the project's tests using the IDE, you might
see the following error:
```
java: exporting a package from system module jdk.compiler is not allowed with --release
@@ -274,14 +276,14 @@ channel; please see our [security policy][security] for details.
[contributing]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/CONTRIBUTING.md
[contributing-pull-request]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/CONTRIBUTING.md#-opening-a-pull-request
[error-prone-bugchecker]: https://github.com/google/error-prone/blob/master/check_api/src/main/java/com/google/errorprone/bugpatterns/BugChecker.java
[error-prone-fork-jitpack]: https://jitpack.io/#PicnicSupermarket/error-prone
[error-prone-fork-packages]: https://github.com/PicnicSupermarket/error-prone/packages
[error-prone-fork-repo]: https://github.com/PicnicSupermarket/error-prone
[error-prone-gradle-installation-guide]: https://github.com/tbroyer/gradle-errorprone-plugin
[error-prone-installation-guide]: https://errorprone.info/docs/installation#maven
[error-prone-orig-repo]: https://github.com/google/error-prone
[error-prone-pull-3301]: https://github.com/google/error-prone/pull/3301
[github-actions-build-badge]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/build.yml/badge.svg
[github-actions-build-master]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/build.yml?query=branch:master&event=push
[github-packages-auth]: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry#authenticating-to-github-packages
[google-java-format]: https://github.com/google/google-java-format
[idea-288052]: https://youtrack.jetbrains.com/issue/IDEA-288052
[license-badge]: https://img.shields.io/github/license/PicnicSupermarket/error-prone-support

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.19.2-SNAPSHOT</version>
<version>0.21.1-SNAPSHOT</version>
</parent>
<artifactId>documentation-support</artifactId>
@@ -15,24 +15,6 @@
<url>https://error-prone.picnic.tech</url>
<dependencies>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotation</artifactId>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_check_api</artifactId>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_test_helpers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>error-prone-utils</artifactId>
@@ -72,6 +54,24 @@
<artifactId>auto-value-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotation</artifactId>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_check_api</artifactId>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_test_helpers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.19.2-SNAPSHOT</version>
<version>0.21.1-SNAPSHOT</version>
</parent>
<artifactId>error-prone-contrib</artifactId>
@@ -15,31 +15,6 @@
<url>https://error-prone.picnic.tech</url>
<dependencies>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotation</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_check_api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_test_helpers</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>documentation-support</artifactId>
@@ -87,6 +62,31 @@
<artifactId>auto-value-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotation</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_check_api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_test_helpers</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
@@ -167,6 +167,11 @@
<artifactId>value-annotations</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>

View File

@@ -18,6 +18,7 @@ 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 javax.lang.model.type.TypeKind;
import tech.picnic.errorprone.utils.SourceCode;
/**
@@ -48,11 +49,14 @@ public final class ClassCastLambdaUsage extends BugChecker implements LambdaExpr
}
Type type = ASTHelpers.getType(typeCast);
if (type == null || type.isParameterized() || type.isPrimitive()) {
if (type == null
|| type.isParameterized()
|| type.isPrimitive()
|| type.getKind() == TypeKind.TYPEVAR) {
/*
* 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.
* The method reference syntax does not support casting to parameterized types, and type
* variables aren't supported either. 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.

View File

@@ -0,0 +1,325 @@
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.PERFORMANCE;
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
import static com.google.errorprone.matchers.Matchers.staticMethod;
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import com.google.common.base.VerifyException;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.Var;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import java.util.Collections;
import java.util.Formattable;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import tech.picnic.errorprone.utils.SourceCode;
/**
* A {@link BugChecker} that flags {@link String#format} and {@link String#formatted} invocations
* that can be omitted by delegating to another format method.
*/
// XXX: The special-casing of Throwable applies only to SLF4J 1.6.0+; see
// https://www.slf4j.org/faq.html#paramException. That should be documented.
// XXX: Some of the `Matcher`s defined here are also declared by the `Slf4jLogStatement` and
// `RedundantStringConversion` checks. Look into deduplicating them.
// XXX: Should we also simplify e.g. `LOG.error(String.join("sep", arg1, arg2), throwable)`? Perhaps
// that's too obscure.
// XXX: This check currently only flags string format expressions that are a direct argument to
// another format-capable method invocation. Indirect cases, such as where the result is assigned to
// a variable, are currently not covered.
@AutoService(BugChecker.class)
@BugPattern(
summary = "String formatting can be deferred",
link = BUG_PATTERNS_BASE_URL + "EagerStringFormatting",
linkType = CUSTOM,
severity = WARNING,
tags = {PERFORMANCE, SIMPLIFICATION})
public final class EagerStringFormatting extends BugChecker implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> FORMATTABLE = isSubtypeOf(Formattable.class);
private static final Matcher<ExpressionTree> LOCALE = isSubtypeOf(Locale.class);
private static final Matcher<ExpressionTree> SLF4J_MARKER = isSubtypeOf("org.slf4j.Marker");
private static final Matcher<ExpressionTree> THROWABLE = isSubtypeOf(Throwable.class);
private static final Matcher<ExpressionTree> REQUIRE_NON_NULL_INVOCATION =
staticMethod().onClass(Objects.class.getCanonicalName()).named("requireNonNull");
private static final Matcher<ExpressionTree> GUAVA_GUARD_INVOCATION =
anyOf(
staticMethod()
.onClass(Preconditions.class.getCanonicalName())
.namedAnyOf("checkArgument", "checkNotNull", "checkState"),
staticMethod()
.onClass(Verify.class.getCanonicalName())
.namedAnyOf("verify", "verifyNotNull"));
private static final Matcher<ExpressionTree> SLF4J_LOGGER_INVOCATION =
instanceMethod()
.onDescendantOf("org.slf4j.Logger")
.namedAnyOf("trace", "debug", "info", "warn", "error");
private static final Matcher<ExpressionTree> STATIC_FORMAT_STRING =
staticMethod().onClass(String.class.getCanonicalName()).named("format");
private static final Matcher<ExpressionTree> INSTANCE_FORMAT_STRING =
instanceMethod().onDescendantOf(String.class.getCanonicalName()).named("formatted");
private static final String MESSAGE_NEVER_NULL_ARGUMENT =
"String formatting never yields `null` expression";
/** Instantiates a new {@link EagerStringFormatting} instance. */
public EagerStringFormatting() {}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
Tree parent = state.getPath().getParentPath().getLeaf();
if (!(parent instanceof MethodInvocationTree methodInvocation)) {
/*
* Fast path: this isn't a method invocation whose result is an argument to another method
* invocation.
*/
// XXX: This logic assumes that the string format operation isn't redundantly wrapped in
// parentheses. Similar assumptions likely exist throughout the code base. Investigate how to
// structurally cover such cases.
return Description.NO_MATCH;
}
return StringFormatExpression.tryCreate(tree, state)
.map(expr -> analyzeFormatStringContext(expr, methodInvocation, state))
.orElse(Description.NO_MATCH);
}
private Description analyzeFormatStringContext(
StringFormatExpression stringFormat, MethodInvocationTree context, VisitorState state) {
if (REQUIRE_NON_NULL_INVOCATION.matches(context, state)) {
return analyzeRequireNonNullStringFormatContext(stringFormat, context);
}
if (GUAVA_GUARD_INVOCATION.matches(context, state)) {
return analyzeGuavaGuardStringFormatContext(stringFormat, context, state);
}
if (SLF4J_LOGGER_INVOCATION.matches(context, state)) {
return analyzeSlf4jLoggerStringFormatContext(stringFormat, context, state);
}
/*
* The string formatting operation does not appear to happen in a context that admits of
* simplification or optimization.
*/
return Description.NO_MATCH;
}
private Description analyzeRequireNonNullStringFormatContext(
StringFormatExpression stringFormat, MethodInvocationTree context) {
List<? extends ExpressionTree> arguments = context.getArguments();
if (arguments.size() != 2 || arguments.get(0).equals(stringFormat.expression())) {
/* Vacuous validation that string formatting doesn't yield `null`. */
return buildDescription(context).setMessage(MESSAGE_NEVER_NULL_ARGUMENT).build();
}
if (stringFormat.arguments().stream()
.anyMatch(EagerStringFormatting::isNonFinalLocalVariable)) {
/*
* The format operation depends on a variable that isn't final or effectively final; moving
* it into a lambda expression would cause a compilation error.
*/
return buildDescription(context)
.setMessage(message() + " (but this requires introducing an effectively final variable)")
.build();
}
/* Suggest that the string formatting is deferred. */
return describeMatch(context, SuggestedFix.prefixWith(stringFormat.expression(), "() -> "));
}
private Description analyzeGuavaGuardStringFormatContext(
StringFormatExpression stringFormat, MethodInvocationTree context, VisitorState state) {
List<? extends ExpressionTree> arguments = context.getArguments();
if (arguments.get(0).equals(stringFormat.expression())) {
/*
* Vacuous `checkNotNull` or `verifyNotNull` validation that string formatting doesn't yield
* `null`.
*/
return buildDescription(context).setMessage(MESSAGE_NEVER_NULL_ARGUMENT).build();
}
if (stringFormat.simplifiableFormatString().isEmpty() || arguments.size() > 2) {
/*
* The format string cannot be simplified, or the format string produces a format string
* itself, or its result is the input to another format operation. These are complex cases
* that we'll only flag.
*/
return createSimplificationSuggestion(context, "Guava");
}
return describeMatch(context, stringFormat.suggestFlattening("%s", state));
}
private Description analyzeSlf4jLoggerStringFormatContext(
StringFormatExpression stringFormat, MethodInvocationTree context, VisitorState state) {
if (stringFormat.simplifiableFormatString().isEmpty()) {
/* We can't simplify this case; only flag it. */
return createSimplificationSuggestion(context, "SLF4J");
}
List<? extends ExpressionTree> arguments = context.getArguments();
int leftOffset = SLF4J_MARKER.matches(arguments.get(0), state) ? 1 : 0;
int rightOffset = THROWABLE.matches(arguments.get(arguments.size() - 1), state) ? 1 : 0;
if (arguments.size() != leftOffset + 1 + rightOffset) {
/*
* The format string produces a format string itself, or its result is the input to another
* format operation. This is a complex case that we'll only flag.
*/
return createSimplificationSuggestion(context, "SLF4J");
}
return describeMatch(context, stringFormat.suggestFlattening("{}", state));
}
private static boolean isNonFinalLocalVariable(Tree tree) {
Symbol symbol = ASTHelpers.getSymbol(tree);
return symbol instanceof VarSymbol
&& symbol.owner instanceof MethodSymbol
&& !ASTHelpers.isConsideredFinal(symbol);
}
private Description createSimplificationSuggestion(MethodInvocationTree context, String library) {
return buildDescription(context)
.setMessage(
"%s (assuming that %s's simplified formatting support suffices)"
.formatted(message(), library))
.build();
}
/** Description of a string format expression. */
@AutoValue
abstract static class StringFormatExpression {
/** The full string format expression. */
abstract MethodInvocationTree expression();
/** The format string expression. */
abstract Tree formatString();
/** The string format arguments to be plugged into its format string. */
abstract ImmutableList<ExpressionTree> arguments();
/**
* The constant format string, if it contains only {@code %s} placeholders, and the number of
* said placeholders matches the number of format arguments.
*/
abstract Optional<String> simplifiableFormatString();
private SuggestedFix suggestFlattening(String newPlaceholder, VisitorState state) {
return SuggestedFix.replace(
expression(),
Stream.concat(
Stream.of(deriveFormatStringExpression(newPlaceholder, state)),
arguments().stream().map(arg -> SourceCode.treeToString(arg, state)))
.collect(joining(", ")));
}
private String deriveFormatStringExpression(String newPlaceholder, VisitorState state) {
String formatString =
String.format(
simplifiableFormatString()
.orElseThrow(() -> new VerifyException("Format string cannot be simplified")),
Collections.nCopies(arguments().size(), newPlaceholder).toArray());
/*
* If the suggested replacement format string is the same as the original, then use the
* expression's existing source code representation. This way string constant references are
* not unnecessarily replaced.
*/
return formatString.equals(ASTHelpers.constValue(formatString(), String.class))
? SourceCode.treeToString(formatString(), state)
: SourceCode.toStringConstantExpression(formatString, state);
}
private static Optional<StringFormatExpression> tryCreate(
MethodInvocationTree tree, VisitorState state) {
if (INSTANCE_FORMAT_STRING.matches(tree, state)) {
return Optional.of(
create(
tree,
requireNonNull(ASTHelpers.getReceiver(tree), "Receiver unexpectedly absent"),
ImmutableList.copyOf(tree.getArguments()),
state));
}
if (STATIC_FORMAT_STRING.matches(tree, state)) {
List<? extends ExpressionTree> arguments = tree.getArguments();
int argOffset = LOCALE.matches(arguments.get(0), state) ? 1 : 0;
return Optional.of(
create(
tree,
arguments.get(argOffset),
ImmutableList.copyOf(arguments.subList(argOffset + 1, arguments.size())),
state));
}
return Optional.empty();
}
private static StringFormatExpression create(
MethodInvocationTree expression,
Tree formatString,
ImmutableList<ExpressionTree> arguments,
VisitorState state) {
return new AutoValue_EagerStringFormatting_StringFormatExpression(
expression,
formatString,
arguments,
Optional.ofNullable(ASTHelpers.constValue(formatString, String.class))
.filter(template -> isSimplifiable(template, arguments, state)));
}
private static boolean isSimplifiable(
String formatString, ImmutableList<ExpressionTree> arguments, VisitorState state) {
if (arguments.stream().anyMatch(arg -> FORMATTABLE.matches(arg, state))) {
/* `Formattable` arguments can have arbitrary format semantics. */
return false;
}
@Var int placeholderCount = 0;
for (int p = formatString.indexOf('%'); p != -1; p = formatString.indexOf('%', p + 2)) {
if (p == formatString.length() - 1) {
/* Malformed format string with trailing `%`. */
return false;
}
char modifier = formatString.charAt(p + 1);
if (modifier == 's') {
placeholderCount++;
} else if (modifier != '%') {
/* Only `%s` and `%%` (a literal `%`) are supported. */
return false;
}
}
return placeholderCount == arguments.size();
}
}
}

View File

@@ -0,0 +1,233 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
import static com.google.errorprone.BugPattern.StandardTags.PERFORMANCE;
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.instanceMethod;
import static com.google.errorprone.matchers.Matchers.staticMethod;
import static com.google.errorprone.matchers.Matchers.symbolMatcher;
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.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableTable;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.fixes.SuggestedFixes;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.util.ASTHelpers;
import com.google.errorprone.util.Visibility;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import javax.lang.model.element.Element;
import tech.picnic.errorprone.utils.SourceCode;
/**
* A {@link BugChecker} that flags single-argument method invocations with an iterable of explicitly
* enumerated values, for which a semantically equivalent varargs variant (appears to) exists as
* well.
*
* <p>This check drops selected {@link ImmutableSet#of} and {@link Set#of} invocations, with the
* assumption that these operations do not deduplicate the collection of explicitly enumerated
* values. It also drops {@link ImmutableMultiset#of} and {@link Set#of} invocations, with the
* assumption that these do not materially impact iteration order.
*
* <p>This checker attempts to identify {@link Iterable}-accepting methods for which a varargs
* overload exists, and suggests calling the varargs overload instead. This is an imperfect
* heuristic, but it e.g. allows invocations of <a
* href="https://immutables.github.io/immutable.html#copy-methods">Immutables-generated {@code
* with*}</a> methods to be simplified.
*/
@AutoService(BugChecker.class)
@BugPattern(
summary = "Iterable creation can be avoided by using a varargs alternative method",
link = BUG_PATTERNS_BASE_URL + "ExplicitArgumentEnumeration",
linkType = CUSTOM,
severity = SUGGESTION,
tags = {PERFORMANCE, SIMPLIFICATION})
public final class ExplicitArgumentEnumeration extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> EXPLICIT_ITERABLE_CREATOR =
anyOf(
staticMethod()
.onClassAny(
ImmutableList.class.getCanonicalName(),
ImmutableMultiset.class.getCanonicalName(),
ImmutableSet.class.getCanonicalName(),
List.class.getCanonicalName(),
Set.class.getCanonicalName())
.named("of"),
allOf(
staticMethod()
.onClassAny(
ImmutableList.class.getCanonicalName(),
ImmutableMultiset.class.getCanonicalName(),
ImmutableSet.class.getCanonicalName())
.named("copyOf"),
symbolMatcher(
(symbol, state) ->
state
.getSymtab()
.arrayClass
.equals(((MethodSymbol) symbol).params().get(0).type.tsym))),
staticMethod().onClass(Arrays.class.getCanonicalName()).named("asList"));
private static final Matcher<ExpressionTree> IMMUTABLE_COLLECTION_BUILDER =
instanceMethod().onDescendantOf(ImmutableCollection.Builder.class.getCanonicalName());
private static final Matcher<ExpressionTree> OBJECT_ENUMERABLE_ASSERT =
instanceMethod().onDescendantOf("org.assertj.core.api.ObjectEnumerableAssert");
private static final Matcher<ExpressionTree> STEP_VERIFIER_STEP =
instanceMethod().onDescendantOf("reactor.test.StepVerifier.Step");
private static final ImmutableTable<Matcher<ExpressionTree>, String, String> ALTERNATIVE_METHODS =
ImmutableTable.<Matcher<ExpressionTree>, String, String>builder()
.put(IMMUTABLE_COLLECTION_BUILDER, "addAll", "add")
.put(OBJECT_ENUMERABLE_ASSERT, "containsAnyElementsOf", "containsAnyOf")
.put(OBJECT_ENUMERABLE_ASSERT, "containsAll", "contains")
.put(OBJECT_ENUMERABLE_ASSERT, "containsExactlyElementsOf", "containsExactly")
.put(
OBJECT_ENUMERABLE_ASSERT,
"containsExactlyInAnyOrderElementsOf",
"containsExactlyInAnyOrder")
.put(OBJECT_ENUMERABLE_ASSERT, "containsOnlyElementsOf", "containsOnly")
.put(OBJECT_ENUMERABLE_ASSERT, "containsOnlyOnceElementsOf", "containsOnlyOnce")
.put(OBJECT_ENUMERABLE_ASSERT, "doesNotContainAnyElementsOf", "doesNotContain")
.put(OBJECT_ENUMERABLE_ASSERT, "hasSameElementsAs", "containsOnly")
.put(STEP_VERIFIER_STEP, "expectNextSequence", "expectNext")
.buildOrThrow();
/** Instantiates a new {@link ExplicitArgumentEnumeration} instance. */
public ExplicitArgumentEnumeration() {}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (tree.getArguments().size() != 1) {
/* Performance optimization: non-unary method invocations cannot be simplified. */
return Description.NO_MATCH;
}
MethodSymbol method = ASTHelpers.getSymbol(tree);
if (!isUnaryIterableAcceptingMethod(method, state) || isLocalOverload(method, state)) {
/*
* This isn't a method invocation we can simplify, or it's an invocation of a local overload.
* The latter type of invocation we do not suggest replacing, as this is fairly likely to
* introduce an unbounded recursive call chain.
*/
return Description.NO_MATCH;
}
ExpressionTree argument = tree.getArguments().get(0);
if (!EXPLICIT_ITERABLE_CREATOR.matches(argument, state)) {
return Description.NO_MATCH;
}
return trySuggestCallingVarargsOverload(method, (MethodInvocationTree) argument, state)
.or(() -> trySuggestCallingCustomAlternative(tree, (MethodInvocationTree) argument, state))
.map(fix -> describeMatch(tree, fix))
.orElse(Description.NO_MATCH);
}
private static boolean isUnaryIterableAcceptingMethod(MethodSymbol method, VisitorState state) {
List<VarSymbol> params = method.params();
return !method.isVarArgs()
&& params.size() == 1
&& ASTHelpers.isSubtype(params.get(0).type, state.getSymtab().iterableType, state);
}
private static boolean isLocalOverload(MethodSymbol calledMethod, VisitorState state) {
MethodTree enclosingMethod = state.findEnclosing(MethodTree.class);
if (enclosingMethod == null) {
return false;
}
MethodSymbol callingMethod = ASTHelpers.getSymbol(enclosingMethod);
return Objects.equals(callingMethod.getEnclosingElement(), calledMethod.getEnclosingElement())
&& callingMethod.getSimpleName().equals(calledMethod.getSimpleName());
}
private static Optional<SuggestedFix> trySuggestCallingVarargsOverload(
MethodSymbol method, MethodInvocationTree argument, VisitorState state) {
/*
* Collect all overloads of the given method that we are sure to be able to call. Note that the
* `isAtLeastAsVisible` check is conservative heuristic.
*/
ImmutableList<MethodSymbol> overloads =
ASTHelpers.matchingMethods(
method.getSimpleName(),
m -> isAtLeastAsVisible(m, method),
method.enclClass().type,
state.getTypes())
.collect(toImmutableList());
/*
* If all overloads have a single parameter, and at least one of them is a varargs method, then
* we assume that unwrapping the iterable argument will cause a suitable overload to be invoked.
* (Note that there may be multiple varargs overloads, either with different parameter types, or
* due to method overriding; this check does not attempt to determine which exact method or
* overload will be invoked as a result of the suggested simplification.)
*
* Note that this is a (highly!) imperfect heuristic, but it is sufficient to prevent e.g.
* unwrapping of arguments to `org.jooq.impl.DSL#row`, which can cause the expression's return
* type to change from `RowN` to (e.g.) `Row2`.
*/
// XXX: There are certainly cases where it _would_ be nice to unwrap the arguments to
// `org.jooq.impl.DSL#row(Collection<?>)`. Look into this.
// XXX: Ideally we do check that one of the overloads accepts the unwrapped arguments.
// XXX: Ideally we validate that eligible overloads have compatible return types.
boolean hasLikelySuitableVarargsOverload =
overloads.stream().allMatch(m -> m.params().size() == 1)
&& overloads.stream().anyMatch(MethodSymbol::isVarArgs);
return hasLikelySuitableVarargsOverload
? Optional.of(SourceCode.unwrapMethodInvocation(argument, state))
: Optional.empty();
}
private static Optional<SuggestedFix> trySuggestCallingCustomAlternative(
MethodInvocationTree tree, MethodInvocationTree argument, VisitorState state) {
return ALTERNATIVE_METHODS.rowMap().entrySet().stream()
.filter(e -> e.getKey().matches(tree, state))
.findFirst()
.flatMap(e -> trySuggestCallingCustomAlternative(tree, argument, state, e.getValue()));
}
private static Optional<SuggestedFix> trySuggestCallingCustomAlternative(
MethodInvocationTree tree,
MethodInvocationTree argument,
VisitorState state,
Map<String, String> alternatives) {
return Optional.ofNullable(
alternatives.get(ASTHelpers.getSymbol(tree).getSimpleName().toString()))
.map(
replacement ->
SuggestedFix.builder()
.merge(SuggestedFixes.renameMethodInvocation(tree, replacement, state))
.merge(SourceCode.unwrapMethodInvocation(argument, state))
.build());
}
private static boolean isAtLeastAsVisible(Element symbol, Element reference) {
return Visibility.fromModifiers(symbol.getModifiers())
.compareTo(Visibility.fromModifiers(reference.getModifiers()))
>= 0;
}
}

View File

@@ -101,6 +101,8 @@ public final class JUnitMethodDeclaration extends BugChecker implements MethodTr
.build());
}
// XXX: Consider dropping leading underscores that otherwise result when canonicalizing
// `test_some_method_name`.
private static Optional<String> tryCanonicalizeMethodName(MethodSymbol symbol) {
return Optional.of(symbol.getQualifiedName().toString())
.filter(name -> name.startsWith(TEST_PREFIX))

View File

@@ -30,6 +30,7 @@ import com.sun.source.tree.Tree;
import com.sun.source.util.TreeScanner;
import com.sun.tools.javac.code.Symbol;
import java.time.Clock;
import java.time.InstantSource;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.Locale;
@@ -69,6 +70,7 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
ImmutableSet.of(
ASTHelpers.class.getCanonicalName(),
Clock.class.getCanonicalName(),
InstantSource.class.getCanonicalName(),
Strings.class.getCanonicalName(),
VisitorState.class.getCanonicalName(),
ZoneOffset.class.getCanonicalName(),
@@ -129,6 +131,7 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
"builder",
"copyOf",
"create",
"EPOCH",
"from",
"getDefaultInstance",
"INSTANCE",
@@ -182,7 +185,7 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
}
}
return imports.build();
return imports.buildOrThrow();
}
private static boolean shouldNotBeStaticallyImported(String type, String member) {

View File

@@ -28,8 +28,6 @@ import tech.picnic.errorprone.utils.SourceCode;
/** A {@link BugChecker} that flags SLF4J usages that are likely to be in error. */
// XXX: The special-casing of Throwable applies only to SLF4J 1.6.0+; see
// https://www.slf4j.org/faq.html#paramException. That should be documented.
// XXX: Also simplify `LOG.error(String.format("Something %s", arg), throwable)`.
// XXX: Also simplify `LOG.error(String.join("sep", arg1, arg2), throwable)`? Perhaps too obscure.
// XXX: Write a similar checker for Spring RestTemplates, String.format and friends, Guava
// preconditions, ...
@AutoService(BugChecker.class)

View File

@@ -54,7 +54,7 @@ public final class SpringMvcAnnotation extends BugChecker implements AnnotationT
.put("PATCH", "PatchMapping")
.put("POST", "PostMapping")
.put("PUT", "PutMapping")
.build();
.buildOrThrow();
/** Instantiates a new {@link SpringMvcAnnotation} instance. */
public SpringMvcAnnotation() {}

View File

@@ -36,7 +36,9 @@ import tech.picnic.errorprone.utils.SourceCode;
*/
// XXX: What about `v1 + "sep" + v2` and similar expressions? Do we want to rewrite those to
// `String.join`, or should some `String.join` invocations be rewritten to use the `+` operator?
// (The latter suggestion would conflict with the `FormatStringConcatenation` check.)
// (The latter suggestion would conflict with the `FormatStringConcatenation` check, but does make
// more sense when `"sep"` is a long string. Similarly for `String.format("%s some long text %s",
// arg1, arg2)`.)
@AutoService(BugChecker.class)
@BugPattern(
summary = "Prefer `String#join` over `String#format`",

View File

@@ -16,10 +16,12 @@ import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MemberReferenceTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MemberReferenceTree;
import com.sun.source.tree.MethodInvocationTree;
import java.time.Clock;
import java.time.Instant;
@@ -41,7 +43,8 @@ import java.time.ZonedDateTime;
linkType = CUSTOM,
severity = WARNING,
tags = FRAGILE_CODE)
public final class TimeZoneUsage extends BugChecker implements MethodInvocationTreeMatcher {
public final class TimeZoneUsage extends BugChecker
implements MethodInvocationTreeMatcher, MemberReferenceTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> BANNED_TIME_METHOD =
anyOf(
@@ -59,6 +62,10 @@ public final class TimeZoneUsage extends BugChecker implements MethodInvocationT
"tickMillis",
"tickMinutes",
"tickSeconds"),
staticMethod()
.onClassAny(Instant.class.getCanonicalName())
.named("now")
.withNoParameters(),
staticMethod()
.onClassAny(
LocalDate.class.getCanonicalName(),
@@ -67,17 +74,22 @@ public final class TimeZoneUsage extends BugChecker implements MethodInvocationT
OffsetDateTime.class.getCanonicalName(),
OffsetTime.class.getCanonicalName(),
ZonedDateTime.class.getCanonicalName())
.named("now"),
staticMethod()
.onClassAny(Instant.class.getCanonicalName())
.named("now")
.withNoParameters());
.named("now"));
/** Instantiates a new {@link TimeZoneUsage} instance. */
public TimeZoneUsage() {}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
return getDescription(tree, state);
}
@Override
public Description matchMemberReference(MemberReferenceTree tree, VisitorState state) {
return getDescription(tree, state);
}
private Description getDescription(ExpressionTree tree, VisitorState state) {
return BANNED_TIME_METHOD.matches(tree, state)
? buildDescription(tree).build()
: Description.NO_MATCH;

View File

@@ -18,6 +18,7 @@ final class AssertJCharSequenceRules {
@BeforeTemplate
void before(CharSequence charSequence) {
Refaster.anyOf(
assertThat(charSequence.isEmpty()).isTrue(),
assertThat(charSequence.length()).isEqualTo(0L),
assertThat(charSequence.length()).isNotPositive());
}
@@ -33,6 +34,7 @@ final class AssertJCharSequenceRules {
@BeforeTemplate
AbstractAssert<?, ?> before(CharSequence charSequence) {
return Refaster.anyOf(
assertThat(charSequence.isEmpty()).isFalse(),
assertThat(charSequence.length()).isNotEqualTo(0),
assertThat(charSequence.length()).isPositive());
}

View File

@@ -5,6 +5,9 @@ import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import java.util.Collection;
import org.assertj.core.api.AbstractIntegerAssert;
import org.assertj.core.api.AbstractIterableAssert;
import org.assertj.core.api.AbstractIterableSizeAssert;
import org.assertj.core.api.EnumerableAssert;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
@@ -21,6 +24,11 @@ final class AssertJEnumerableRules {
enumAssert.hasSizeLessThan(1));
}
@BeforeTemplate
void before(AbstractIterableAssert<?, ?, E, ?> enumAssert) {
enumAssert.size().isNotPositive();
}
@AfterTemplate
void after(EnumerableAssert<?, E> enumAssert) {
enumAssert.isEmpty();
@@ -34,30 +42,170 @@ final class AssertJEnumerableRules {
enumAssert.hasSizeGreaterThan(0), enumAssert.hasSizeGreaterThanOrEqualTo(1));
}
@BeforeTemplate
AbstractIterableAssert<?, ?, E, ?> before(AbstractIterableAssert<?, ?, E, ?> enumAssert) {
return Refaster.anyOf(
enumAssert.size().isNotEqualTo(0).returnToIterable(),
enumAssert.size().isPositive().returnToIterable());
}
// XXX: If this template matches, then the expression's return type changes incompatibly.
// Consider moving this template to a separate block (statement) rule.
@BeforeTemplate
AbstractIntegerAssert<?> before2(AbstractIterableAssert<?, ?, E, ?> enumAssert) {
return Refaster.anyOf(enumAssert.size().isNotEqualTo(0), enumAssert.size().isPositive());
}
@AfterTemplate
EnumerableAssert<?, E> after(EnumerableAssert<?, E> enumAssert) {
return enumAssert.isNotEmpty();
}
}
static final class EnumerableAssertHasSameSizeAs<S, T> {
static final class EnumerableAssertHasSize<E> {
@BeforeTemplate
EnumerableAssert<?, S> before(EnumerableAssert<?, S> enumAssert, Iterable<T> iterable) {
AbstractIterableAssert<?, ?, E, ?> before(
AbstractIterableAssert<?, ?, E, ?> enumAssert, int size) {
return enumAssert.size().isEqualTo(size).returnToIterable();
}
// XXX: If this template matches, then the expression's return type changes incompatibly.
// Consider moving this template to a separate block (statement) rule.
@BeforeTemplate
AbstractIterableSizeAssert<?, ?, E, ?> before2(
AbstractIterableAssert<?, ?, E, ?> enumAssert, int size) {
return enumAssert.size().isEqualTo(size);
}
@AfterTemplate
EnumerableAssert<?, E> after(EnumerableAssert<?, E> enumAssert, int size) {
return enumAssert.hasSize(size);
}
}
static final class EnumerableAssertHasSizeLessThan<E> {
@BeforeTemplate
AbstractIterableAssert<?, ?, E, ?> before(
AbstractIterableAssert<?, ?, E, ?> enumAssert, int size) {
return enumAssert.size().isLessThan(size).returnToIterable();
}
// XXX: If this template matches, then the expression's return type changes incompatibly.
// Consider moving this template to a separate block (statement) rule.
@BeforeTemplate
AbstractIterableSizeAssert<?, ?, E, ?> before2(
AbstractIterableAssert<?, ?, E, ?> enumAssert, int size) {
return enumAssert.size().isLessThan(size);
}
@AfterTemplate
EnumerableAssert<?, E> after(EnumerableAssert<?, E> enumAssert, int size) {
return enumAssert.hasSizeLessThan(size);
}
}
static final class EnumerableAssertHasSizeLessThanOrEqualTo<E> {
@BeforeTemplate
AbstractIterableAssert<?, ?, E, ?> before(
AbstractIterableAssert<?, ?, E, ?> enumAssert, int size) {
return enumAssert.size().isLessThanOrEqualTo(size).returnToIterable();
}
// XXX: If this template matches, then the expression's return type changes incompatibly.
// Consider moving this template to a separate block (statement) rule.
@BeforeTemplate
AbstractIterableSizeAssert<?, ?, E, ?> before2(
AbstractIterableAssert<?, ?, E, ?> enumAssert, int size) {
return enumAssert.size().isLessThanOrEqualTo(size);
}
@AfterTemplate
EnumerableAssert<?, E> after(EnumerableAssert<?, E> enumAssert, int size) {
return enumAssert.hasSizeLessThanOrEqualTo(size);
}
}
static final class EnumerableAssertHasSizeGreaterThan<E> {
@BeforeTemplate
AbstractIterableAssert<?, ?, E, ?> before(
AbstractIterableAssert<?, ?, E, ?> enumAssert, int size) {
return enumAssert.size().isGreaterThan(size).returnToIterable();
}
// XXX: If this template matches, then the expression's return type changes incompatibly.
// Consider moving this template to a separate block (statement) rule.
@BeforeTemplate
AbstractIterableSizeAssert<?, ?, E, ?> before2(
AbstractIterableAssert<?, ?, E, ?> enumAssert, int size) {
return enumAssert.size().isGreaterThan(size);
}
@AfterTemplate
EnumerableAssert<?, E> after(EnumerableAssert<?, E> enumAssert, int size) {
return enumAssert.hasSizeGreaterThan(size);
}
}
static final class EnumerableAssertHasSizeGreaterThanOrEqualTo<E> {
@BeforeTemplate
AbstractIterableAssert<?, ?, E, ?> before(
AbstractIterableAssert<?, ?, E, ?> enumAssert, int size) {
return enumAssert.size().isGreaterThanOrEqualTo(size).returnToIterable();
}
// XXX: If this template matches, then the expression's return type changes incompatibly.
// Consider moving this template to a separate block (statement) rule.
@BeforeTemplate
AbstractIterableSizeAssert<?, ?, E, ?> before2(
AbstractIterableAssert<?, ?, E, ?> enumAssert, int size) {
return enumAssert.size().isGreaterThanOrEqualTo(size);
}
@AfterTemplate
EnumerableAssert<?, E> after(EnumerableAssert<?, E> enumAssert, int size) {
return enumAssert.hasSizeGreaterThanOrEqualTo(size);
}
}
static final class EnumerableAssertHasSizeBetween<E> {
@BeforeTemplate
AbstractIterableAssert<?, ?, E, ?> before(
AbstractIterableAssert<?, ?, E, ?> enumAssert, int lower, int upper) {
return enumAssert.size().isBetween(lower, upper).returnToIterable();
}
// XXX: If this template matches, then the expression's return type changes incompatibly.
// Consider moving this template to a separate block (statement) rule.
@BeforeTemplate
AbstractIterableSizeAssert<?, ?, E, ?> before2(
AbstractIterableAssert<?, ?, E, ?> enumAssert, int lower, int upper) {
return enumAssert.size().isBetween(lower, upper);
}
@AfterTemplate
EnumerableAssert<?, E> after(EnumerableAssert<?, E> enumAssert, int lower, int upper) {
return enumAssert.hasSizeBetween(lower, upper);
}
}
static final class EnumerableAssertHasSameSizeAs<S, E> {
@BeforeTemplate
EnumerableAssert<?, S> before(EnumerableAssert<?, S> enumAssert, Iterable<E> iterable) {
return enumAssert.hasSize(Iterables.size(iterable));
}
@BeforeTemplate
EnumerableAssert<?, S> before(EnumerableAssert<?, S> enumAssert, Collection<T> iterable) {
EnumerableAssert<?, S> before(EnumerableAssert<?, S> enumAssert, Collection<E> iterable) {
return enumAssert.hasSize(iterable.size());
}
@BeforeTemplate
EnumerableAssert<?, S> before(EnumerableAssert<?, S> enumAssert, T[] iterable) {
EnumerableAssert<?, S> before(EnumerableAssert<?, S> enumAssert, E[] iterable) {
return enumAssert.hasSize(iterable.length);
}
@AfterTemplate
EnumerableAssert<?, S> after(EnumerableAssert<?, S> enumAssert, Iterable<T> iterable) {
EnumerableAssert<?, S> after(EnumerableAssert<?, S> enumAssert, Iterable<E> iterable) {
return enumAssert.hasSameSizeAs(iterable);
}
}

View File

@@ -0,0 +1,90 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.Iterables;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Collection;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AbstractIntegerAssert;
import org.assertj.core.api.IterableAssert;
import org.assertj.core.api.ObjectAssert;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
@OnlineDocumentation
final class AssertJIterableRules {
private AssertJIterableRules() {}
static final class AssertThatIterableIsEmpty<E> {
@BeforeTemplate
void before(Iterable<E> iterable) {
assertThat(iterable.iterator()).isExhausted();
}
@BeforeTemplate
void before(Collection<E> iterable) {
assertThat(iterable.isEmpty()).isTrue();
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(Collection<E> iterable) {
assertThat(iterable).isEmpty();
}
}
static final class AssertThatIterableIsNotEmpty<E> {
@BeforeTemplate
AbstractAssert<?, ?> before(Iterable<E> iterable) {
return assertThat(iterable.iterator()).hasNext();
}
@BeforeTemplate
AbstractAssert<?, ?> before(Collection<E> iterable) {
return assertThat(iterable.isEmpty()).isFalse();
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
IterableAssert<E> after(Iterable<E> iterable) {
return assertThat(iterable).isNotEmpty();
}
}
static final class AssertThatIterableSize<E> {
@BeforeTemplate
AbstractIntegerAssert<?> before(Iterable<E> iterable) {
return assertThat(Iterables.size(iterable));
}
@BeforeTemplate
AbstractIntegerAssert<?> before(Collection<E> iterable) {
return assertThat(iterable.size());
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractIntegerAssert<?> after(Iterable<E> iterable) {
return assertThat(iterable).size();
}
}
// XXX: In practice this rule isn't very useful, as it only matches invocations of
// `assertThat(E)`. In most cases a more specific overload of `assertThat` is invoked, in which
// case this rule won't match. Look into a more robust approach.
static final class AssertThatIterableHasOneElementEqualTo<S, E extends S> {
@BeforeTemplate
ObjectAssert<S> before(Iterable<S> iterable, E element) {
return assertThat(Iterables.getOnlyElement(iterable)).isEqualTo(element);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
IterableAssert<S> after(Iterable<S> iterable, E element) {
return assertThat(iterable).containsExactly(element);
}
}
}

View File

@@ -0,0 +1,43 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Iterator;
import org.assertj.core.api.AbstractBooleanAssert;
import org.assertj.core.api.IteratorAssert;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
@OnlineDocumentation
final class AssertJIteratorRules {
private AssertJIteratorRules() {}
static final class AssertThatHasNext<T> {
@BeforeTemplate
AbstractBooleanAssert<?> before(Iterator<T> iterator) {
return assertThat(iterator.hasNext()).isTrue();
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
IteratorAssert<T> after(Iterator<T> iterator) {
return assertThat(iterator).hasNext();
}
}
static final class AssertThatIsExhausted<T> {
@BeforeTemplate
AbstractBooleanAssert<?> before(Iterator<T> iterator) {
return assertThat(iterator.hasNext()).isFalse();
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
IteratorAssert<T> after(Iterator<T> iterator) {
return assertThat(iterator).isExhausted();
}
}
}

View File

@@ -11,7 +11,6 @@ import com.google.errorprone.refaster.annotation.Matches;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AbstractBooleanAssert;
import org.assertj.core.api.AbstractCollectionAssert;
@@ -182,13 +181,13 @@ final class AssertJMapRules {
static final class AssertThatMapContainsOnlyKeys<K, V> {
@BeforeTemplate
AbstractCollectionAssert<?, Collection<? extends K>, K, ?> before(
Map<K, V> map, Set<? extends K> keys) {
Map<K, V> map, Iterable<? extends K> keys) {
return assertThat(map.keySet()).hasSameElementsAs(keys);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
MapAssert<K, V> after(Map<K, V> map, Set<? extends K> keys) {
MapAssert<K, V> after(Map<K, V> map, Iterable<? extends K> keys) {
return assertThat(map).containsOnlyKeys(keys);
}
}

View File

@@ -12,6 +12,7 @@ import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AbstractBooleanAssert;
import org.assertj.core.api.AbstractStringAssert;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
@@ -31,19 +32,6 @@ final class AssertJStringRules {
}
}
static final class AssertThatStringIsEmpty {
@BeforeTemplate
void before(String string) {
assertThat(string.isEmpty()).isTrue();
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(String string) {
assertThat(string).isEmpty();
}
}
static final class AbstractStringAssertStringIsNotEmpty {
@BeforeTemplate
AbstractStringAssert<?> before(AbstractStringAssert<?> stringAssert) {
@@ -56,16 +44,29 @@ final class AssertJStringRules {
}
}
static final class AssertThatStringIsNotEmpty {
static final class AssertThatStringContains {
@BeforeTemplate
AbstractAssert<?, ?> before(String string) {
return assertThat(string.isEmpty()).isFalse();
AbstractBooleanAssert<?> before(String string, CharSequence substring) {
return assertThat(string.contains(substring)).isTrue();
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractAssert<?, ?> after(String string) {
return assertThat(string).isNotEmpty();
AbstractStringAssert<?> after(String string, CharSequence substring) {
return assertThat(string).contains(substring);
}
}
static final class AssertThatStringDoesNotContain {
@BeforeTemplate
AbstractBooleanAssert<?> before(String string, CharSequence substring) {
return assertThat(string.contains(substring)).isFalse();
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractStringAssert<?> after(String string, CharSequence substring) {
return assertThat(string).doesNotContain(substring);
}
}

View File

@@ -7,7 +7,10 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.InstanceOfAssertFactories.throwable;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
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;
@@ -16,6 +19,7 @@ import java.io.IOException;
import org.assertj.core.api.AbstractObjectAssert;
import org.assertj.core.api.AbstractThrowableAssert;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.assertj.core.api.ThrowableAssertAlternative;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/**
@@ -31,6 +35,21 @@ import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
final class AssertJThrowingCallableRules {
private AssertJThrowingCallableRules() {}
static final class AssertThatThrownByIsInstanceOf<T extends Throwable> {
@BeforeTemplate
void before(ThrowingCallable throwingCallable, Class<T> exceptionType) {
Refaster.anyOf(
assertThatThrownBy(throwingCallable).asInstanceOf(throwable(exceptionType)),
assertThatThrownBy(throwingCallable).asInstanceOf(type(exceptionType)));
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(ThrowingCallable throwingCallable, Class<T> exceptionType) {
assertThatThrownBy(throwingCallable).isInstanceOf(exceptionType);
}
}
static final class AssertThatThrownByIllegalArgumentException {
@BeforeTemplate
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable) {
@@ -61,6 +80,27 @@ final class AssertJThrowingCallableRules {
}
}
static final class AssertThatThrownByIllegalArgumentExceptionRootCauseHasMessage {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIllegalArgumentException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIllegalArgumentException()
.isThrownBy(throwingCallable)
.havingRootCause()
.withMessage(message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(ThrowingCallable throwingCallable, String message) {
return assertThatThrownBy(throwingCallable)
.isInstanceOf(IllegalArgumentException.class)
.rootCause()
.hasMessage(message);
}
}
static final class AssertThatThrownByIllegalArgumentExceptionHasMessageParameters {
@BeforeTemplate
@SuppressWarnings(
@@ -171,6 +211,27 @@ final class AssertJThrowingCallableRules {
}
}
static final class AssertThatThrownByIllegalStateExceptionRootCauseHasMessage {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByIllegalStateException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIllegalStateException()
.isThrownBy(throwingCallable)
.havingRootCause()
.withMessage(message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(ThrowingCallable throwingCallable, String message) {
return assertThatThrownBy(throwingCallable)
.isInstanceOf(IllegalStateException.class)
.rootCause()
.hasMessage(message);
}
}
static final class AssertThatThrownByIllegalStateExceptionHasMessageParameters {
@BeforeTemplate
@SuppressWarnings(
@@ -279,6 +340,27 @@ final class AssertJThrowingCallableRules {
}
}
static final class AssertThatThrownByNullPointerExceptionRootCauseHasMessage {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByNullPointerException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatNullPointerException()
.isThrownBy(throwingCallable)
.havingRootCause()
.withMessage(message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(ThrowingCallable throwingCallable, String message) {
return assertThatThrownBy(throwingCallable)
.isInstanceOf(NullPointerException.class)
.rootCause()
.hasMessage(message);
}
}
static final class AssertThatThrownByNullPointerExceptionHasMessageParameters {
@BeforeTemplate
@SuppressWarnings(
@@ -386,6 +468,26 @@ final class AssertJThrowingCallableRules {
}
}
static final class AssertThatThrownByIOExceptionRootCauseHasMessage {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownByIOException" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
return assertThatIOException()
.isThrownBy(throwingCallable)
.havingRootCause()
.withMessage(message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(ThrowingCallable throwingCallable, String message) {
return assertThatThrownBy(throwingCallable)
.isInstanceOf(IOException.class)
.rootCause()
.hasMessage(message);
}
}
static final class AssertThatThrownByIOExceptionHasMessageParameters {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownByIOException" /* This is a more specific template. */)
@@ -452,24 +554,24 @@ final class AssertJThrowingCallableRules {
}
}
static final class AssertThatThrownBy {
static final class AssertThatThrownByAsInstanceOfThrowable<T extends Throwable> {
@BeforeTemplate
AbstractObjectAssert<?, ?> before(
ThrowingCallable throwingCallable, Class<? extends Throwable> exceptionType) {
ThrowableAssertAlternative<T> before(
ThrowingCallable throwingCallable, Class<T> exceptionType) {
return assertThatExceptionOfType(exceptionType).isThrownBy(throwingCallable);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(
ThrowingCallable throwingCallable, Class<? extends Throwable> exceptionType) {
return assertThatThrownBy(throwingCallable).isInstanceOf(exceptionType);
AbstractThrowableAssert<?, T> after(ThrowingCallable throwingCallable, Class<T> exceptionType) {
return assertThatThrownBy(throwingCallable).asInstanceOf(throwable(exceptionType));
}
}
static final class AssertThatThrownByHasMessage {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
@SuppressWarnings(
"AssertThatThrownByAsInstanceOfThrowable" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
ThrowingCallable throwingCallable,
Class<? extends Throwable> exceptionType,
@@ -489,9 +591,37 @@ final class AssertJThrowingCallableRules {
}
}
static final class AssertThatThrownByRootCauseHasMessage {
@BeforeTemplate
@SuppressWarnings(
"AssertThatThrownByAsInstanceOfThrowable" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
ThrowingCallable throwingCallable,
Class<? extends Throwable> exceptionType,
String message) {
return assertThatExceptionOfType(exceptionType)
.isThrownBy(throwingCallable)
.havingRootCause()
.withMessage(message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
AbstractObjectAssert<?, ?> after(
ThrowingCallable throwingCallable,
Class<? extends Throwable> exceptionType,
String message) {
return assertThatThrownBy(throwingCallable)
.isInstanceOf(exceptionType)
.rootCause()
.hasMessage(message);
}
}
static final class AssertThatThrownByHasMessageParameters {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
@SuppressWarnings(
"AssertThatThrownByAsInstanceOfThrowable" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
ThrowingCallable throwingCallable,
Class<? extends Throwable> exceptionType,
@@ -517,7 +647,8 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByHasMessageStartingWith {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
@SuppressWarnings(
"AssertThatThrownByAsInstanceOfThrowable" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
ThrowingCallable throwingCallable,
Class<? extends Throwable> exceptionType,
@@ -541,7 +672,8 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByHasMessageContaining {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
@SuppressWarnings(
"AssertThatThrownByAsInstanceOfThrowable" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
ThrowingCallable throwingCallable,
Class<? extends Throwable> exceptionType,
@@ -565,7 +697,8 @@ final class AssertJThrowingCallableRules {
static final class AssertThatThrownByHasMessageNotContaining {
@BeforeTemplate
@SuppressWarnings("AssertThatThrownBy" /* This is a more specific template. */)
@SuppressWarnings(
"AssertThatThrownByAsInstanceOfThrowable" /* This is a more specific template. */)
AbstractObjectAssert<?, ?> before(
ThrowingCallable throwingCallable,
Class<? extends Throwable> exceptionType,

View File

@@ -121,18 +121,18 @@ final class AssortedRules {
*/
static final class DisjointSets<T> {
@BeforeTemplate
boolean before(Set<T> set1, Set<T> set2) {
return Sets.intersection(set1, set2).isEmpty();
boolean before(Set<T> collection1, Set<T> collection2) {
return Sets.intersection(collection1, collection2).isEmpty();
}
@BeforeTemplate
boolean before2(Set<T> set1, Set<T> set2) {
return set1.stream().noneMatch(set2::contains);
boolean before2(Collection<T> collection1, Collection<T> collection2) {
return collection1.stream().noneMatch(collection2::contains);
}
@AfterTemplate
boolean after(Set<T> set1, Set<T> set2) {
return disjoint(set1, set2);
boolean after(Collection<T> collection1, Collection<T> collection2) {
return disjoint(collection1, collection2);
}
}

View File

@@ -0,0 +1,33 @@
package tech.picnic.errorprone.refasterrules;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.AlsoNegation;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to expressions dealing with {@link CharSequence}s. */
@OnlineDocumentation
final class CharSequenceRules {
private CharSequenceRules() {}
/**
* Prefer {@link CharSequence#isEmpty()} over alternatives that consult the char sequence's
* length.
*/
// XXX: Drop this rule once we (and OpenRewrite) no longer support projects targeting Java 14 or
// below.
static final class CharSequenceIsEmpty {
@BeforeTemplate
boolean before(CharSequence charSequence) {
return Refaster.anyOf(
charSequence.length() == 0, charSequence.length() <= 0, charSequence.length() < 1);
}
@AfterTemplate
@AlsoNegation
boolean after(CharSequence charSequence) {
return charSequence.isEmpty();
}
}
}

View File

@@ -158,14 +158,14 @@ final class CollectionRules {
}
}
static final class SetRemoveAllCollection<T, S extends T> {
static final class CollectionRemoveAllFromCollectionBlock<T, S extends T> {
@BeforeTemplate
void before(Set<T> removeFrom, Collection<S> elementsToRemove) {
void before(Collection<T> removeFrom, Collection<S> elementsToRemove) {
elementsToRemove.forEach(removeFrom::remove);
}
@BeforeTemplate
void before2(Set<T> removeFrom, Collection<S> elementsToRemove) {
void before2(Collection<T> removeFrom, Collection<S> elementsToRemove) {
for (T element : elementsToRemove) {
removeFrom.remove(element);
}
@@ -175,14 +175,14 @@ final class CollectionRules {
// that this is supported out of the box. After doing so, also drop the `S extends T` type
// constraint; ideally this check applies to any `S`.
@BeforeTemplate
void before3(Set<T> removeFrom, Collection<S> elementsToRemove) {
void before3(Collection<T> removeFrom, Collection<S> elementsToRemove) {
for (S element : elementsToRemove) {
removeFrom.remove(element);
}
}
@AfterTemplate
void after(Set<T> removeFrom, Collection<S> elementsToRemove) {
void after(Collection<T> removeFrom, Collection<S> elementsToRemove) {
removeFrom.removeAll(elementsToRemove);
}
}

View File

@@ -4,6 +4,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.AlsoNegation;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.Repeated;
import java.io.File;
@@ -11,6 +12,7 @@ import java.io.IOException;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileAttribute;
@@ -141,4 +143,35 @@ final class FileRules {
return Files.createTempFile(directory.toPath(), prefix, suffix).toFile();
}
}
/**
* Invoke {@link File#mkdirs()} before {@link Files#exists(Path, LinkOption...)} to avoid
* concurrency issues.
*/
static final class PathToFileMkDirsFilesExists {
@BeforeTemplate
boolean before(Path path) {
return Files.exists(path) || path.toFile().mkdirs();
}
@AfterTemplate
@AlsoNegation
boolean after(Path path) {
return path.toFile().mkdirs() || Files.exists(path);
}
}
/** Invoke {@link File#mkdirs()} before {@link File#exists()} to avoid concurrency issues. */
static final class FileMkDirsFileExists {
@BeforeTemplate
boolean before(File file) {
return file.exists() || file.mkdirs();
}
@AfterTemplate
@AlsoNegation
boolean after(File file) {
return file.mkdirs() || file.exists();
}
}
}

View File

@@ -36,8 +36,7 @@ final class ImmutableListMultimapRules {
* Prefer {@link ImmutableListMultimap#builder()} over the associated constructor on constructions
* that produce a less-specific type.
*/
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
// https://github.com/google/error-prone/pull/2706.
// XXX: This rule may drop generic type information, leading to non-compilable code.
static final class ImmutableListMultimapBuilder<K, V> {
@BeforeTemplate
ImmutableMultimap.Builder<K, V> before() {

View File

@@ -28,8 +28,7 @@ final class ImmutableListRules {
private ImmutableListRules() {}
/** Prefer {@link ImmutableList#builder()} over the associated constructor. */
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
// https://github.com/google/error-prone/pull/2706.
// XXX: This rule may drop generic type information, leading to non-compilable code.
static final class ImmutableListBuilder<T> {
@BeforeTemplate
ImmutableList.Builder<T> before() {

View File

@@ -15,6 +15,7 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.Matches;
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
import com.google.errorprone.refaster.annotation.Placeholder;
import com.google.errorprone.refaster.annotation.Repeated;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Collection;
import java.util.Iterator;
@@ -31,8 +32,7 @@ final class ImmutableMapRules {
private ImmutableMapRules() {}
/** Prefer {@link ImmutableMap#builder()} over the associated constructor. */
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
// https://github.com/google/error-prone/pull/2706.
// XXX: This rule may drop generic type information, leading to non-compilable code.
static final class ImmutableMapBuilder<K, V> {
@BeforeTemplate
ImmutableMap.Builder<K, V> before() {
@@ -45,12 +45,28 @@ final class ImmutableMapRules {
}
}
/**
* Prefer {@link ImmutableMap.Builder#buildOrThrow()} over the less explicit {@link
* ImmutableMap.Builder#build()}.
*/
static final class ImmutableMapBuilderBuildOrThrow<K, V> {
@BeforeTemplate
ImmutableMap<K, V> before(ImmutableMap.Builder<K, V> builder) {
return builder.build();
}
@AfterTemplate
ImmutableMap<K, V> after(ImmutableMap.Builder<K, V> builder) {
return builder.buildOrThrow();
}
}
/** Prefer {@link ImmutableMap#of(Object, Object)} over more contrived alternatives. */
static final class EntryToImmutableMap<K, V> {
@BeforeTemplate
ImmutableMap<K, V> before(Map.Entry<? extends K, ? extends V> entry) {
return Refaster.anyOf(
ImmutableMap.<K, V>builder().put(entry).build(),
ImmutableMap.<K, V>builder().put(entry).buildOrThrow(),
Stream.of(entry).collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)));
}
@@ -105,16 +121,17 @@ final class ImmutableMapRules {
/** Prefer {@link ImmutableMap#copyOf(Iterable)} over more contrived alternatives. */
static final class EntryIterableToImmutableMap<K, V> {
@BeforeTemplate
ImmutableMap<K, V> before(Map<? extends K, ? extends V> iterable) {
Map<K, V> before(Map<? extends K, ? extends V> iterable) {
return Refaster.anyOf(
ImmutableMap.copyOf(iterable.entrySet()),
ImmutableMap.<K, V>builder().putAll(iterable).build());
ImmutableMap.<K, V>builder().putAll(iterable).buildOrThrow(),
Map.copyOf(iterable));
}
@BeforeTemplate
ImmutableMap<K, V> before(Iterable<? extends Map.Entry<? extends K, ? extends V>> iterable) {
return Refaster.anyOf(
ImmutableMap.<K, V>builder().putAll(iterable).build(),
ImmutableMap.<K, V>builder().putAll(iterable).buildOrThrow(),
Streams.stream(iterable).collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)));
}
@@ -140,8 +157,6 @@ final class ImmutableMapRules {
@Placeholder(allowsIdentity = true)
abstract V valueFunction(@MayOptionallyUse E element);
// XXX: We could add variants in which the entry is created some other way, but we have another
// rule that covers canonicalization to `Map.entry`.
@BeforeTemplate
ImmutableMap<K, V> before(Stream<E> stream) {
return stream
@@ -225,7 +240,11 @@ final class ImmutableMapRules {
static final class ImmutableMapOf<K, V> {
@BeforeTemplate
Map<K, V> before() {
return Refaster.anyOf(ImmutableMap.<K, V>builder().build(), emptyMap(), Map.of());
return Refaster.anyOf(
ImmutableMap.<K, V>builder().buildOrThrow(),
ImmutableMap.ofEntries(),
emptyMap(),
Map.of());
}
@AfterTemplate
@@ -244,7 +263,10 @@ final class ImmutableMapRules {
@BeforeTemplate
Map<K, V> before(K k1, V v1) {
return Refaster.anyOf(
ImmutableMap.<K, V>builder().put(k1, v1).build(), singletonMap(k1, v1), Map.of(k1, v1));
ImmutableMap.<K, V>builder().put(k1, v1).buildOrThrow(),
ImmutableMap.ofEntries(Map.entry(k1, v1)),
singletonMap(k1, v1),
Map.of(k1, v1));
}
@AfterTemplate
@@ -262,7 +284,8 @@ final class ImmutableMapRules {
static final class ImmutableMapOf2<K, V> {
@BeforeTemplate
Map<K, V> before(K k1, V v1, K k2, V v2) {
return Map.of(k1, v1, k2, v2);
return Refaster.anyOf(
ImmutableMap.ofEntries(Map.entry(k1, v1), Map.entry(k2, v2)), Map.of(k1, v1, k2, v2));
}
@AfterTemplate
@@ -280,7 +303,9 @@ final class ImmutableMapRules {
static final class ImmutableMapOf3<K, V> {
@BeforeTemplate
Map<K, V> before(K k1, V v1, K k2, V v2, K k3, V v3) {
return Map.of(k1, v1, k2, v2, k3, v3);
return Refaster.anyOf(
ImmutableMap.ofEntries(Map.entry(k1, v1), Map.entry(k2, v2), Map.entry(k3, v3)),
Map.of(k1, v1, k2, v2, k3, v3));
}
@AfterTemplate
@@ -300,7 +325,10 @@ final class ImmutableMapRules {
static final class ImmutableMapOf4<K, V> {
@BeforeTemplate
Map<K, V> before(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
return Map.of(k1, v1, k2, v2, k3, v3, k4, v4);
return Refaster.anyOf(
ImmutableMap.ofEntries(
Map.entry(k1, v1), Map.entry(k2, v2), Map.entry(k3, v3), Map.entry(k4, v4)),
Map.of(k1, v1, k2, v2, k3, v3, k4, v4));
}
@AfterTemplate
@@ -320,7 +348,14 @@ final class ImmutableMapRules {
static final class ImmutableMapOf5<K, V> {
@BeforeTemplate
Map<K, V> before(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
return Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5);
return Refaster.anyOf(
ImmutableMap.ofEntries(
Map.entry(k1, v1),
Map.entry(k2, v2),
Map.entry(k3, v3),
Map.entry(k4, v4),
Map.entry(k5, v5)),
Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5));
}
@AfterTemplate
@@ -338,14 +373,14 @@ final class ImmutableMapRules {
abstract boolean keyFilter(@MayOptionallyUse K key);
@BeforeTemplate
ImmutableMap<K, V> before(ImmutableMap<K, V> map) {
ImmutableMap<K, V> before(Map<K, V> map) {
return map.entrySet().stream()
.filter(e -> keyFilter(e.getKey()))
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
}
@AfterTemplate
ImmutableMap<K, V> after(ImmutableMap<K, V> map) {
ImmutableMap<K, V> after(Map<K, V> map) {
return ImmutableMap.copyOf(Maps.filterKeys(map, k -> keyFilter(k)));
}
}
@@ -359,18 +394,34 @@ final class ImmutableMapRules {
abstract boolean valueFilter(@MayOptionallyUse V value);
@BeforeTemplate
ImmutableMap<K, V> before(ImmutableMap<K, V> map) {
ImmutableMap<K, V> before(Map<K, V> map) {
return map.entrySet().stream()
.filter(e -> valueFilter(e.getValue()))
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
}
@AfterTemplate
ImmutableMap<K, V> after(ImmutableMap<K, V> map) {
ImmutableMap<K, V> after(Map<K, V> map) {
return ImmutableMap.copyOf(Maps.filterValues(map, v -> valueFilter(v)));
}
}
/**
* Prefer {@link ImmutableMap#ofEntries(Map.Entry[])} over alternatives that don't communicate the
* immutability of the resulting map at the type level.
*/
static final class ImmutableMapOfEntries<K, V> {
@BeforeTemplate
Map<K, V> before(@Repeated Map.Entry<? extends K, ? extends V> entries) {
return Map.ofEntries(entries);
}
@AfterTemplate
ImmutableMap<K, V> after(@Repeated Map.Entry<? extends K, ? extends V> entries) {
return ImmutableMap.ofEntries(entries);
}
}
// XXX: Add a rule for this:
// Maps.transformValues(streamOfEntries.collect(groupBy(fun)), ImmutableMap::copyOf)
// ->

View File

@@ -21,8 +21,7 @@ final class ImmutableMultisetRules {
private ImmutableMultisetRules() {}
/** Prefer {@link ImmutableMultiset#builder()} over the associated constructor. */
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
// https://github.com/google/error-prone/pull/2706.
// XXX: This rule may drop generic type information, leading to non-compilable code.
static final class ImmutableMultisetBuilder<T> {
@BeforeTemplate
ImmutableMultiset.Builder<T> before() {

View File

@@ -29,8 +29,7 @@ final class ImmutableSetMultimapRules {
private ImmutableSetMultimapRules() {}
/** Prefer {@link ImmutableSetMultimap#builder()} over the associated constructor. */
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
// https://github.com/google/error-prone/pull/2706.
// XXX: This rule may drop generic type information, leading to non-compilable code.
static final class ImmutableSetMultimapBuilder<K, V> {
@BeforeTemplate
ImmutableSetMultimap.Builder<K, V> before() {

View File

@@ -29,8 +29,7 @@ final class ImmutableSetRules {
private ImmutableSetRules() {}
/** Prefer {@link ImmutableSet#builder()} over the associated constructor. */
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
// https://github.com/google/error-prone/pull/2706.
// XXX: This rule may drop generic type information, leading to non-compilable code.
static final class ImmutableSetBuilder<T> {
@BeforeTemplate
ImmutableSet.Builder<T> before() {

View File

@@ -37,8 +37,7 @@ final class ImmutableSortedMapRules {
* Prefer {@link ImmutableSortedMap#naturalOrder()} over the alternative that requires explicitly
* providing the {@link Comparator}.
*/
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
// https://github.com/google/error-prone/pull/2706.
// XXX: This rule may drop generic type information, leading to non-compilable code.
static final class ImmutableSortedMapNaturalOrderBuilder<K extends Comparable<? super K>, V> {
@BeforeTemplate
ImmutableSortedMap.Builder<K, V> before() {
@@ -55,8 +54,7 @@ final class ImmutableSortedMapRules {
* Prefer {@link ImmutableSortedMap#reverseOrder()} over the alternative that requires explicitly
* providing the {@link Comparator}.
*/
// XXX: This drops generic type information, sometimes leading to non-compilable code. See
// https://github.com/google/error-prone/pull/2706.
// XXX: This rule may drop generic type information, leading to non-compilable code.
static final class ImmutableSortedMapReverseOrderBuilder<K extends Comparable<? super K>, V> {
@BeforeTemplate
ImmutableSortedMap.Builder<K, V> before() {
@@ -73,7 +71,7 @@ final class ImmutableSortedMapRules {
static final class EmptyImmutableSortedMap<K extends Comparable<? super K>, V> {
@BeforeTemplate
ImmutableSortedMap<K, V> before() {
return ImmutableSortedMap.<K, V>naturalOrder().build();
return ImmutableSortedMap.<K, V>naturalOrder().buildOrThrow();
}
@AfterTemplate
@@ -91,7 +89,7 @@ final class ImmutableSortedMapRules {
static final class PairToImmutableSortedMap<K extends Comparable<? super K>, V> {
@BeforeTemplate
ImmutableSortedMap<K, V> before(K key, V value) {
return ImmutableSortedMap.<K, V>naturalOrder().put(key, value).build();
return ImmutableSortedMap.<K, V>naturalOrder().put(key, value).buildOrThrow();
}
@AfterTemplate
@@ -107,7 +105,7 @@ final class ImmutableSortedMapRules {
@BeforeTemplate
ImmutableSortedMap<K, V> before(Map.Entry<? extends K, ? extends V> entry) {
return Refaster.anyOf(
ImmutableSortedMap.<K, V>naturalOrder().put(entry).build(),
ImmutableSortedMap.<K, V>naturalOrder().put(entry).buildOrThrow(),
Stream.of(entry)
.collect(
toImmutableSortedMap(naturalOrder(), Map.Entry::getKey, Map.Entry::getValue)));
@@ -128,7 +126,7 @@ final class ImmutableSortedMapRules {
return Refaster.anyOf(
ImmutableSortedMap.copyOf(iterable, naturalOrder()),
ImmutableSortedMap.copyOf(iterable.entrySet()),
ImmutableSortedMap.<K, V>naturalOrder().putAll(iterable).build());
ImmutableSortedMap.<K, V>naturalOrder().putAll(iterable).buildOrThrow());
}
@BeforeTemplate
@@ -136,7 +134,7 @@ final class ImmutableSortedMapRules {
Iterable<? extends Map.Entry<? extends K, ? extends V>> iterable) {
return Refaster.anyOf(
ImmutableSortedMap.copyOf(iterable, naturalOrder()),
ImmutableSortedMap.<K, V>naturalOrder().putAll(iterable).build(),
ImmutableSortedMap.<K, V>naturalOrder().putAll(iterable).buildOrThrow(),
Streams.stream(iterable)
.collect(
toImmutableSortedMap(

View File

@@ -0,0 +1,114 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableTable.toImmutableTable;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Table;
import com.google.common.collect.Tables;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
import com.google.errorprone.refaster.annotation.Placeholder;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.stream.Stream;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to expressions dealing with {@link ImmutableTable}s. */
@OnlineDocumentation
final class ImmutableTableRules {
private ImmutableTableRules() {}
/** Prefer {@link ImmutableTable#builder()} over the associated constructor. */
// XXX: This rule may drop generic type information, leading to non-compilable code.
static final class ImmutableTableBuilder<R, C, V> {
@BeforeTemplate
ImmutableTable.Builder<R, C, V> before() {
return new ImmutableTable.Builder<>();
}
@AfterTemplate
ImmutableTable.Builder<R, C, V> after() {
return ImmutableTable.builder();
}
}
/**
* Prefer {@link ImmutableTable.Builder#buildOrThrow()} over the less explicit {@link
* ImmutableTable.Builder#build()}.
*/
static final class ImmutableTableBuilderBuildOrThrow<R, C, V> {
@BeforeTemplate
ImmutableTable<R, C, V> before(ImmutableTable.Builder<R, C, V> builder) {
return builder.build();
}
@AfterTemplate
ImmutableTable<R, C, V> after(ImmutableTable.Builder<R, C, V> builder) {
return builder.buildOrThrow();
}
}
/** Prefer {@link ImmutableTable#of(Object, Object, Object)} over more contrived alternatives. */
static final class CellToImmutableTable<R, C, V> {
@BeforeTemplate
ImmutableTable<R, C, V> before(Table.Cell<? extends R, ? extends C, ? extends V> cell) {
return Refaster.anyOf(
ImmutableTable.<R, C, V>builder().put(cell).buildOrThrow(),
Stream.of(cell)
.collect(
toImmutableTable(
Table.Cell::getRowKey, Table.Cell::getColumnKey, Table.Cell::getValue)));
}
@AfterTemplate
ImmutableTable<R, C, V> after(Table.Cell<? extends R, ? extends C, ? extends V> cell) {
return ImmutableTable.of(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
}
}
/**
* Don't map a stream's elements to table cells, only to subsequently collect them into an {@link
* ImmutableTable}. The collection can be performed directly.
*/
abstract static class StreamOfCellsToImmutableTable<E, R, C, V> {
@Placeholder(allowsIdentity = true)
abstract R rowFunction(@MayOptionallyUse E element);
@Placeholder(allowsIdentity = true)
abstract C columnFunction(@MayOptionallyUse E element);
@Placeholder(allowsIdentity = true)
abstract V valueFunction(@MayOptionallyUse E element);
@BeforeTemplate
ImmutableTable<R, C, V> before(Stream<E> stream) {
return stream
.map(e -> Tables.immutableCell(rowFunction(e), columnFunction(e), valueFunction(e)))
.collect(
toImmutableTable(
Table.Cell::getRowKey, Table.Cell::getColumnKey, Table.Cell::getValue));
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
ImmutableTable<R, C, V> after(Stream<E> stream) {
return stream.collect(
toImmutableTable(e -> rowFunction(e), e -> columnFunction(e), e -> valueFunction(e)));
}
}
/** Prefer {@link ImmutableTable#of()} over more contrived alternatives . */
static final class ImmutableTableOf<R, C, V> {
@BeforeTemplate
ImmutableTable<R, C, V> before() {
return ImmutableTable.<R, C, V>builder().buildOrThrow();
}
@AfterTemplate
ImmutableTable<R, C, V> after() {
return ImmutableTable.of();
}
}
}

View File

@@ -5,6 +5,8 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.fail;
import static org.assertj.core.api.Assertions.offset;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
@@ -34,7 +36,8 @@ import tech.picnic.errorprone.refaster.annotation.TypeMigration;
* <p>Note that, while both libraries throw an {@link AssertionError} in case of an assertion
* failure, the exact subtype used generally differs.
*/
// XXX: Not all JUnit `Assertions` methods have an associated Refaster rule yet; expand this class.
// XXX: The `AssertThat*Array*ContainsExactly*` rules assume that `expected` and `actual` are not
// both `null`.
// XXX: Introduce a `@Matcher` on `Executable` and `ThrowingSupplier` expressions, such that they
// are only matched if they are also compatible with the `ThrowingCallable` functional interface.
// When implementing such a matcher, note that expressions with a non-void return type such as
@@ -50,39 +53,6 @@ import tech.picnic.errorprone.refaster.annotation.TypeMigration;
"assertAll(String, Collection<Executable>)",
"assertAll(String, Executable[])",
"assertAll(String, Stream<Executable>)",
"assertArrayEquals(boolean[], boolean[])",
"assertArrayEquals(boolean[], boolean[], String)",
"assertArrayEquals(boolean[], boolean[], Supplier<String>)",
"assertArrayEquals(byte[], byte[])",
"assertArrayEquals(byte[], byte[], String)",
"assertArrayEquals(byte[], byte[], Supplier<String>)",
"assertArrayEquals(char[], char[])",
"assertArrayEquals(char[], char[], String)",
"assertArrayEquals(char[], char[], Supplier<String>)",
"assertArrayEquals(double[], double[])",
"assertArrayEquals(double[], double[], double)",
"assertArrayEquals(double[], double[], double, String)",
"assertArrayEquals(double[], double[], double, Supplier<String>)",
"assertArrayEquals(double[], double[], String)",
"assertArrayEquals(double[], double[], Supplier<String>)",
"assertArrayEquals(float[], float[])",
"assertArrayEquals(float[], float[], float)",
"assertArrayEquals(float[], float[], float, String)",
"assertArrayEquals(float[], float[], float, Supplier<String>)",
"assertArrayEquals(float[], float[], String)",
"assertArrayEquals(float[], float[], Supplier<String>)",
"assertArrayEquals(int[], int[])",
"assertArrayEquals(int[], int[], String)",
"assertArrayEquals(int[], int[], Supplier<String>)",
"assertArrayEquals(long[], long[])",
"assertArrayEquals(long[], long[], String)",
"assertArrayEquals(long[], long[], Supplier<String>)",
"assertArrayEquals(Object[], Object[])",
"assertArrayEquals(Object[], Object[], String)",
"assertArrayEquals(Object[], Object[], Supplier<String>)",
"assertArrayEquals(short[], short[])",
"assertArrayEquals(short[], short[], String)",
"assertArrayEquals(short[], short[], Supplier<String>)",
"assertEquals(Byte, Byte)",
"assertEquals(Byte, byte)",
"assertEquals(byte, Byte)",
@@ -302,6 +272,436 @@ import tech.picnic.errorprone.refaster.annotation.TypeMigration;
final class JUnitToAssertJRules {
private JUnitToAssertJRules() {}
static final class AssertThatBooleanArrayContainsExactly {
@BeforeTemplate
void before(boolean[] actual, boolean[] expected) {
assertArrayEquals(expected, actual);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(boolean[] actual, boolean[] expected) {
assertThat(actual).containsExactly(expected);
}
}
static final class AssertThatBooleanArrayWithFailMessageContainsExactly {
@BeforeTemplate
void before(boolean[] actual, String message, boolean[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(boolean[] actual, String message, boolean[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatBooleanArrayWithFailMessageSupplierContainsExactly {
@BeforeTemplate
void before(boolean[] actual, Supplier<String> message, boolean[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(boolean[] actual, Supplier<String> message, boolean[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatByteArrayContainsExactly {
@BeforeTemplate
void before(byte[] actual, byte[] expected) {
assertArrayEquals(expected, actual);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(byte[] actual, byte[] expected) {
assertThat(actual).containsExactly(expected);
}
}
static final class AssertThatByteArrayWithFailMessageContainsExactly {
@BeforeTemplate
void before(byte[] actual, String message, byte[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(byte[] actual, String message, byte[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatByteArrayWithFailMessageSupplierContainsExactly {
@BeforeTemplate
void before(byte[] actual, Supplier<String> message, byte[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(byte[] actual, Supplier<String> message, byte[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatCharArrayContainsExactly {
@BeforeTemplate
void before(char[] actual, char[] expected) {
assertArrayEquals(expected, actual);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(char[] actual, char[] expected) {
assertThat(actual).containsExactly(expected);
}
}
static final class AssertThatCharArrayWithFailMessageContainsExactly {
@BeforeTemplate
void before(char[] actual, String message, char[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(char[] actual, String message, char[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatCharArrayWithFailMessageSupplierContainsExactly {
@BeforeTemplate
void before(char[] actual, Supplier<String> message, char[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(char[] actual, Supplier<String> message, char[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatShortArrayContainsExactly {
@BeforeTemplate
void before(short[] actual, short[] expected) {
assertArrayEquals(expected, actual);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(short[] actual, short[] expected) {
assertThat(actual).containsExactly(expected);
}
}
static final class AssertThatShortArrayWithFailMessageContainsExactly {
@BeforeTemplate
void before(short[] actual, String message, short[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(short[] actual, String message, short[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatShortArrayWithFailMessageSupplierContainsExactly {
@BeforeTemplate
void before(short[] actual, Supplier<String> message, short[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(short[] actual, Supplier<String> message, short[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatIntArrayContainsExactly {
@BeforeTemplate
void before(int[] actual, int[] expected) {
assertArrayEquals(expected, actual);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(int[] actual, int[] expected) {
assertThat(actual).containsExactly(expected);
}
}
static final class AssertThatIntArrayWithFailMessageContainsExactly {
@BeforeTemplate
void before(int[] actual, String message, int[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(int[] actual, String message, int[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatIntArrayWithFailMessageSupplierContainsExactly {
@BeforeTemplate
void before(int[] actual, Supplier<String> message, int[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(int[] actual, Supplier<String> message, int[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatLongArrayContainsExactly {
@BeforeTemplate
void before(long[] actual, long[] expected) {
assertArrayEquals(expected, actual);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(long[] actual, long[] expected) {
assertThat(actual).containsExactly(expected);
}
}
static final class AssertThatLongArrayWithFailMessageContainsExactly {
@BeforeTemplate
void before(long[] actual, String message, long[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(long[] actual, String message, long[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatLongArrayWithFailMessageSupplierContainsExactly {
@BeforeTemplate
void before(long[] actual, Supplier<String> message, long[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(long[] actual, Supplier<String> message, long[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatFloatArrayContainsExactly {
@BeforeTemplate
void before(float[] actual, float[] expected) {
assertArrayEquals(expected, actual);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(float[] actual, float[] expected) {
assertThat(actual).containsExactly(expected);
}
}
static final class AssertThatFloatArrayWithFailMessageContainsExactly {
@BeforeTemplate
void before(float[] actual, String message, float[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(float[] actual, String message, float[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatFloatArrayWithFailMessageSupplierContainsExactly {
@BeforeTemplate
void before(float[] actual, Supplier<String> message, float[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(float[] actual, Supplier<String> message, float[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatFloatArrayContainsExactlyWithOffset {
@BeforeTemplate
void before(float[] actual, float[] expected, float delta) {
assertArrayEquals(expected, actual, delta);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(float[] actual, float[] expected, float delta) {
assertThat(actual).containsExactly(expected, offset(delta));
}
}
static final class AssertThatFloatArrayWithFailMessageContainsExactlyWithOffset {
@BeforeTemplate
void before(float[] actual, String message, float[] expected, float delta) {
assertArrayEquals(expected, actual, delta, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(float[] actual, String message, float[] expected, float delta) {
assertThat(actual).withFailMessage(message).containsExactly(expected, offset(delta));
}
}
static final class AssertThatFloatArrayWithFailMessageSupplierContainsExactlyWithOffset {
@BeforeTemplate
void before(float[] actual, Supplier<String> message, float[] expected, float delta) {
assertArrayEquals(expected, actual, delta, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(float[] actual, Supplier<String> message, float[] expected, float delta) {
assertThat(actual).withFailMessage(message).containsExactly(expected, offset(delta));
}
}
static final class AssertThatDoubleArrayContainsExactly {
@BeforeTemplate
void before(double[] actual, double[] expected) {
assertArrayEquals(expected, actual);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(double[] actual, double[] expected) {
assertThat(actual).containsExactly(expected);
}
}
static final class AssertThatDoubleArrayWithFailMessageContainsExactly {
@BeforeTemplate
void before(double[] actual, String message, double[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(double[] actual, String message, double[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatDoubleArrayWithFailMessageSupplierContainsExactly {
@BeforeTemplate
void before(double[] actual, Supplier<String> message, double[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(double[] actual, Supplier<String> message, double[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatDoubleArrayContainsExactlyWithOffset {
@BeforeTemplate
void before(double[] actual, double[] expected, double delta) {
assertArrayEquals(expected, actual, delta);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(double[] actual, double[] expected, double delta) {
assertThat(actual).containsExactly(expected, offset(delta));
}
}
static final class AssertThatDoubleArrayWithFailMessageContainsExactlyWithOffset {
@BeforeTemplate
void before(double[] actual, String message, double[] expected, double delta) {
assertArrayEquals(expected, actual, delta, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(double[] actual, String message, double[] expected, double delta) {
assertThat(actual).withFailMessage(message).containsExactly(expected, offset(delta));
}
}
static final class AssertThatDoubleArrayWithFailMessageSupplierContainsExactlyWithOffset {
@BeforeTemplate
void before(
double[] actual, Supplier<String> messageSupplier, double[] expected, double delta) {
assertArrayEquals(expected, actual, delta, messageSupplier);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(double[] actual, Supplier<String> messageSupplier, double[] expected, double delta) {
assertThat(actual).withFailMessage(messageSupplier).containsExactly(expected, offset(delta));
}
}
static final class AssertThatObjectArrayContainsExactly {
@BeforeTemplate
void before(Object[] actual, Object[] expected) {
assertArrayEquals(expected, actual);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(Object[] actual, Object[] expected) {
assertThat(actual).containsExactly(expected);
}
}
static final class AssertThatObjectArrayWithFailMessageContainsExactly {
@BeforeTemplate
void before(Object[] actual, String message, Object[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(Object[] actual, String message, Object[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class AssertThatObjectArrayWithFailMessageSupplierContainsExactly {
@BeforeTemplate
void before(Object[] actual, Supplier<String> message, Object[] expected) {
assertArrayEquals(expected, actual, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(Object[] actual, Supplier<String> message, Object[] expected) {
assertThat(actual).withFailMessage(message).containsExactly(expected);
}
}
static final class Fail<T> {
@BeforeTemplate
T before() {

View File

@@ -3,6 +3,7 @@ package tech.picnic.errorprone.refasterrules;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import static java.util.Objects.requireNonNullElse;
import static java.util.Objects.requireNonNullElseGet;
import static java.util.function.Predicate.not;
import com.google.common.base.MoreObjects;
import com.google.errorprone.refaster.Refaster;
@@ -94,11 +95,14 @@ final class NullRules {
}
}
/** Prefer {@link Objects#isNull(Object)} over the equivalent lambda function. */
/**
* Prefer {@link Objects#isNull(Object)} over the equivalent lambda function or more contrived
* alternatives.
*/
static final class IsNullFunction<T> {
@BeforeTemplate
Predicate<T> before() {
return o -> o == null;
return Refaster.anyOf(o -> o == null, not(Objects::nonNull));
}
@AfterTemplate
@@ -107,11 +111,14 @@ final class NullRules {
}
}
/** Prefer {@link Objects#nonNull(Object)} over the equivalent lambda function. */
/**
* Prefer {@link Objects#nonNull(Object)} over the equivalent lambda function or more contrived
* alternatives.
*/
static final class NonNullFunction<T> {
@BeforeTemplate
Predicate<T> before() {
return o -> o != null;
return Refaster.anyOf(o -> o != null, not(Objects::isNull));
}
@AfterTemplate

View File

@@ -282,6 +282,8 @@ final class OptionalRules {
// XXX: The rewritten `filter`/`map` expression may be more performant than its replacement. See
// https://github.com/palantir/gradle-baseline/pull/2946. (There are plans to pair Refaster rules
// with JMH benchmarks; this would be a great use case.)
// XXX: Perhaps `stream.mapMulti(Optional::ifPresent)` is what we should use. See
// https://github.com/palantir/gradle-baseline/pull/2996.
static final class StreamFlatMapOptional<T> {
@BeforeTemplate
Stream<T> before(Stream<Optional<T>> stream) {

View File

@@ -8,12 +8,15 @@ import com.google.common.primitives.Floats;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.common.primitives.Shorts;
import com.google.common.primitives.UnsignedBytes;
import com.google.common.primitives.UnsignedInts;
import com.google.common.primitives.UnsignedLongs;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.AlsoNegation;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import java.util.Arrays;
import java.util.Comparator;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to expressions dealing with primitives. */
@@ -525,10 +528,7 @@ final class PrimitiveRules {
}
}
/**
* Prefer JDK's {@link Integer#toUnsignedString(int,int)} over third-party or more verbose
* alternatives.
*/
/** Prefer JDK's {@link Integer#toUnsignedString(int,int)} over third-party alternatives. */
static final class IntegerToUnsignedStringWithRadix {
@BeforeTemplate
String before(int i, int radix) {
@@ -541,10 +541,7 @@ final class PrimitiveRules {
}
}
/**
* Prefer JDK's {@link Long#toUnsignedString(long,int)} over third-party or more verbose
* alternatives.
*/
/** Prefer JDK's {@link Long#toUnsignedString(long,int)} over third-party alternatives. */
static final class LongToUnsignedStringWithRadix {
@BeforeTemplate
String before(long i, int radix) {
@@ -556,4 +553,49 @@ final class PrimitiveRules {
return Long.toUnsignedString(i, radix);
}
}
/** Prefer JDK's {@link Arrays#compareUnsigned(byte[], byte[])} over third-party alternatives. */
// XXX: This rule will yield non-compilable code if the result of the replaced expression is
// dereferenced. Investigate how to make this safe.
static final class ArraysCompareUnsignedBytes {
@BeforeTemplate
Comparator<byte[]> before() {
return UnsignedBytes.lexicographicalComparator();
}
@AfterTemplate
Comparator<byte[]> after() {
return Arrays::compareUnsigned;
}
}
/** Prefer JDK's {@link Arrays#compareUnsigned(int[], int[])} over third-party alternatives. */
// XXX: This rule will yield non-compilable code if the result of the replaced expression is
// dereferenced. Investigate how to make this safe.
static final class ArraysCompareUnsignedInts {
@BeforeTemplate
Comparator<int[]> before() {
return UnsignedInts.lexicographicalComparator();
}
@AfterTemplate
Comparator<int[]> after() {
return Arrays::compareUnsigned;
}
}
/** Prefer JDK's {@link Arrays#compareUnsigned(long[], long[])} over third-party alternatives. */
// XXX: This rule will yield non-compilable code if the result of the replaced expression is
// dereferenced. Investigate how to make this safe.
static final class ArraysCompareUnsignedLongs {
@BeforeTemplate
Comparator<long[]> before() {
return UnsignedLongs.lexicographicalComparator();
}
@AfterTemplate
Comparator<long[]> after() {
return Arrays::compareUnsigned;
}
}
}

View File

@@ -489,9 +489,16 @@ final class ReactorRules {
return Flux.range(value, 1);
}
// XXX: Consider generalizing part of this template using an Error Prone check that covers any
// sequence of explicitly enumerated values passed to an iteration order-preserving collection
// factory method.
@BeforeTemplate
Flux<T> before(T value) {
return Mono.just(value).repeat().take(1);
return Refaster.anyOf(
Mono.just(value).flux(),
Mono.just(value).repeat().take(1),
Flux.fromIterable(ImmutableList.of(value)),
Flux.fromIterable(ImmutableSet.of(value)));
}
@AfterTemplate
@@ -889,6 +896,38 @@ final class ReactorRules {
}
}
/**
* Prefer immediately unwrapping {@link Optional} transformation results inside {@link
* Flux#mapNotNull(Function)} over more contrived alternatives.
*/
abstract static class FluxMapNotNullTransformationOrElse<T, S> {
@Placeholder(allowsIdentity = true)
abstract Optional<S> transformation(@MayOptionallyUse T value);
@BeforeTemplate
Flux<S> before(Flux<T> flux) {
return flux.map(v -> transformation(v)).mapNotNull(o -> o.orElse(null));
}
@AfterTemplate
Flux<S> after(Flux<T> flux) {
return flux.mapNotNull(x -> transformation(x).orElse(null));
}
}
/** Prefer {@link Flux#mapNotNull(Function)} over more contrived alternatives. */
static final class FluxMapNotNullOrElse<T> {
@BeforeTemplate
Flux<T> before(Flux<Optional<T>> flux) {
return flux.filter(Optional::isPresent).map(Optional::orElseThrow);
}
@AfterTemplate
Flux<T> after(Flux<Optional<T>> flux) {
return flux.mapNotNull(x -> x.orElse(null));
}
}
/** Prefer {@link Mono#flux()}} over more contrived alternatives. */
static final class MonoFlux<T> {
@BeforeTemplate
@@ -907,7 +946,11 @@ final class ReactorRules {
static final class MonoThen<T> {
@BeforeTemplate
Mono<@Nullable Void> before(Mono<T> mono) {
return Refaster.anyOf(mono.ignoreElement().then(), mono.flux().then());
return Refaster.anyOf(
mono.ignoreElement().then(),
mono.flux().then(),
Mono.when(mono),
Mono.whenDelayError(mono));
}
@AfterTemplate
@@ -1046,10 +1089,12 @@ final class ReactorRules {
// rule. Consider introducing an Error Prone check for this.
static final class MonoSingleOptional<T> {
@BeforeTemplate
Mono<Optional<T>> before(Mono<T> mono) {
Mono<Optional<T>> before(Mono<T> mono, Optional<T> optional, Mono<Optional<T>> alternate) {
return Refaster.anyOf(
mono.flux().collect(toOptional()),
mono.map(Optional::of).defaultIfEmpty(Optional.empty()),
mono.map(Optional::of),
mono.singleOptional().defaultIfEmpty(optional),
mono.singleOptional().switchIfEmpty(alternate),
mono.transform(Mono::singleOptional));
}
@@ -1742,6 +1787,91 @@ final class ReactorRules {
}
}
/** Prefer {@link PublisherProbe#assertWasSubscribed()} over more verbose alternatives. */
static final class PublisherProbeAssertWasSubscribed<T> {
@BeforeTemplate
void before(PublisherProbe<T> probe) {
Refaster.anyOf(
assertThat(probe.wasSubscribed()).isTrue(),
assertThat(probe.subscribeCount()).isNotNegative(),
assertThat(probe.subscribeCount()).isNotEqualTo(0),
assertThat(probe.subscribeCount()).isPositive());
}
@AfterTemplate
void after(PublisherProbe<T> probe) {
probe.assertWasSubscribed();
}
}
/** Prefer {@link PublisherProbe#assertWasNotSubscribed()} over more verbose alternatives. */
static final class PublisherProbeAssertWasNotSubscribed<T> {
@BeforeTemplate
void before(PublisherProbe<T> probe) {
Refaster.anyOf(
assertThat(probe.wasSubscribed()).isFalse(),
assertThat(probe.subscribeCount()).isEqualTo(0),
assertThat(probe.subscribeCount()).isNotPositive());
}
@AfterTemplate
void after(PublisherProbe<T> probe) {
probe.assertWasNotSubscribed();
}
}
/** Prefer {@link PublisherProbe#assertWasCancelled()} over more verbose alternatives. */
static final class PublisherProbeAssertWasCancelled<T> {
@BeforeTemplate
void before(PublisherProbe<T> probe) {
assertThat(probe.wasCancelled()).isTrue();
}
@AfterTemplate
void after(PublisherProbe<T> probe) {
probe.assertWasCancelled();
}
}
/** Prefer {@link PublisherProbe#assertWasNotCancelled()} over more verbose alternatives. */
static final class PublisherProbeAssertWasNotCancelled<T> {
@BeforeTemplate
void before(PublisherProbe<T> probe) {
assertThat(probe.wasCancelled()).isFalse();
}
@AfterTemplate
void after(PublisherProbe<T> probe) {
probe.assertWasNotCancelled();
}
}
/** Prefer {@link PublisherProbe#assertWasRequested()} over more verbose alternatives. */
static final class PublisherProbeAssertWasRequested<T> {
@BeforeTemplate
void before(PublisherProbe<T> probe) {
assertThat(probe.wasRequested()).isTrue();
}
@AfterTemplate
void after(PublisherProbe<T> probe) {
probe.assertWasRequested();
}
}
/** Prefer {@link PublisherProbe#assertWasNotRequested()} over more verbose alternatives. */
static final class PublisherProbeAssertWasNotRequested<T> {
@BeforeTemplate
void before(PublisherProbe<T> probe) {
assertThat(probe.wasRequested()).isFalse();
}
@AfterTemplate
void after(PublisherProbe<T> probe) {
probe.assertWasNotRequested();
}
}
/** Prefer {@link Mono#as(Function)} when creating a {@link StepVerifier}. */
static final class StepVerifierFromMono<T> {
@BeforeTemplate
@@ -2057,6 +2187,22 @@ final class ReactorRules {
}
}
/**
* Don't propagate {@link Mono} cancellations to upstream cache value computations, as completion
* of such computations may benefit concurrent or subsequent cache usages.
*/
static final class MonoFromFutureAsyncLoadingCacheGetAll<K1, K2 extends K1, V> {
@BeforeTemplate
Mono<Map<K1, V>> before(AsyncLoadingCache<K1, V> cache, Iterable<K2> keys) {
return Mono.fromFuture(() -> cache.getAll(keys));
}
@AfterTemplate
Mono<Map<K1, V>> after(AsyncLoadingCache<K1, V> cache, Iterable<K2> keys) {
return Mono.fromFuture(() -> cache.getAll(keys), /* suppressCancel= */ true);
}
}
/**
* Prefer {@link Flux#fromStream(Supplier)} over {@link Flux#fromStream(Stream)}, as the former
* yields a {@link Flux} that is more likely to behave as expected when subscribed to more than

View File

@@ -297,6 +297,22 @@ final class StreamRules {
}
}
/**
* Prefer an unconditional {@link Map#get(Object)} call followed by a {@code null} check over a
* call to {@link Map#containsKey(Object)}, as the former avoids a second lookup operation.
*/
static final class StreamMapFilter<T, K, V> {
@BeforeTemplate
Stream<V> before(Stream<T> stream, Map<K, V> map) {
return stream.filter(map::containsKey).map(map::get);
}
@AfterTemplate
Stream<V> after(Stream<T> stream, Map<K, V> map) {
return stream.map(map::get).filter(Objects::nonNull);
}
}
static final class StreamMin<T> {
@BeforeTemplate
@SuppressWarnings("java:S4266" /* This violation will be rewritten. */)

View File

@@ -29,11 +29,14 @@ final class StringRules {
private StringRules() {}
/** Prefer {@link String#isEmpty()} over alternatives that consult the string's length. */
// XXX: Now that we build with JDK 15+, this rule can be generalized to cover all `CharSequence`
// subtypes. This does require a mechanism (perhaps an annotation, or a separate Maven module) to
// make sure that non-String expressions are rewritten only if client code also targets JDK 15+.
// XXX: Drop this rule once we (and OpenRewrite) no longer support projects targeting Java 14 or
// below. The `CharSequenceIsEmpty` rule then suffices. (This rule exists so that e.g. projects
// that target JDK 11 can disable `CharSequenceIsEmpty` without losing a valuable rule.)
// XXX: Look into a more general approach to supporting different Java language levels, such as
// rule selection based on some annotation, or a separate Maven module.
static final class StringIsEmpty {
@BeforeTemplate
@SuppressWarnings("CharSequenceIsEmpty" /* This is a more specific template. */)
boolean before(String str) {
return Refaster.anyOf(str.length() == 0, str.length() <= 0, str.length() < 1);
}

View File

@@ -1,5 +1,6 @@
package tech.picnic.errorprone.refasterrules;
import com.google.errorprone.VisitorState;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
@@ -65,16 +66,18 @@ final class SuggestedFixRules {
}
}
/** Prefer {@link SuggestedFix#swap(Tree, Tree)} over more contrived alternatives. */
/**
* Prefer {@link SuggestedFix#swap(Tree, Tree, VisitorState)} over more contrived alternatives.
*/
static final class SuggestedFixSwap {
@BeforeTemplate
SuggestedFix before(Tree tree1, Tree tree2) {
return SuggestedFix.builder().swap(tree1, tree2).build();
SuggestedFix before(Tree tree1, Tree tree2, VisitorState state) {
return SuggestedFix.builder().swap(tree1, tree2, state).build();
}
@AfterTemplate
SuggestedFix after(Tree tree1, Tree tree2) {
return SuggestedFix.swap(tree1, tree2);
SuggestedFix after(Tree tree1, Tree tree2, VisitorState state) {
return SuggestedFix.swap(tree1, tree2, state);
}
}

View File

@@ -53,71 +53,20 @@ import tech.picnic.errorprone.refaster.annotation.TypeMigration;
// XXX: As-is these rules do not result in a complete migration:
// - Expressions containing comments are skipped due to a limitation of Refaster.
// - Assertions inside lambda expressions are also skipped. Unclear why.
// XXX: The `assertEquals` tests for this class generally use the same expression for `expected` and
// XXX: Many of the test expressions for these rules use the same expression for `expected` and
// `actual`, which makes the validation weaker than necessary; fix this. (And investigate whether we
// can introduce validation for this.)
@OnlineDocumentation
@TypeMigration(
of = Assert.class,
unmigratedMethods = {
// XXX: Add migrations for the methods below.
"assertEquals(Boolean, Boolean)",
"assertEquals(Boolean, boolean)",
"assertEquals(boolean, Boolean)",
"assertEquals(Boolean, Boolean, String)",
"assertEquals(Boolean, boolean, String)",
"assertEquals(boolean, Boolean, String)",
"assertEquals(Byte, Byte)",
"assertEquals(Byte, byte)",
"assertEquals(byte, Byte)",
"assertEquals(Byte, Byte, String)",
"assertEquals(Byte, byte, String)",
"assertEquals(byte, Byte, String)",
"assertEquals(char, Character)",
"assertEquals(char, Character, String)",
"assertEquals(Character, char)",
"assertEquals(Character, char, String)",
"assertEquals(Character, Character)",
"assertEquals(Character, Character, String)",
"assertEquals(Double, Double)",
"assertEquals(Double, double)",
"assertEquals(double, Double)",
"assertEquals(Double, Double, String)",
"assertEquals(Double, double, String)",
"assertEquals(double, Double, String)",
"assertEquals(double[], double[], double)",
"assertEquals(double[], double[], double, String)",
"assertEquals(Float, Float)",
"assertEquals(Float, float)",
"assertEquals(float, Float)",
"assertEquals(Float, Float, String)",
"assertEquals(Float, float, String)",
"assertEquals(float, Float, String)",
"assertEquals(float[], float[], float)",
"assertEquals(float[], float[], float, String)",
"assertEquals(int, Integer)",
"assertEquals(int, Integer, String)",
"assertEquals(Integer, int)",
"assertEquals(Integer, int, String)",
"assertEquals(Integer, Integer)",
"assertEquals(Integer, Integer, String)",
"assertEquals(Long, Long)",
"assertEquals(Long, long)",
"assertEquals(long, Long)",
"assertEquals(Long, Long, String)",
"assertEquals(Long, long, String)",
"assertEquals(Short, Short)",
"assertEquals(Short, short)",
"assertEquals(short, Short)",
"assertEquals(Short, Short, String)",
"assertEquals(Short, short, String)",
"assertEquals(short, Short, String)",
/*
* These `assertEqualsDeep` methods cannot (easily) be expressed using AssertJ because they
* mix regular equality and array equality:
*/
"assertEqualsDeep(Map<?, ?>, Map<?, ?>)",
"assertEqualsDeep(Map<?, ?>, Map<?, ?>, String)",
"assertEqualsDeep(Set<?>, Set<?>)",
"assertEqualsDeep(Set<?>, Set<?>, String)",
// XXX: Add migrations for the methods below.
"assertEqualsNoOrder(Collection<?>, Collection<?>)",
@@ -351,47 +300,168 @@ final class TestNGToAssertJRules {
}
}
@SuppressWarnings("java:S1448" /* Each variant requires a separate `@BeforeTemplate` method. */)
static final class AssertEqual {
@BeforeTemplate
void before(boolean actual, boolean expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(boolean actual, Boolean expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Boolean actual, boolean expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Boolean actual, Boolean expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(byte actual, byte expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(byte actual, Byte expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Byte actual, byte expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Byte actual, Byte expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(char actual, char expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(char actual, Character expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Character actual, char expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Character actual, Character expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(short actual, short expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(short actual, Short expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Short actual, short expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Short actual, Short expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(int actual, int expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(int actual, Integer expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Integer actual, int expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Integer actual, Integer expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(long actual, long expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(long actual, Long expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Long actual, long expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Long actual, Long expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(float actual, float expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(float actual, Float expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Float actual, float expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Float actual, Float expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(double actual, double expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(double actual, Double expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Double actual, double expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Double actual, Double expected) {
assertEquals(actual, expected);
}
@BeforeTemplate
void before(Object actual, Object expected) {
assertEquals(actual, expected);
@@ -414,47 +484,168 @@ final class TestNGToAssertJRules {
}
}
@SuppressWarnings("java:S1448" /* Each variant requires a separate `@BeforeTemplate` method. */)
static final class AssertEqualWithMessage {
@BeforeTemplate
void before(boolean actual, String message, boolean expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(boolean actual, String message, Boolean expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Boolean actual, String message, boolean expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Boolean actual, String message, Boolean expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(byte actual, String message, byte expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(byte actual, String message, Byte expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Byte actual, String message, byte expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Byte actual, String message, Byte expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(char actual, String message, char expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(char actual, String message, Character expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Character actual, String message, char expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Character actual, String message, Character expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(short actual, String message, short expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(short actual, String message, Short expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Short actual, String message, short expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Short actual, String message, Short expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(int actual, String message, int expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(int actual, String message, Integer expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Integer actual, String message, int expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Integer actual, String message, Integer expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(long actual, String message, long expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(long actual, String message, Long expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Long actual, String message, long expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Long actual, String message, Long expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(float actual, String message, float expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(float actual, String message, Float expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Float actual, String message, float expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Float actual, String message, Float expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(double actual, String message, double expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(double actual, String message, Double expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Double actual, String message, double expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Double actual, String message, Double expected) {
assertEquals(actual, expected, message);
}
@BeforeTemplate
void before(Object actual, String message, Object expected) {
assertEquals(actual, expected, message);
@@ -485,7 +676,7 @@ final class TestNGToAssertJRules {
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(float actual, float expected, float delta) {
void after(Float actual, float expected, float delta) {
assertThat(actual).isCloseTo(expected, offset(delta));
}
}
@@ -635,6 +826,58 @@ final class TestNGToAssertJRules {
}
}
static final class AssertEqualFloatArraysWithDelta {
@BeforeTemplate
void before(float[] actual, float[] expected, float delta) {
assertEquals(actual, expected, delta);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(float[] actual, float[] expected, float delta) {
assertThat(actual).containsExactly(expected, offset(delta));
}
}
static final class AssertEqualFloatArraysWithDeltaWithMessage {
@BeforeTemplate
void before(float[] actual, String message, float[] expected, float delta) {
assertEquals(actual, expected, delta, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(float[] actual, String message, float[] expected, float delta) {
assertThat(actual).withFailMessage(message).containsExactly(expected, offset(delta));
}
}
static final class AssertEqualDoubleArraysWithDelta {
@BeforeTemplate
void before(double[] actual, double[] expected, double delta) {
assertEquals(actual, expected, delta);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(double[] actual, double[] expected, double delta) {
assertThat(actual).containsExactly(expected, offset(delta));
}
}
static final class AssertEqualDoubleArraysWithDeltaWithMessage {
@BeforeTemplate
void before(double[] actual, String message, double[] expected, double delta) {
assertEquals(actual, expected, delta, message);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(double[] actual, String message, double[] expected, double delta) {
assertThat(actual).withFailMessage(message).containsExactly(expected, offset(delta));
}
}
static final class AssertEqualArraysIrrespectiveOfOrder {
@BeforeTemplate
void before(Object[] actual, Object[] expected) {

View File

@@ -16,7 +16,7 @@ final class ClassCastLambdaUsageTest {
"import java.util.stream.Stream;",
"",
"class A {",
" void m() {",
" <T> void m() {",
" Number localVariable = 0;",
"",
" Stream.of(0).map(i -> i);",
@@ -32,13 +32,14 @@ final class ClassCastLambdaUsageTest {
" 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);",
" Stream.<ImmutableSet>of(ImmutableSet.of(6)).map(s -> (ImmutableSet<Number>) s);",
" Stream.of(ImmutableSet.of(7)).map(s -> (ImmutableSet<?>) s);",
" Stream.of(8).reduce((a, b) -> (Integer) a);",
" IntStream.of(9).mapToObj(i -> (char) i);",
" Stream.of(10).map(i -> (T) i);",
"",
" // BUG: Diagnostic contains:",
" Stream.of(8).map(i -> (Integer) i);",
" Stream.of(11).map(i -> (Integer) i);",
" }",
"}")
.doTest();

View File

@@ -0,0 +1,286 @@
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 EagerStringFormattingTest {
@Test
void identification() {
CompilationTestHelper.newInstance(EagerStringFormatting.class, getClass())
.expectErrorMessage("DEFER", m -> m.contains("String formatting can be deferred\n"))
.expectErrorMessage(
"DEFER_EXTRA_VARIABLE",
m ->
m.contains(
"String formatting can be deferred (but this requires introducing an effectively final variable)"))
.expectErrorMessage(
"DEFER_SIMPLIFIED_GUAVA",
m ->
m.contains(
"String formatting can be deferred (assuming that Guava's simplified formatting support suffices)"))
.expectErrorMessage(
"DEFER_SIMPLIFIED_SLF4J",
m ->
m.contains(
"String formatting can be deferred (assuming that SLF4J's simplified formatting support suffices)"))
.expectErrorMessage(
"VACUOUS", m -> m.contains("String formatting never yields `null` expression"))
.addSourceLines(
"A.java",
"import static com.google.common.base.Preconditions.checkArgument;",
"import static com.google.common.base.Preconditions.checkNotNull;",
"import static com.google.common.base.Preconditions.checkState;",
"import static com.google.common.base.Verify.verify;",
"import static com.google.common.base.Verify.verifyNotNull;",
"import static java.util.Objects.requireNonNull;",
"",
"import java.util.Formattable;",
"import java.util.Locale;",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"import org.slf4j.Marker;",
"",
"class A {",
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
"",
" private int nonFinalField = 0;",
"",
" void m() {",
" Formattable formattable = (formatter, flags, width, precision) -> {};",
" int effectivelyFinalLocal = 0;",
" /* A local variable that is also not effectively final. */",
" int nonFinalLocal = 0;",
" nonFinalLocal = 1;",
"",
" String.format(\"%s\", \"foo\");",
" String.format(Locale.US, \"%s\", \"foo\");",
" \"%s\".formatted(\"foo\");",
" String.format(\"%s\", \"foo\", \"bar\");",
" String.format(\"%s %s\", \"foo\", \"bar\");",
" String.format(\"%s %s %%\", \"foo\", \"bar\");",
"",
" System.out.println(String.format(\"%s\", nonFinalLocal));",
"",
" requireNonNull(\"never-null\");",
" requireNonNull(\"never-null\", () -> String.format(\"Format string: %s\", nonFinalField));",
" // BUG: Diagnostic matches: VACUOUS",
" requireNonNull(String.format(\"Never-null format string: %s\", nonFinalField));",
" // BUG: Diagnostic matches: VACUOUS",
" requireNonNull(\"Never-null format string: %s\".formatted(nonFinalField), \"message\");",
" // BUG: Diagnostic matches: VACUOUS",
" requireNonNull(",
" String.format(\"Never-null format string\"), String.format(\"Malformed format string: %\"));",
" // BUG: Diagnostic matches: DEFER_EXTRA_VARIABLE",
" requireNonNull(\"never-null\", String.format(\"Format string: %s\", nonFinalLocal));",
" // BUG: Diagnostic matches: DEFER",
" requireNonNull(\"never-null\", String.format(\"Format string: %s\", effectivelyFinalLocal));",
" // BUG: Diagnostic matches: DEFER",
" requireNonNull(",
" \"never-null\",",
" String.format(",
" \"Custom format string: %s, %d, %s\", getClass(), nonFinalField, \"string-constant\"));",
"",
" checkArgument(true);",
" checkNotNull(\"never-null\");",
" checkState(false);",
" verify(true);",
" verifyNotNull(\"never-null\");",
" checkArgument(false, \"Without format string\");",
" checkNotNull(\"never-null\", \"Without format string\");",
" checkState(true, \"Without format string\");",
" verify(false, \"Without format string\");",
" verifyNotNull(\"never-null\", \"Without format string\");",
" checkArgument(true, \"With format string: %s\", nonFinalLocal);",
" checkNotNull(\"never-null\", \"With format string: %s\", nonFinalLocal);",
" checkState(false, \"With format string: %s\", nonFinalLocal);",
" verify(true, \"With format string: %s\", nonFinalLocal);",
" verifyNotNull(\"never-null\", \"With format string: %s\", nonFinalLocal);",
" // BUG: Diagnostic matches: VACUOUS",
" checkNotNull(String.format(\"Never-null format string: %s\", nonFinalLocal));",
" // BUG: Diagnostic matches: VACUOUS",
" verifyNotNull(\"Never-null format string: %s\".formatted(nonFinalLocal), \"message\");",
" // BUG: Diagnostic matches: VACUOUS",
" checkNotNull(",
" String.format(\"Never-null format string\"), String.format(\"Malformed format string: %\"));",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA",
" checkArgument(true, String.format(toString()));",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA",
" checkNotNull(\"never-null\", toString().formatted());",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA",
" checkState(true, String.format(\"Custom format string: %d\", nonFinalLocal));",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA",
" verify(true, \"Mismatched format string:\".formatted(nonFinalLocal));",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA",
" verifyNotNull(\"never-null\", \"Mismatched format string: %d\".formatted());",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA",
" checkArgument(true, String.format(\"Malformed format string: %\"));",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA",
" checkNotNull(\"never-null\", \"Format string with `Formattable`: %s\".formatted(formattable));",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA",
" checkState(true, String.format(\"Generated format string: %%s\"), nonFinalLocal);",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA",
" verify(",
" true,",
" \"Format string with format string argument: %s\",",
" String.format(\"Format string argument: %s\", nonFinalLocal));",
" // BUG: Diagnostic matches: DEFER",
" verifyNotNull(",
" \"never-null\", String.format(\"Format string: %s, %s\", nonFinalLocal, nonFinalLocal));",
" // BUG: Diagnostic matches: DEFER",
" checkArgument(true, \"Format string: %s%%\".formatted(nonFinalLocal));",
" // BUG: Diagnostic matches: DEFER",
" checkNotNull(",
" \"never-null\", String.format(Locale.US, \"Format string with locale: %s\", nonFinalLocal));",
"",
" LOG.trace(\"Without format string\");",
" LOG.debug(\"With format string: {}\", nonFinalLocal);",
" LOG.info((Marker) null, \"With marker\");",
" LOG.warn((Marker) null, \"With marker and format string: {}\", nonFinalLocal);",
" LOG.error(\"With throwable\", new RuntimeException());",
" LOG.trace(\"With throwable and format string: {}\", nonFinalLocal, new RuntimeException());",
" LOG.debug((Marker) null, \"With marker and throwable\", new RuntimeException());",
" LOG.info(",
" (Marker) null,",
" \"With marker, throwable and format string: {}\",",
" nonFinalLocal,",
" new RuntimeException());",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J",
" LOG.warn(String.format(toString()));",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J",
" LOG.error(toString().formatted());",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J",
" LOG.trace(String.format(\"Custom format string: %d\", nonFinalLocal));",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J",
" LOG.debug(\"Mismatched format string:\".formatted(nonFinalLocal));",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J",
" LOG.info(\"Mismatched format string %d:\".formatted());",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J",
" LOG.warn(String.format(\"Malformed format string: %\"));",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J",
" LOG.error(\"Format string with `Formattable`: %s\".formatted(formattable));",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J",
" LOG.trace(String.format(\"Generated format string: {}\"), nonFinalLocal);",
" // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J",
" LOG.debug(",
" \"Format string with format string argument: {}\",",
" String.format(\"Format string argument: %s\", nonFinalLocal));",
" // BUG: Diagnostic matches: DEFER",
" LOG.info(String.format(\"Vacuous format string %%\"));",
" // BUG: Diagnostic matches: DEFER",
" LOG.warn(String.format(\"With format string: %s, %s\", nonFinalLocal, nonFinalLocal));",
" // BUG: Diagnostic matches: DEFER",
" LOG.error(String.format(Locale.ROOT, \"With vacuous localized format string %%\"));",
" // BUG: Diagnostic matches: DEFER",
" LOG.trace((Marker) null, String.format(\"With marker and format string: %s\", nonFinalLocal));",
" // BUG: Diagnostic matches: DEFER",
" LOG.debug(",
" String.format(\"With throwable and format string: %s\", nonFinalLocal),",
" new RuntimeException());",
" // BUG: Diagnostic matches: DEFER",
" LOG.info(",
" (Marker) null,",
" String.format(\"With marker, throwable and format string: %s\", nonFinalLocal),",
" new RuntimeException());",
" }",
"}")
.doTest();
}
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(EagerStringFormatting.class, getClass())
.addInputLines(
"A.java",
"import static com.google.common.base.Preconditions.checkArgument;",
"import static com.google.common.base.Preconditions.checkNotNull;",
"import static com.google.common.base.Preconditions.checkState;",
"import static com.google.common.base.Verify.verify;",
"import static com.google.common.base.Verify.verifyNotNull;",
"import static java.util.Objects.requireNonNull;",
"",
"import java.util.Locale;",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"import org.slf4j.Marker;",
"",
"class A {",
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
" private static final String GUAVA_COMPATIBLE_PATTERN = \"with-only-%s-placeholder\";",
" private static final String GUAVA_INCOMPATIBLE_PATTERN = \"with-%%-marker\";",
"",
" void m() {",
" requireNonNull(\"never-null\", String.format(\"Format string: %s\", 0));",
"",
" checkArgument(true, String.format(\"Vacuous format string %%\"));",
" checkNotNull(\"never-null\", \"Format string: %s %s%%\".formatted(1, 2));",
" checkState(false, String.format(Locale.US, \"Format string with locale: %s\", 3));",
" verify(true, GUAVA_COMPATIBLE_PATTERN.formatted(4));",
" verifyNotNull(\"never-null\", String.format(Locale.ENGLISH, GUAVA_COMPATIBLE_PATTERN, 5));",
" checkArgument(false, GUAVA_INCOMPATIBLE_PATTERN.formatted());",
" checkNotNull(\"never-null\", String.format(GUAVA_INCOMPATIBLE_PATTERN));",
"",
" LOG.trace(\"Vacuous format string %%\".formatted());",
" LOG.debug(String.format(\"With format string: %s, %s%%\", 6, 7));",
" LOG.info(String.format(Locale.ROOT, \"With vacuous localized format string %%\"));",
" LOG.warn((Marker) null, \"With marker and format string: %s\".formatted(8));",
" LOG.error(",
" String.format(Locale.US, \"With throwable and format string: %s, %s\", 9, 10),",
" new RuntimeException());",
" LOG.trace(",
" (Marker) null,",
" \"With marker, throwable and format string: %s\".formatted(11),",
" new RuntimeException());",
" LOG.debug(GUAVA_COMPATIBLE_PATTERN.formatted(12));",
" LOG.info(String.format(Locale.ENGLISH, GUAVA_COMPATIBLE_PATTERN, 13));",
" LOG.warn(GUAVA_INCOMPATIBLE_PATTERN.formatted());",
" LOG.error(String.format(GUAVA_INCOMPATIBLE_PATTERN));",
" }",
"}")
.addOutputLines(
"A.java",
"import static com.google.common.base.Preconditions.checkArgument;",
"import static com.google.common.base.Preconditions.checkNotNull;",
"import static com.google.common.base.Preconditions.checkState;",
"import static com.google.common.base.Verify.verify;",
"import static com.google.common.base.Verify.verifyNotNull;",
"import static java.util.Objects.requireNonNull;",
"",
"import java.util.Locale;",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"import org.slf4j.Marker;",
"",
"class A {",
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
" private static final String GUAVA_COMPATIBLE_PATTERN = \"with-only-%s-placeholder\";",
" private static final String GUAVA_INCOMPATIBLE_PATTERN = \"with-%%-marker\";",
"",
" void m() {",
" requireNonNull(\"never-null\", () -> String.format(\"Format string: %s\", 0));",
"",
" checkArgument(true, \"Vacuous format string %\");",
" checkNotNull(\"never-null\", \"Format string: %s %s%\", 1, 2);",
" checkState(false, \"Format string with locale: %s\", 3);",
" verify(true, GUAVA_COMPATIBLE_PATTERN, 4);",
" verifyNotNull(\"never-null\", GUAVA_COMPATIBLE_PATTERN, 5);",
" checkArgument(false, \"with-%-marker\");",
" checkNotNull(\"never-null\", \"with-%-marker\");",
"",
" LOG.trace(\"Vacuous format string %\");",
" LOG.debug(\"With format string: {}, {}%\", 6, 7);",
" LOG.info(\"With vacuous localized format string %\");",
" LOG.warn((Marker) null, \"With marker and format string: {}\", 8);",
" LOG.error(\"With throwable and format string: {}, {}\", 9, 10, new RuntimeException());",
" LOG.trace(",
" (Marker) null, \"With marker, throwable and format string: {}\", 11, new RuntimeException());",
" LOG.debug(\"with-only-{}-placeholder\", 12);",
" LOG.info(\"with-only-{}-placeholder\", 13);",
" LOG.warn(\"with-%-marker\");",
" LOG.error(\"with-%-marker\");",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -0,0 +1,175 @@
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 ExplicitArgumentEnumerationTest {
@Test
void identification() {
CompilationTestHelper.newInstance(ExplicitArgumentEnumeration.class, getClass())
.addSourceLines(
"A.java",
"import static org.assertj.core.api.Assertions.assertThat;",
"",
"import com.google.common.collect.ImmutableList;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import org.jooq.impl.DSL;",
"import reactor.core.publisher.Flux;",
"import reactor.test.StepVerifier;",
"",
"class A {",
" // BUG: Diagnostic contains:",
" private final int value = unaryMethod(ImmutableList.of(1, 2));",
"",
" void m() {",
" ImmutableList<String> list = ImmutableList.of();",
" assertThat(ImmutableList.of()).containsAnyElementsOf(list);",
"",
" ImmutableList.<ImmutableList<String>>builder().add(ImmutableList.of());",
"",
" DSL.row(ImmutableList.of(1, 2));",
"",
" // BUG: Diagnostic contains:",
" unaryMethod(ImmutableList.of(1, 2));",
" unaryMethodWithLessVisibleOverload(ImmutableList.of(1, 2));",
" binaryMethod(ImmutableList.of(1, 2), 3);",
"",
" ImmutableList.builder()",
" // BUG: Diagnostic contains:",
" .addAll(ImmutableList.of())",
" // BUG: Diagnostic contains:",
" .addAll(ImmutableList.copyOf(new String[0]))",
" .addAll(ImmutableList.copyOf(ImmutableList.of()))",
" .build();",
"",
" assertThat(ImmutableList.of(1))",
" // BUG: Diagnostic contains:",
" .containsAnyElementsOf(ImmutableList.of(1))",
" // BUG: Diagnostic contains:",
" .isSubsetOf(ImmutableList.of(1));",
"",
" Flux.just(1, 2)",
" .as(StepVerifier::create)",
" // BUG: Diagnostic contains:",
" .expectNextSequence(ImmutableList.of(1, 2))",
" .verifyComplete();",
"",
" CompilationTestHelper.newInstance(BugChecker.class, getClass())",
" // BUG: Diagnostic contains:",
" .setArgs(ImmutableList.of(\"foo\"))",
" .withClasspath();",
" }",
"",
" private int unaryMethod(ImmutableList<Integer> args) {",
" return 0;",
" }",
"",
" private int unaryMethod(Integer... args) {",
" return unaryMethod(ImmutableList.copyOf(args));",
" }",
"",
" void unaryMethodWithLessVisibleOverload(ImmutableList<Integer> args) {}",
"",
" private void unaryMethodWithLessVisibleOverload(Integer... args) {",
" unaryMethodWithLessVisibleOverload(ImmutableList.copyOf(args));",
" }",
"",
" private void binaryMethod(ImmutableList<Integer> args, int extraArg) {}",
"",
" private void binaryMethod(Integer... args) {",
" binaryMethod(ImmutableList.copyOf(args), 0);",
" }",
"}")
.doTest();
}
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(ExplicitArgumentEnumeration.class, getClass())
.addInputLines(
"A.java",
"import static org.assertj.core.api.Assertions.assertThat;",
"",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableMultiset;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import java.util.Arrays;",
"import java.util.List;",
"import java.util.Set;",
"import reactor.core.publisher.Flux;",
"import reactor.test.StepVerifier;",
"",
"class A {",
" void m() {",
" ImmutableList.builder().addAll(ImmutableList.of()).build();",
"",
" assertThat(ImmutableList.of()).containsAnyElementsOf(ImmutableMultiset.of());",
" assertThat(ImmutableList.of()).containsAll(ImmutableSet.of());",
" assertThat(ImmutableList.of()).containsExactlyElementsOf(List.of());",
" assertThat(ImmutableList.of()).containsExactlyInAnyOrderElementsOf(Set.of());",
" assertThat(ImmutableList.of()).containsSequence(Arrays.asList());",
" assertThat(ImmutableList.of()).containsSubsequence(ImmutableList.of(1));",
" assertThat(ImmutableList.of()).doesNotContainAnyElementsOf(ImmutableMultiset.of(2));",
" assertThat(ImmutableList.of()).doesNotContainSequence(ImmutableSet.of(3));",
" assertThat(ImmutableList.of()).doesNotContainSubsequence(List.of(4));",
" assertThat(ImmutableList.of()).hasSameElementsAs(Set.of(5));",
" assertThat(ImmutableList.of()).isSubsetOf(Arrays.asList(6));",
"",
" Flux.empty()",
" .as(StepVerifier::create)",
" .expectNextSequence(ImmutableList.of(1, 2))",
" .verifyComplete();",
"",
" BugCheckerRefactoringTestHelper.newInstance(BugChecker.class, getClass())",
" .setArgs(ImmutableList.of(\"foo\", \"bar\"));",
" CompilationTestHelper.newInstance(BugChecker.class, getClass())",
" .setArgs(ImmutableList.of(\"foo\", \"bar\"));",
" }",
"}")
.addOutputLines(
"A.java",
"import static org.assertj.core.api.Assertions.assertThat;",
"",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableMultiset;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import java.util.Arrays;",
"import java.util.List;",
"import java.util.Set;",
"import reactor.core.publisher.Flux;",
"import reactor.test.StepVerifier;",
"",
"class A {",
" void m() {",
" ImmutableList.builder().add().build();",
"",
" assertThat(ImmutableList.of()).containsAnyOf();",
" assertThat(ImmutableList.of()).contains();",
" assertThat(ImmutableList.of()).containsExactly();",
" assertThat(ImmutableList.of()).containsExactlyInAnyOrder();",
" assertThat(ImmutableList.of()).containsSequence();",
" assertThat(ImmutableList.of()).containsSubsequence(1);",
" assertThat(ImmutableList.of()).doesNotContain(2);",
" assertThat(ImmutableList.of()).doesNotContainSequence(3);",
" assertThat(ImmutableList.of()).doesNotContainSubsequence(4);",
" assertThat(ImmutableList.of()).containsOnly(5);",
" assertThat(ImmutableList.of()).isSubsetOf(6);",
"",
" Flux.empty().as(StepVerifier::create).expectNext(1, 2).verifyComplete();",
"",
" BugCheckerRefactoringTestHelper.newInstance(BugChecker.class, getClass()).setArgs(\"foo\", \"bar\");",
" CompilationTestHelper.newInstance(BugChecker.class, getClass()).setArgs(\"foo\", \"bar\");",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,7 +11,7 @@ final class JUnitMethodDeclarationTest {
CompilationTestHelper.newInstance(JUnitMethodDeclaration.class, getClass())
.addSourceLines(
"A.java",
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
"import static org.junit.jupiter.params.provider.Arguments.*;",
"",
"import org.junit.jupiter.api.AfterAll;",
"import org.junit.jupiter.api.AfterEach;",
@@ -154,8 +154,10 @@ final class JUnitMethodDeclarationTest {
" void overload() {}",
"",
" @Test",
" // BUG: Diagnostic contains: (but note that `arguments` is already statically imported)",
" void testArguments() {}",
" // BUG: Diagnostic contains: (but note that another method named `arguments` is in scope)",
" void testArguments() {",
" arguments();",
" }",
"",
" @Test",
" // BUG: Diagnostic contains: (but note that `public` is not a valid identifier)",

View File

@@ -1,6 +1,5 @@
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;
@@ -294,9 +293,8 @@ final class LexicographicalAnnotationAttributeListingTest {
/* Some violations are not flagged because they are not in- or excluded. */
CompilationTestHelper.newInstance(LexicographicalAnnotationAttributeListing.class, getClass())
.setArgs(
ImmutableList.of(
"-XepOpt:LexicographicalAnnotationAttributeListing:Includes=pkg.A.Foo,pkg.A.Bar",
"-XepOpt:LexicographicalAnnotationAttributeListing:Excludes=pkg.A.Bar#value"))
"-XepOpt:LexicographicalAnnotationAttributeListing:Includes=pkg.A.Foo,pkg.A.Bar",
"-XepOpt:LexicographicalAnnotationAttributeListing:Excludes=pkg.A.Bar#value")
.addSourceLines(
"pkg/A.java",
"package pkg;",

View File

@@ -55,6 +55,10 @@ final class NonStaticImportTest {
"// BUG: Diagnostic contains:",
"import static java.time.Instant.MIN;",
"// BUG: Diagnostic contains:",
"import static java.time.InstantSource.system;",
"// BUG: Diagnostic contains:",
"import static java.time.LocalDate.EPOCH;",
"// BUG: Diagnostic contains:",
"import static java.time.ZoneOffset.SHORT_IDS;",
"import static java.time.ZoneOffset.UTC;",
"// BUG: Diagnostic contains:",
@@ -71,6 +75,7 @@ final class NonStaticImportTest {
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import java.time.Instant;",
"import java.time.LocalDate;",
"import java.time.ZoneOffset;",
"import java.util.Locale;",
"import java.util.Map;",
@@ -82,8 +87,10 @@ final class NonStaticImportTest {
" void m() {",
" nullToEmpty(null);",
" copyOf(ImmutableList.of());",
" LocalDate epoch = EPOCH;",
" int max = MAX_VALUE;",
" int min = MIN_VALUE;",
" system();",
" systemUTC();",
" Instant minInstant = MIN;",
" Map<String, String> shortIds = SHORT_IDS;",

View File

@@ -1,6 +1,5 @@
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;
@@ -408,8 +407,7 @@ final class RedundantStringConversionTest {
void identificationOfCustomConversionMethod() {
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.setArgs(
ImmutableList.of(
"-XepOpt:RedundantStringConversion:ExtraConversionMethods=java.lang.Enum#name(),A#name(),A.B#toString(int)"))
"-XepOpt:RedundantStringConversion:ExtraConversionMethods=java.lang.Enum#name(),A#name(),A.B#toString(int)")
.addSourceLines(
"A.java",
"import java.math.RoundingMode;",

View File

@@ -21,6 +21,7 @@ final class TimeZoneUsageTest {
"import java.time.OffsetTime;",
"import java.time.ZoneId;",
"import java.time.ZonedDateTime;",
"import reactor.core.publisher.Mono;",
"",
"class A {",
" void m() {",
@@ -31,68 +32,122 @@ final class TimeZoneUsageTest {
" Clock.tick(clock, Duration.ZERO);",
"",
" // BUG: Diagnostic contains:",
" Clock.systemUTC();",
" clock.getZone();",
" // BUG: Diagnostic contains:",
" Clock.systemDefaultZone();",
" Mono.fromSupplier(clock::getZone);",
" // BUG: Diagnostic contains:",
" clock.withZone(UTC);",
" // BUG: Diagnostic contains:",
" Mono.<ZoneId>empty().map(clock::withZone);",
" // BUG: Diagnostic contains:",
" Clock.system(UTC);",
" // BUG: Diagnostic contains:",
" Mono.<ZoneId>empty().map(Clock::system);",
" // BUG: Diagnostic contains:",
" Clock.systemDefaultZone();",
" // BUG: Diagnostic contains:",
" Mono.fromSupplier(Clock::systemDefaultZone);",
" // BUG: Diagnostic contains:",
" Clock.systemUTC();",
" // BUG: Diagnostic contains:",
" Mono.fromSupplier(Clock::systemUTC);",
" // BUG: Diagnostic contains:",
" Clock.tickMillis(UTC);",
" // BUG: Diagnostic contains:",
" Mono.<ZoneId>empty().map(Clock::tickMillis);",
" // BUG: Diagnostic contains:",
" Clock.tickMinutes(UTC);",
" // BUG: Diagnostic contains:",
" Mono.<ZoneId>empty().map(Clock::tickMinutes);",
" // BUG: Diagnostic contains:",
" Clock.tickSeconds(UTC);",
" // BUG: Diagnostic contains:",
" clock.getZone();",
" // BUG: Diagnostic contains:",
" clock.withZone(UTC);",
" Mono.<ZoneId>empty().map(Clock::tickSeconds);",
"",
" Instant.now(clock);",
" Mono.<Clock>empty().map(Instant::now);",
" // BUG: Diagnostic contains:",
" Instant.now();",
" // This is equivalent to `clock.instant()`, which is fine.",
" Instant.now(clock);",
" // BUG: Diagnostic contains:",
" Mono.fromSupplier(Instant::now);",
"",
" // BUG: Diagnostic contains:",
" LocalDate.now();",
" // BUG: Diagnostic contains:",
" Mono.fromSupplier(LocalDate::now);",
" // BUG: Diagnostic contains:",
" LocalDate.now(clock);",
" // BUG: Diagnostic contains:",
" Mono.<Clock>empty().map(LocalDate::now);",
" // BUG: Diagnostic contains:",
" LocalDate.now(UTC);",
" // BUG: Diagnostic contains:",
" Mono.<ZoneId>empty().map(LocalDate::now);",
"",
" // BUG: Diagnostic contains:",
" LocalDateTime.now();",
" // BUG: Diagnostic contains:",
" Mono.fromSupplier(LocalDateTime::now);",
" // BUG: Diagnostic contains:",
" LocalDateTime.now(clock);",
" // BUG: Diagnostic contains:",
" Mono.<Clock>empty().map(LocalDateTime::now);",
" // BUG: Diagnostic contains:",
" LocalDateTime.now(UTC);",
" // BUG: Diagnostic contains:",
" Mono.<ZoneId>empty().map(LocalDateTime::now);",
"",
" // BUG: Diagnostic contains:",
" LocalTime.now();",
" // BUG: Diagnostic contains:",
" Mono.fromSupplier(LocalTime::now);",
" // BUG: Diagnostic contains:",
" LocalTime.now(clock);",
" // BUG: Diagnostic contains:",
" Mono.<Clock>empty().map(LocalTime::now);",
" // BUG: Diagnostic contains:",
" LocalTime.now(UTC);",
" // BUG: Diagnostic contains:",
" Mono.<ZoneId>empty().map(LocalTime::now);",
"",
" // BUG: Diagnostic contains:",
" OffsetDateTime.now();",
" // BUG: Diagnostic contains:",
" Mono.fromSupplier(OffsetDateTime::now);",
" // BUG: Diagnostic contains:",
" OffsetDateTime.now(clock);",
" // BUG: Diagnostic contains:",
" Mono.<Clock>empty().map(OffsetDateTime::now);",
" // BUG: Diagnostic contains:",
" OffsetDateTime.now(UTC);",
" // BUG: Diagnostic contains:",
" Mono.<ZoneId>empty().map(OffsetDateTime::now);",
"",
" // BUG: Diagnostic contains:",
" OffsetTime.now();",
" // BUG: Diagnostic contains:",
" Mono.fromSupplier(OffsetTime::now);",
" // BUG: Diagnostic contains:",
" OffsetTime.now(clock);",
" // BUG: Diagnostic contains:",
" Mono.<Clock>empty().map(OffsetTime::now);",
" // BUG: Diagnostic contains:",
" OffsetTime.now(UTC);",
" // BUG: Diagnostic contains:",
" Mono.<ZoneId>empty().map(OffsetTime::now);",
"",
" // BUG: Diagnostic contains:",
" ZonedDateTime.now();",
" // BUG: Diagnostic contains:",
" Mono.fromSupplier(ZonedDateTime::now);",
" // BUG: Diagnostic contains:",
" ZonedDateTime.now(clock);",
" // BUG: Diagnostic contains:",
" Mono.<Clock>empty().map(ZonedDateTime::now);",
" // BUG: Diagnostic contains:",
" ZonedDateTime.now(UTC);",
" // BUG: Diagnostic contains:",
" Mono.<ZoneId>empty().map(ZonedDateTime::now);",
" }",
"",
" abstract class ForwardingClock extends Clock {",

View File

@@ -23,6 +23,8 @@ final class RefasterRulesTest {
AssertJEnumerableRules.class,
AssertJFloatRules.class,
AssertJIntegerRules.class,
AssertJIterableRules.class,
AssertJIteratorRules.class,
AssertJLongRules.class,
AssertJMapRules.class,
AssertJNumberRules.class,
@@ -36,13 +38,13 @@ final class RefasterRulesTest {
AssortedRules.class,
BigDecimalRules.class,
BugCheckerRules.class,
CharSequenceRules.class,
ClassRules.class,
CollectionRules.class,
ComparatorRules.class,
DoubleStreamRules.class,
EqualityRules.class,
FileRules.class,
InputStreamRules.class,
ImmutableEnumSetRules.class,
ImmutableListRules.class,
ImmutableListMultimapRules.class,
@@ -53,6 +55,8 @@ final class RefasterRulesTest {
ImmutableSortedMapRules.class,
ImmutableSortedMultisetRules.class,
ImmutableSortedSetRules.class,
ImmutableTableRules.class,
InputStreamRules.class,
IntStreamRules.class,
JUnitRules.class,
JUnitToAssertJRules.class,

View File

@@ -8,13 +8,16 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class AssertJCharSequenceRulesTest implements RefasterRuleCollectionTestCase {
void testAssertThatCharSequenceIsEmpty() {
assertThat("foo".length()).isEqualTo(0L);
assertThat("foo".length()).isNotPositive();
assertThat("foo".isEmpty()).isTrue();
assertThat("bar".length()).isEqualTo(0L);
assertThat("baz".length()).isNotPositive();
}
ImmutableSet<AbstractAssert<?, ?>> testAssertThatCharSequenceIsNotEmpty() {
return ImmutableSet.of(
assertThat("foo".length()).isNotEqualTo(0), assertThat("bar".length()).isPositive());
assertThat("foo".isEmpty()).isFalse(),
assertThat("bar".length()).isNotEqualTo(0),
assertThat("baz".length()).isPositive());
}
AbstractAssert<?, ?> testAssertThatCharSequenceHasSize() {

View File

@@ -9,11 +9,15 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class AssertJCharSequenceRulesTest implements RefasterRuleCollectionTestCase {
void testAssertThatCharSequenceIsEmpty() {
assertThat("foo").isEmpty();
assertThat("foo").isEmpty();
assertThat("bar").isEmpty();
assertThat("baz").isEmpty();
}
ImmutableSet<AbstractAssert<?, ?>> testAssertThatCharSequenceIsNotEmpty() {
return ImmutableSet.of(assertThat("foo").isNotEmpty(), assertThat("bar").isNotEmpty());
return ImmutableSet.of(
assertThat("foo").isNotEmpty(),
assertThat("bar").isNotEmpty(),
assertThat("baz").isNotEmpty());
}
AbstractAssert<?, ?> testAssertThatCharSequenceHasSize() {

View File

@@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.EnumerableAssert;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
@@ -14,14 +15,56 @@ final class AssertJEnumerableRulesTest implements RefasterRuleCollectionTestCase
}
void testEnumerableAssertIsEmpty() {
assertThat(ImmutableSet.of()).hasSize(0);
assertThat(ImmutableSet.of()).hasSizeLessThanOrEqualTo(0);
assertThat(ImmutableSet.of()).hasSizeLessThan(1);
assertThat(ImmutableSet.of(1)).hasSize(0);
assertThat(ImmutableSet.of(2)).hasSizeLessThanOrEqualTo(0);
assertThat(ImmutableSet.of(3)).hasSizeLessThan(1);
assertThat(ImmutableSet.of(4)).size().isNotPositive();
}
ImmutableSet<EnumerableAssert<?, Character>> testEnumerableAssertIsNotEmpty() {
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertIsNotEmpty() {
return ImmutableSet.of(
assertThat("foo").hasSizeGreaterThan(0), assertThat("bar").hasSizeGreaterThanOrEqualTo(1));
assertThat(ImmutableSet.of(1)).hasSizeGreaterThan(0),
assertThat(ImmutableSet.of(2)).hasSizeGreaterThanOrEqualTo(1),
assertThat(ImmutableSet.of(3)).size().isNotEqualTo(0).returnToIterable(),
assertThat(ImmutableSet.of(4)).size().isPositive().returnToIterable(),
assertThat(ImmutableSet.of(5)).size().isNotEqualTo(0),
assertThat(ImmutableSet.of(6)).size().isPositive());
}
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertHasSize() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).size().isEqualTo(2).returnToIterable(),
assertThat(ImmutableSet.of(3)).size().isEqualTo(4));
}
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertHasSizeLessThan() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).size().isLessThan(2).returnToIterable(),
assertThat(ImmutableSet.of(3)).size().isLessThan(4));
}
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertHasSizeLessThanOrEqualTo() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).size().isLessThanOrEqualTo(2).returnToIterable(),
assertThat(ImmutableSet.of(3)).size().isLessThanOrEqualTo(4));
}
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertHasSizeGreaterThan() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).size().isGreaterThan(2).returnToIterable(),
assertThat(ImmutableSet.of(3)).size().isGreaterThan(4));
}
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertHasSizeGreaterThanOrEqualTo() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).size().isGreaterThanOrEqualTo(2).returnToIterable(),
assertThat(ImmutableSet.of(3)).size().isGreaterThanOrEqualTo(4));
}
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertHasSizeBetween() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).size().isBetween(2, 3).returnToIterable(),
assertThat(ImmutableSet.of(4)).size().isBetween(5, 6));
}
ImmutableSet<EnumerableAssert<?, Integer>> testEnumerableAssertHasSameSizeAs() {

View File

@@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.EnumerableAssert;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
@@ -14,13 +15,55 @@ final class AssertJEnumerableRulesTest implements RefasterRuleCollectionTestCase
}
void testEnumerableAssertIsEmpty() {
assertThat(ImmutableSet.of()).isEmpty();
assertThat(ImmutableSet.of()).isEmpty();
assertThat(ImmutableSet.of()).isEmpty();
assertThat(ImmutableSet.of(1)).isEmpty();
assertThat(ImmutableSet.of(2)).isEmpty();
assertThat(ImmutableSet.of(3)).isEmpty();
assertThat(ImmutableSet.of(4)).isEmpty();
}
ImmutableSet<EnumerableAssert<?, Character>> testEnumerableAssertIsNotEmpty() {
return ImmutableSet.of(assertThat("foo").isNotEmpty(), assertThat("bar").isNotEmpty());
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertIsNotEmpty() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).isNotEmpty(),
assertThat(ImmutableSet.of(2)).isNotEmpty(),
assertThat(ImmutableSet.of(3)).isNotEmpty(),
assertThat(ImmutableSet.of(4)).isNotEmpty(),
assertThat(ImmutableSet.of(5)).isNotEmpty(),
assertThat(ImmutableSet.of(6)).isNotEmpty());
}
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertHasSize() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).hasSize(2), assertThat(ImmutableSet.of(3)).hasSize(4));
}
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertHasSizeLessThan() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).hasSizeLessThan(2),
assertThat(ImmutableSet.of(3)).hasSizeLessThan(4));
}
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertHasSizeLessThanOrEqualTo() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).hasSizeLessThanOrEqualTo(2),
assertThat(ImmutableSet.of(3)).hasSizeLessThanOrEqualTo(4));
}
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertHasSizeGreaterThan() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).hasSizeGreaterThan(2),
assertThat(ImmutableSet.of(3)).hasSizeGreaterThan(4));
}
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertHasSizeGreaterThanOrEqualTo() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).hasSizeGreaterThanOrEqualTo(2),
assertThat(ImmutableSet.of(3)).hasSizeGreaterThanOrEqualTo(4));
}
ImmutableSet<AbstractAssert<?, ?>> testEnumerableAssertHasSizeBetween() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).hasSizeBetween(2, 3),
assertThat(ImmutableSet.of(4)).hasSizeBetween(5, 6));
}
ImmutableSet<EnumerableAssert<?, Integer>> testEnumerableAssertHasSameSizeAs() {

View File

@@ -0,0 +1,35 @@
package tech.picnic.errorprone.refasterrules;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AbstractIntegerAssert;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class AssertJIterableRulesTest implements RefasterRuleCollectionTestCase {
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(Iterables.class);
}
void testAssertThatIterableIsEmpty() {
assertThat(ImmutableSet.of(1).iterator()).isExhausted();
assertThat(ImmutableSet.of(2).isEmpty()).isTrue();
}
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIterableIsNotEmpty() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1).iterator()).hasNext(),
assertThat(ImmutableSet.of(2).isEmpty()).isFalse());
}
ImmutableSet<AbstractIntegerAssert<?>> testAssertThatIterableSize() {
return ImmutableSet.of(
assertThat(Iterables.size(ImmutableSet.of(1))), assertThat(ImmutableSet.of(2).size()));
}
AbstractAssert<?, ?> testAssertThatIterableHasOneElementEqualTo() {
return assertThat(Iterables.getOnlyElement(ImmutableSet.of(new Object()))).isEqualTo("foo");
}
}

View File

@@ -0,0 +1,34 @@
package tech.picnic.errorprone.refasterrules;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AbstractIntegerAssert;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class AssertJIterableRulesTest implements RefasterRuleCollectionTestCase {
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(Iterables.class);
}
void testAssertThatIterableIsEmpty() {
assertThat(ImmutableSet.of(1)).isEmpty();
assertThat(ImmutableSet.of(2)).isEmpty();
}
ImmutableSet<AbstractAssert<?, ?>> testAssertThatIterableIsNotEmpty() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).isNotEmpty(), assertThat(ImmutableSet.of(2)).isNotEmpty());
}
ImmutableSet<AbstractIntegerAssert<?>> testAssertThatIterableSize() {
return ImmutableSet.of(
assertThat(ImmutableSet.of(1)).size(), assertThat(ImmutableSet.of(2)).size());
}
AbstractAssert<?, ?> testAssertThatIterableHasOneElementEqualTo() {
return assertThat(ImmutableSet.of(new Object())).containsExactly("foo");
}
}

View File

@@ -0,0 +1,17 @@
package tech.picnic.errorprone.refasterrules;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableSet;
import org.assertj.core.api.AbstractAssert;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class AssertJIteratorRulesTest implements RefasterRuleCollectionTestCase {
AbstractAssert<?, ?> testAssertThatHasNext() {
return assertThat(ImmutableSet.of().iterator().hasNext()).isTrue();
}
AbstractAssert<?, ?> testAssertThatIsExhausted() {
return assertThat(ImmutableSet.of().iterator().hasNext()).isFalse();
}
}

View File

@@ -0,0 +1,17 @@
package tech.picnic.errorprone.refasterrules;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableSet;
import org.assertj.core.api.AbstractAssert;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class AssertJIteratorRulesTest implements RefasterRuleCollectionTestCase {
AbstractAssert<?, ?> testAssertThatHasNext() {
return assertThat(ImmutableSet.of().iterator()).hasNext();
}
AbstractAssert<?, ?> testAssertThatIsExhausted() {
return assertThat(ImmutableSet.of().iterator()).isExhausted();
}
}

View File

@@ -21,16 +21,16 @@ final class AssertJStringRulesTest implements RefasterRuleCollectionTestCase {
assertThat("foo").isEqualTo("");
}
void testAssertThatStringIsEmpty() {
assertThat("foo".isEmpty()).isTrue();
}
AbstractStringAssert<?> testAbstractStringAssertStringIsNotEmpty() {
return assertThat("foo").isNotEqualTo("");
}
AbstractAssert<?, ?> testAssertThatStringIsNotEmpty() {
return assertThat("foo".isEmpty()).isFalse();
AbstractAssert<?, ?> testAssertThatStringContains() {
return assertThat("foo".contains("bar")).isTrue();
}
AbstractAssert<?, ?> testAssertThatStringDoesNotContain() {
return assertThat("foo".contains("bar")).isFalse();
}
AbstractAssert<?, ?> testAssertThatMatches() {

View File

@@ -22,16 +22,16 @@ final class AssertJStringRulesTest implements RefasterRuleCollectionTestCase {
assertThat("foo").isEmpty();
}
void testAssertThatStringIsEmpty() {
assertThat("foo").isEmpty();
}
AbstractStringAssert<?> testAbstractStringAssertStringIsNotEmpty() {
return assertThat("foo").isNotEmpty();
}
AbstractAssert<?, ?> testAssertThatStringIsNotEmpty() {
return assertThat("foo").isNotEmpty();
AbstractAssert<?, ?> testAssertThatStringContains() {
return assertThat("foo").contains("bar");
}
AbstractAssert<?, ?> testAssertThatStringDoesNotContain() {
return assertThat("foo").doesNotContain("bar");
}
AbstractAssert<?, ?> testAssertThatMatches() {

View File

@@ -6,6 +6,8 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.InstanceOfAssertFactories.throwable;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
import com.google.common.collect.ImmutableSet;
import org.assertj.core.api.AbstractObjectAssert;
@@ -20,7 +22,13 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
assertThatIOException(),
assertThatIllegalArgumentException(),
assertThatIllegalStateException(),
assertThatNullPointerException());
assertThatNullPointerException(),
type(Throwable.class));
}
void testAssertThatThrownByIsInstanceOf() {
assertThatThrownBy(() -> {}).asInstanceOf(throwable(IllegalArgumentException.class));
assertThatThrownBy(() -> {}).asInstanceOf(type(IllegalArgumentException.class));
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalArgumentException() {
@@ -31,6 +39,13 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
return assertThatIllegalArgumentException().isThrownBy(() -> {}).withMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalArgumentExceptionRootCauseHasMessage() {
return assertThatIllegalArgumentException()
.isThrownBy(() -> {})
.havingRootCause()
.withMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalArgumentExceptionHasMessageParameters() {
return assertThatIllegalArgumentException().isThrownBy(() -> {}).withMessage("foo %s", "bar");
}
@@ -59,6 +74,13 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
return assertThatIllegalStateException().isThrownBy(() -> {}).withMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalStateExceptionRootCauseHasMessage() {
return assertThatIllegalStateException()
.isThrownBy(() -> {})
.havingRootCause()
.withMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalStateExceptionHasMessageParameters() {
return assertThatIllegalStateException().isThrownBy(() -> {}).withMessage("foo %s", "bar");
}
@@ -83,6 +105,13 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
return assertThatNullPointerException().isThrownBy(() -> {}).withMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByNullPointerExceptionRootCauseHasMessage() {
return assertThatNullPointerException()
.isThrownBy(() -> {})
.havingRootCause()
.withMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByNullPointerExceptionHasMessageParameters() {
return assertThatNullPointerException().isThrownBy(() -> {}).withMessage("foo %s", "bar");
}
@@ -107,6 +136,10 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
return assertThatIOException().isThrownBy(() -> {}).withMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIOExceptionRootCauseHasMessage() {
return assertThatIOException().isThrownBy(() -> {}).havingRootCause().withMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIOExceptionHasMessageParameters() {
return assertThatIOException().isThrownBy(() -> {}).withMessage("foo %s", "bar");
}
@@ -123,7 +156,7 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
return assertThatIOException().isThrownBy(() -> {}).withMessageNotContaining("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownBy() {
AbstractObjectAssert<?, ?> testAssertThatThrownByAsInstanceOfThrowable() {
return assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> {});
}
@@ -133,6 +166,13 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
.withMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByRootCauseHasMessage() {
return assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> {})
.havingRootCause()
.withMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByHasMessageParameters() {
return assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> {})

View File

@@ -6,6 +6,8 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.InstanceOfAssertFactories.throwable;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -21,7 +23,13 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
assertThatIOException(),
assertThatIllegalArgumentException(),
assertThatIllegalStateException(),
assertThatNullPointerException());
assertThatNullPointerException(),
type(Throwable.class));
}
void testAssertThatThrownByIsInstanceOf() {
assertThatThrownBy(() -> {}).isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> {}).isInstanceOf(IllegalArgumentException.class);
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalArgumentException() {
@@ -34,6 +42,13 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
.hasMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalArgumentExceptionRootCauseHasMessage() {
return assertThatThrownBy(() -> {})
.isInstanceOf(IllegalArgumentException.class)
.rootCause()
.hasMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalArgumentExceptionHasMessageParameters() {
return assertThatThrownBy(() -> {})
.isInstanceOf(IllegalArgumentException.class)
@@ -68,6 +83,13 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
return assertThatThrownBy(() -> {}).isInstanceOf(IllegalStateException.class).hasMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalStateExceptionRootCauseHasMessage() {
return assertThatThrownBy(() -> {})
.isInstanceOf(IllegalStateException.class)
.rootCause()
.hasMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIllegalStateExceptionHasMessageParameters() {
return assertThatThrownBy(() -> {})
.isInstanceOf(IllegalStateException.class)
@@ -100,6 +122,13 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
return assertThatThrownBy(() -> {}).isInstanceOf(NullPointerException.class).hasMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByNullPointerExceptionRootCauseHasMessage() {
return assertThatThrownBy(() -> {})
.isInstanceOf(NullPointerException.class)
.rootCause()
.hasMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByNullPointerExceptionHasMessageParameters() {
return assertThatThrownBy(() -> {})
.isInstanceOf(NullPointerException.class)
@@ -132,6 +161,13 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
return assertThatThrownBy(() -> {}).isInstanceOf(IOException.class).hasMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIOExceptionRootCauseHasMessage() {
return assertThatThrownBy(() -> {})
.isInstanceOf(IOException.class)
.rootCause()
.hasMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByIOExceptionHasMessageParameters() {
return assertThatThrownBy(() -> {}).isInstanceOf(IOException.class).hasMessage("foo %s", "bar");
}
@@ -152,8 +188,8 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
.hasMessageNotContaining("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownBy() {
return assertThatThrownBy(() -> {}).isInstanceOf(IllegalArgumentException.class);
AbstractObjectAssert<?, ?> testAssertThatThrownByAsInstanceOfThrowable() {
return assertThatThrownBy(() -> {}).asInstanceOf(throwable(IllegalArgumentException.class));
}
AbstractObjectAssert<?, ?> testAssertThatThrownByHasMessage() {
@@ -162,6 +198,13 @@ final class AssertJThrowingCallableRulesTest implements RefasterRuleCollectionTe
.hasMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByRootCauseHasMessage() {
return assertThatThrownBy(() -> {})
.isInstanceOf(IllegalArgumentException.class)
.rootCause()
.hasMessage("foo");
}
AbstractObjectAssert<?, ?> testAssertThatThrownByHasMessageParameters() {
return assertThatThrownBy(() -> {})
.isInstanceOf(IllegalArgumentException.class)

View File

@@ -0,0 +1,16 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.collect.ImmutableSet;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class CharSequenceRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<Boolean> testCharSequenceIsEmpty() {
return ImmutableSet.of(
new StringBuilder("foo").length() == 0,
new StringBuilder("bar").length() <= 0,
new StringBuilder("baz").length() < 1,
new StringBuilder("qux").length() != 0,
new StringBuilder("quux").length() > 0,
new StringBuilder("corge").length() >= 1);
}
}

View File

@@ -0,0 +1,16 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.collect.ImmutableSet;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class CharSequenceRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<Boolean> testCharSequenceIsEmpty() {
return ImmutableSet.of(
new StringBuilder("foo").isEmpty(),
new StringBuilder("bar").isEmpty(),
new StringBuilder("baz").isEmpty(),
!new StringBuilder("qux").isEmpty(),
!new StringBuilder("quux").isEmpty(),
!new StringBuilder("corge").isEmpty());
}
}

View File

@@ -62,7 +62,7 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
return Iterables.removeAll(new ArrayList<>(), ImmutableSet.of("foo"));
}
void testSetRemoveAllCollection() {
void testCollectionRemoveAllFromCollectionBlock() {
ImmutableSet.of("foo").forEach(new HashSet<>()::remove);
for (Number element : ImmutableList.of(1)) {
new HashSet<Number>().remove(element);

View File

@@ -58,7 +58,7 @@ final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
return new ArrayList<>().removeAll(ImmutableSet.of("foo"));
}
void testSetRemoveAllCollection() {
void testCollectionRemoveAllFromCollectionBlock() {
new HashSet<>().removeAll(ImmutableSet.of("foo"));
new HashSet<Number>().removeAll(ImmutableList.of(1));
new HashSet<Number>().removeAll(ImmutableSet.of(2));

View File

@@ -39,4 +39,16 @@ final class FileRulesTest implements RefasterRuleCollectionTestCase {
File testFilesCreateTempFileInCustomDirectoryToFile() throws IOException {
return File.createTempFile("foo", "bar", new File("baz"));
}
ImmutableSet<Boolean> testPathToFileMkDirsFilesExists() {
return ImmutableSet.of(
Files.exists(Path.of("foo")) || Path.of("foo").toFile().mkdirs(),
!Files.exists(Path.of("bar")) && !Path.of("bar").toFile().mkdirs());
}
ImmutableSet<Boolean> testFileMkDirsFileExists() {
return ImmutableSet.of(
new File("foo").exists() || new File("foo").mkdirs(),
!new File("bar").exists() && !new File("bar").mkdirs());
}
}

View File

@@ -39,4 +39,16 @@ final class FileRulesTest implements RefasterRuleCollectionTestCase {
File testFilesCreateTempFileInCustomDirectoryToFile() throws IOException {
return Files.createTempFile(new File("baz").toPath(), "foo", "bar").toFile();
}
ImmutableSet<Boolean> testPathToFileMkDirsFilesExists() {
return ImmutableSet.of(
Path.of("foo").toFile().mkdirs() || Files.exists(Path.of("foo")),
!Path.of("bar").toFile().mkdirs() && !Files.exists(Path.of("bar")));
}
ImmutableSet<Boolean> testFileMkDirsFileExists() {
return ImmutableSet.of(
new File("foo").mkdirs() || new File("foo").exists(),
!new File("bar").mkdirs() && !new File("bar").exists());
}
}

View File

@@ -24,9 +24,13 @@ final class ImmutableMapRulesTest implements RefasterRuleCollectionTestCase {
return new ImmutableMap.Builder<>();
}
ImmutableMap<Object, Object> testImmutableMapBuilderBuildOrThrow() {
return ImmutableMap.builder().build();
}
ImmutableSet<ImmutableMap<String, Integer>> testEntryToImmutableMap() {
return ImmutableSet.of(
ImmutableMap.<String, Integer>builder().put(Map.entry("foo", 1)).build(),
ImmutableMap.<String, Integer>builder().put(Map.entry("foo", 1)).buildOrThrow(),
Stream.of(Map.entry("foo", 1))
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)));
}
@@ -51,13 +55,14 @@ final class ImmutableMapRulesTest implements RefasterRuleCollectionTestCase {
ImmutableMap.copyOf(Maps.asMap(ImmutableSet.of(10), Integer::valueOf)));
}
ImmutableSet<ImmutableMap<String, Integer>> testEntryIterableToImmutableMap() {
ImmutableSet<Map<String, Integer>> testEntryIterableToImmutableMap() {
return ImmutableSet.of(
ImmutableMap.copyOf(ImmutableMap.of("foo", 1).entrySet()),
ImmutableMap.<String, Integer>builder().putAll(ImmutableMap.of("foo", 1)).build(),
ImmutableMap.<String, Integer>builder().putAll(ImmutableMap.of("foo", 1)).buildOrThrow(),
Map.copyOf(ImmutableMap.of("foo", 1)),
ImmutableMap.<String, Integer>builder()
.putAll(ImmutableMap.of("foo", 1).entrySet())
.build(),
.buildOrThrow(),
ImmutableMap.of("foo", 1).entrySet().stream()
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)),
Streams.stream(Iterables.cycle(Map.entry("foo", 1)))
@@ -100,32 +105,51 @@ final class ImmutableMapRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<Map<String, String>> testImmutableMapOf() {
return ImmutableSet.of(
ImmutableMap.<String, String>builder().build(),
ImmutableMap.<String, String>builder().buildOrThrow(),
ImmutableMap.ofEntries(),
Collections.<String, String>emptyMap(),
Map.<String, String>of());
}
ImmutableSet<Map<String, String>> testImmutableMapOf1() {
return ImmutableSet.of(
ImmutableMap.<String, String>builder().put("k1", "v1").build(),
ImmutableMap.<String, String>builder().put("k1", "v1").buildOrThrow(),
ImmutableMap.ofEntries(Map.entry("k1", "v1")),
Collections.singletonMap("k1", "v1"),
Map.of("k1", "v1"));
}
Map<String, String> testImmutableMapOf2() {
return Map.of("k1", "v1", "k2", "v2");
ImmutableSet<Map<String, String>> testImmutableMapOf2() {
return ImmutableSet.of(
ImmutableMap.ofEntries(Map.entry("k1", "v1"), Map.entry("k2", "v2")),
Map.of("k1", "v1", "k2", "v2"));
}
Map<String, String> testImmutableMapOf3() {
return Map.of("k1", "v1", "k2", "v2", "k3", "v3");
ImmutableSet<Map<String, String>> testImmutableMapOf3() {
return ImmutableSet.of(
ImmutableMap.ofEntries(Map.entry("k1", "v1"), Map.entry("k2", "v2"), Map.entry("k3", "v3")),
Map.of("k1", "v1", "k2", "v2", "k3", "v3"));
}
Map<String, String> testImmutableMapOf4() {
return Map.of("k1", "v1", "k2", "v2", "k3", "v3", "k4", "v4");
ImmutableSet<Map<String, String>> testImmutableMapOf4() {
return ImmutableSet.of(
ImmutableMap.ofEntries(
Map.entry("k1", "v1"),
Map.entry("k2", "v2"),
Map.entry("k3", "v3"),
Map.entry("k4", "v4")),
Map.of("k1", "v1", "k2", "v2", "k3", "v3", "k4", "v4"));
}
Map<String, String> testImmutableMapOf5() {
return Map.of("k1", "v1", "k2", "v2", "k3", "v3", "k4", "v4", "k5", "v5");
ImmutableSet<Map<String, String>> testImmutableMapOf5() {
return ImmutableSet.of(
ImmutableMap.ofEntries(
Map.entry("k1", "v1"),
Map.entry("k2", "v2"),
Map.entry("k3", "v3"),
Map.entry("k4", "v4"),
Map.entry("k5", "v5")),
Map.of("k1", "v1", "k2", "v2", "k3", "v3", "k4", "v4", "k5", "v5"));
}
ImmutableMap<String, Integer> testImmutableMapCopyOfMapsFilterKeys() {
@@ -139,4 +163,11 @@ final class ImmutableMapRulesTest implements RefasterRuleCollectionTestCase {
.filter(entry -> entry.getValue() > 0)
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
}
ImmutableSet<Map<String, Integer>> testImmutableMapOfEntries() {
return ImmutableSet.of(
Map.ofEntries(),
Map.ofEntries(Map.entry("foo", 1)),
Map.ofEntries(Map.entry("bar", 2), Map.entry("baz", 3)));
}
}

View File

@@ -24,6 +24,10 @@ final class ImmutableMapRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableMap.builder();
}
ImmutableMap<Object, Object> testImmutableMapBuilderBuildOrThrow() {
return ImmutableMap.builder().buildOrThrow();
}
ImmutableSet<ImmutableMap<String, Integer>> testEntryToImmutableMap() {
return ImmutableSet.of(
ImmutableMap.of(Map.entry("foo", 1).getKey(), Map.entry("foo", 1).getValue()),
@@ -46,8 +50,9 @@ final class ImmutableMapRulesTest implements RefasterRuleCollectionTestCase {
Maps.toMap(ImmutableSet.of(10), Integer::valueOf));
}
ImmutableSet<ImmutableMap<String, Integer>> testEntryIterableToImmutableMap() {
ImmutableSet<Map<String, Integer>> testEntryIterableToImmutableMap() {
return ImmutableSet.of(
ImmutableMap.copyOf(ImmutableMap.of("foo", 1)),
ImmutableMap.copyOf(ImmutableMap.of("foo", 1)),
ImmutableMap.copyOf(ImmutableMap.of("foo", 1)),
ImmutableMap.copyOf(ImmutableMap.of("foo", 1).entrySet()),
@@ -83,28 +88,39 @@ final class ImmutableMapRulesTest implements RefasterRuleCollectionTestCase {
}
ImmutableSet<Map<String, String>> testImmutableMapOf() {
return ImmutableSet.of(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of());
return ImmutableSet.of(
ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of());
}
ImmutableSet<Map<String, String>> testImmutableMapOf1() {
return ImmutableSet.of(
ImmutableMap.of("k1", "v1"), ImmutableMap.of("k1", "v1"), ImmutableMap.of("k1", "v1"));
ImmutableMap.of("k1", "v1"),
ImmutableMap.of("k1", "v1"),
ImmutableMap.of("k1", "v1"),
ImmutableMap.of("k1", "v1"));
}
Map<String, String> testImmutableMapOf2() {
return ImmutableMap.of("k1", "v1", "k2", "v2");
ImmutableSet<Map<String, String>> testImmutableMapOf2() {
return ImmutableSet.of(
ImmutableMap.of("k1", "v1", "k2", "v2"), ImmutableMap.of("k1", "v1", "k2", "v2"));
}
Map<String, String> testImmutableMapOf3() {
return ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3");
ImmutableSet<Map<String, String>> testImmutableMapOf3() {
return ImmutableSet.of(
ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3"),
ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3"));
}
Map<String, String> testImmutableMapOf4() {
return ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3", "k4", "v4");
ImmutableSet<Map<String, String>> testImmutableMapOf4() {
return ImmutableSet.of(
ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3", "k4", "v4"),
ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3", "k4", "v4"));
}
Map<String, String> testImmutableMapOf5() {
return ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3", "k4", "v4", "k5", "v5");
ImmutableSet<Map<String, String>> testImmutableMapOf5() {
return ImmutableSet.of(
ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3", "k4", "v4", "k5", "v5"),
ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3", "k4", "v4", "k5", "v5"));
}
ImmutableMap<String, Integer> testImmutableMapCopyOfMapsFilterKeys() {
@@ -114,4 +130,11 @@ final class ImmutableMapRulesTest implements RefasterRuleCollectionTestCase {
ImmutableMap<String, Integer> testImmutableMapCopyOfMapsFilterValues() {
return ImmutableMap.copyOf(Maps.filterValues(ImmutableMap.of("foo", 1), v -> v > 0));
}
ImmutableSet<Map<String, Integer>> testImmutableMapOfEntries() {
return ImmutableSet.of(
ImmutableMap.ofEntries(),
ImmutableMap.ofEntries(Map.entry("foo", 1)),
ImmutableMap.ofEntries(Map.entry("bar", 2), Map.entry("baz", 3)));
}
}

View File

@@ -32,16 +32,16 @@ final class ImmutableSortedMapRulesTest implements RefasterRuleCollectionTestCas
}
ImmutableSortedMap<String, Integer> testEmptyImmutableSortedMap() {
return ImmutableSortedMap.<String, Integer>naturalOrder().build();
return ImmutableSortedMap.<String, Integer>naturalOrder().buildOrThrow();
}
ImmutableSortedMap<String, Integer> testPairToImmutableSortedMap() {
return ImmutableSortedMap.<String, Integer>naturalOrder().put("foo", 1).build();
return ImmutableSortedMap.<String, Integer>naturalOrder().put("foo", 1).buildOrThrow();
}
ImmutableSet<ImmutableSortedMap<String, Integer>> testEntryToImmutableSortedMap() {
return ImmutableSet.of(
ImmutableSortedMap.<String, Integer>naturalOrder().put(Map.entry("foo", 1)).build(),
ImmutableSortedMap.<String, Integer>naturalOrder().put(Map.entry("foo", 1)).buildOrThrow(),
Stream.of(Map.entry("foo", 1))
.collect(toImmutableSortedMap(naturalOrder(), Map.Entry::getKey, Map.Entry::getValue)));
}
@@ -52,10 +52,10 @@ final class ImmutableSortedMapRulesTest implements RefasterRuleCollectionTestCas
ImmutableSortedMap.copyOf(ImmutableSortedMap.of("foo", 1).entrySet()),
ImmutableSortedMap.<String, Integer>naturalOrder()
.putAll(ImmutableSortedMap.of("foo", 1))
.build(),
.buildOrThrow(),
ImmutableSortedMap.<String, Integer>naturalOrder()
.putAll(ImmutableSortedMap.of("foo", 1).entrySet())
.build(),
.buildOrThrow(),
ImmutableSortedMap.of("foo", 1).entrySet().stream()
.collect(toImmutableSortedMap(naturalOrder(), Map.Entry::getKey, Map.Entry::getValue)),
Streams.stream(Iterables.cycle(Map.entry("foo", 1)))

View File

@@ -0,0 +1,48 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableTable.toImmutableTable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Table;
import com.google.common.collect.Tables;
import java.util.stream.Stream;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class ImmutableTableRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(Table.class);
}
ImmutableTable.Builder<String, Integer, String> testImmutableTableBuilder() {
return new ImmutableTable.Builder<>();
}
ImmutableTable<Object, Object, Object> testImmutableTableBuilderBuildOrThrow() {
return ImmutableTable.builder().build();
}
ImmutableSet<ImmutableTable<String, Integer, String>> testCellToImmutableTable() {
return ImmutableSet.of(
ImmutableTable.<String, Integer, String>builder()
.put(Tables.immutableCell("foo", 1, "bar"))
.buildOrThrow(),
Stream.of(Tables.immutableCell("baz", 2, "qux"))
.collect(
toImmutableTable(
Table.Cell::getRowKey, Table.Cell::getColumnKey, Table.Cell::getValue)));
}
ImmutableTable<Integer, String, Integer> testStreamOfCellsToImmutableTable() {
return Stream.of(1, 2, 3)
.map(n -> Tables.immutableCell(n, n.toString(), n * 2))
.collect(
toImmutableTable(
Table.Cell::getRowKey, Table.Cell::getColumnKey, Table.Cell::getValue));
}
ImmutableTable<String, String, String> testImmutableTableOf() {
return ImmutableTable.<String, String, String>builder().buildOrThrow();
}
}

View File

@@ -0,0 +1,45 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableTable.toImmutableTable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Table;
import com.google.common.collect.Tables;
import java.util.stream.Stream;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class ImmutableTableRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(Table.class);
}
ImmutableTable.Builder<String, Integer, String> testImmutableTableBuilder() {
return ImmutableTable.builder();
}
ImmutableTable<Object, Object, Object> testImmutableTableBuilderBuildOrThrow() {
return ImmutableTable.builder().buildOrThrow();
}
ImmutableSet<ImmutableTable<String, Integer, String>> testCellToImmutableTable() {
return ImmutableSet.of(
ImmutableTable.of(
Tables.immutableCell("foo", 1, "bar").getRowKey(),
Tables.immutableCell("foo", 1, "bar").getColumnKey(),
Tables.immutableCell("foo", 1, "bar").getValue()),
ImmutableTable.of(
Tables.immutableCell("baz", 2, "qux").getRowKey(),
Tables.immutableCell("baz", 2, "qux").getColumnKey(),
Tables.immutableCell("baz", 2, "qux").getValue()));
}
ImmutableTable<Integer, String, Integer> testStreamOfCellsToImmutableTable() {
return Stream.of(1, 2, 3).collect(toImmutableTable(n -> n, n -> n.toString(), n -> n * 2));
}
ImmutableTable<String, String, String> testImmutableTableOf() {
return ImmutableTable.of();
}
}

View File

@@ -1,5 +1,7 @@
package tech.picnic.errorprone.refasterrules;
import static org.assertj.core.api.Assertions.offset;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
@@ -24,6 +26,8 @@ final class JUnitToAssertJRulesTest implements RefasterRuleCollectionTestCase {
assertInstanceOf(null, null),
assertThrows(null, null),
assertThrowsExactly(null, null),
offset(0.0),
(Runnable) () -> assertArrayEquals((int[]) null, null),
(Runnable) () -> assertFalse(true),
(Runnable) () -> assertNotNull(null),
(Runnable) () -> assertNotSame(null, null),
@@ -32,6 +36,138 @@ final class JUnitToAssertJRulesTest implements RefasterRuleCollectionTestCase {
(Runnable) () -> assertTrue(true));
}
void testAssertThatBooleanArrayContainsExactly() {
assertArrayEquals(new boolean[] {true}, new boolean[] {false});
}
void testAssertThatBooleanArrayWithFailMessageContainsExactly() {
assertArrayEquals(new boolean[] {true}, new boolean[] {false}, "foo");
}
void testAssertThatBooleanArrayWithFailMessageSupplierContainsExactly() {
assertArrayEquals(new boolean[] {true}, new boolean[] {false}, () -> "foo");
}
void testAssertThatByteArrayContainsExactly() {
assertArrayEquals(new byte[] {1}, new byte[] {2});
}
void testAssertThatByteArrayWithFailMessageContainsExactly() {
assertArrayEquals(new byte[] {1}, new byte[] {2}, "foo");
}
void testAssertThatByteArrayWithFailMessageSupplierContainsExactly() {
assertArrayEquals(new byte[] {1}, new byte[] {2}, () -> "foo");
}
void testAssertThatCharArrayContainsExactly() {
assertArrayEquals(new char[] {'a'}, new char[] {'b'});
}
void testAssertThatCharArrayWithFailMessageContainsExactly() {
assertArrayEquals(new char[] {'a'}, new char[] {'b'}, "foo");
}
void testAssertThatCharArrayWithFailMessageSupplierContainsExactly() {
assertArrayEquals(new char[] {'a'}, new char[] {'b'}, () -> "foo");
}
void testAssertThatShortArrayContainsExactly() {
assertArrayEquals(new short[] {1}, new short[] {2});
}
void testAssertThatShortArrayWithFailMessageContainsExactly() {
assertArrayEquals(new short[] {1}, new short[] {2}, "foo");
}
void testAssertThatShortArrayWithFailMessageSupplierContainsExactly() {
assertArrayEquals(new short[] {1}, new short[] {2}, () -> "foo");
}
void testAssertThatIntArrayContainsExactly() {
assertArrayEquals(new int[] {1}, new int[] {2});
}
void testAssertThatIntArrayWithFailMessageContainsExactly() {
assertArrayEquals(new int[] {1}, new int[] {2}, "foo");
}
void testAssertThatIntArrayWithFailMessageSupplierContainsExactly() {
assertArrayEquals(new int[] {1}, new int[] {2}, () -> "foo");
}
void testAssertThatLongArrayContainsExactly() {
assertArrayEquals(new long[] {1L}, new long[] {2L});
}
void testAssertThatLongArrayWithFailMessageContainsExactly() {
assertArrayEquals(new long[] {1L}, new long[] {2L}, "foo");
}
void testAssertThatLongArrayWithFailMessageSupplierContainsExactly() {
assertArrayEquals(new long[] {1L}, new long[] {2L}, () -> "foo");
}
void testAssertThatFloatArrayContainsExactly() {
assertArrayEquals(new float[] {1.0F}, new float[] {2.0F});
}
void testAssertThatFloatArrayWithFailMessageContainsExactly() {
assertArrayEquals(new float[] {1.0F}, new float[] {2.0F}, "foo");
}
void testAssertThatFloatArrayWithFailMessageSupplierContainsExactly() {
assertArrayEquals(new float[] {1.0F}, new float[] {2.0F}, () -> "foo");
}
void testAssertThatFloatArrayContainsExactlyWithOffset() {
assertArrayEquals(new float[] {1.0F}, new float[] {2.0F}, 0.1f);
}
void testAssertThatFloatArrayWithFailMessageContainsExactlyWithOffset() {
assertArrayEquals(new float[] {1.0F}, new float[] {2.0F}, 0.1f, "foo");
}
void testAssertThatFloatArrayWithFailMessageSupplierContainsExactlyWithOffset() {
assertArrayEquals(new float[] {1.0F}, new float[] {2.0F}, 0.1f, () -> "foo");
}
void testAssertThatDoubleArrayContainsExactly() {
assertArrayEquals(new double[] {1.0}, new double[] {2.0});
}
void testAssertThatDoubleArrayWithFailMessageContainsExactly() {
assertArrayEquals(new double[] {1.0}, new double[] {2.0}, "foo");
}
void testAssertThatDoubleArrayWithFailMessageSupplierContainsExactly() {
assertArrayEquals(new double[] {1.0}, new double[] {2.0}, () -> "foo");
}
void testAssertThatDoubleArrayContainsExactlyWithOffset() {
assertArrayEquals(new double[] {1.0}, new double[] {2.0}, 0.1);
}
void testAssertThatDoubleArrayWithFailMessageContainsExactlyWithOffset() {
assertArrayEquals(new double[] {1.0}, new double[] {2.0}, 0.1, "foo");
}
void testAssertThatDoubleArrayWithFailMessageSupplierContainsExactlyWithOffset() {
assertArrayEquals(new double[] {1.0}, new double[] {2.0}, 0.1, () -> "foo");
}
void testAssertThatObjectArrayContainsExactly() {
assertArrayEquals(new Object[] {"foo"}, new Object[] {"bar"});
}
void testAssertThatObjectArrayWithFailMessageContainsExactly() {
assertArrayEquals(new Object[] {"foo"}, new Object[] {"bar"}, "foo");
}
void testAssertThatObjectArrayWithFailMessageSupplierContainsExactly() {
assertArrayEquals(new Object[] {"foo"}, new Object[] {"bar"}, () -> "foo");
}
Object testFail() {
return Assertions.fail();
}

View File

@@ -3,6 +3,8 @@ package tech.picnic.errorprone.refasterrules;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.offset;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
@@ -27,6 +29,8 @@ final class JUnitToAssertJRulesTest implements RefasterRuleCollectionTestCase {
assertInstanceOf(null, null),
assertThrows(null, null),
assertThrowsExactly(null, null),
offset(0.0),
(Runnable) () -> assertArrayEquals((int[]) null, null),
(Runnable) () -> assertFalse(true),
(Runnable) () -> assertNotNull(null),
(Runnable) () -> assertNotSame(null, null),
@@ -35,6 +39,150 @@ final class JUnitToAssertJRulesTest implements RefasterRuleCollectionTestCase {
(Runnable) () -> assertTrue(true));
}
void testAssertThatBooleanArrayContainsExactly() {
assertThat(new boolean[] {false}).containsExactly(new boolean[] {true});
}
void testAssertThatBooleanArrayWithFailMessageContainsExactly() {
assertThat(new boolean[] {false}).withFailMessage("foo").containsExactly(new boolean[] {true});
}
void testAssertThatBooleanArrayWithFailMessageSupplierContainsExactly() {
assertThat(new boolean[] {false})
.withFailMessage(() -> "foo")
.containsExactly(new boolean[] {true});
}
void testAssertThatByteArrayContainsExactly() {
assertThat(new byte[] {2}).containsExactly(new byte[] {1});
}
void testAssertThatByteArrayWithFailMessageContainsExactly() {
assertThat(new byte[] {2}).withFailMessage("foo").containsExactly(new byte[] {1});
}
void testAssertThatByteArrayWithFailMessageSupplierContainsExactly() {
assertThat(new byte[] {2}).withFailMessage(() -> "foo").containsExactly(new byte[] {1});
}
void testAssertThatCharArrayContainsExactly() {
assertThat(new char[] {'b'}).containsExactly(new char[] {'a'});
}
void testAssertThatCharArrayWithFailMessageContainsExactly() {
assertThat(new char[] {'b'}).withFailMessage("foo").containsExactly(new char[] {'a'});
}
void testAssertThatCharArrayWithFailMessageSupplierContainsExactly() {
assertThat(new char[] {'b'}).withFailMessage(() -> "foo").containsExactly(new char[] {'a'});
}
void testAssertThatShortArrayContainsExactly() {
assertThat(new short[] {2}).containsExactly(new short[] {1});
}
void testAssertThatShortArrayWithFailMessageContainsExactly() {
assertThat(new short[] {2}).withFailMessage("foo").containsExactly(new short[] {1});
}
void testAssertThatShortArrayWithFailMessageSupplierContainsExactly() {
assertThat(new short[] {2}).withFailMessage(() -> "foo").containsExactly(new short[] {1});
}
void testAssertThatIntArrayContainsExactly() {
assertThat(new int[] {2}).containsExactly(new int[] {1});
}
void testAssertThatIntArrayWithFailMessageContainsExactly() {
assertThat(new int[] {2}).withFailMessage("foo").containsExactly(new int[] {1});
}
void testAssertThatIntArrayWithFailMessageSupplierContainsExactly() {
assertThat(new int[] {2}).withFailMessage(() -> "foo").containsExactly(new int[] {1});
}
void testAssertThatLongArrayContainsExactly() {
assertThat(new long[] {2L}).containsExactly(new long[] {1L});
}
void testAssertThatLongArrayWithFailMessageContainsExactly() {
assertThat(new long[] {2L}).withFailMessage("foo").containsExactly(new long[] {1L});
}
void testAssertThatLongArrayWithFailMessageSupplierContainsExactly() {
assertThat(new long[] {2L}).withFailMessage(() -> "foo").containsExactly(new long[] {1L});
}
void testAssertThatFloatArrayContainsExactly() {
assertThat(new float[] {2.0F}).containsExactly(new float[] {1.0F});
}
void testAssertThatFloatArrayWithFailMessageContainsExactly() {
assertThat(new float[] {2.0F}).withFailMessage("foo").containsExactly(new float[] {1.0F});
}
void testAssertThatFloatArrayWithFailMessageSupplierContainsExactly() {
assertThat(new float[] {2.0F}).withFailMessage(() -> "foo").containsExactly(new float[] {1.0F});
}
void testAssertThatFloatArrayContainsExactlyWithOffset() {
assertThat(new float[] {2.0F}).containsExactly(new float[] {1.0F}, offset(0.1f));
}
void testAssertThatFloatArrayWithFailMessageContainsExactlyWithOffset() {
assertThat(new float[] {2.0F})
.withFailMessage("foo")
.containsExactly(new float[] {1.0F}, offset(0.1f));
}
void testAssertThatFloatArrayWithFailMessageSupplierContainsExactlyWithOffset() {
assertThat(new float[] {2.0F})
.withFailMessage(() -> "foo")
.containsExactly(new float[] {1.0F}, offset(0.1f));
}
void testAssertThatDoubleArrayContainsExactly() {
assertThat(new double[] {2.0}).containsExactly(new double[] {1.0});
}
void testAssertThatDoubleArrayWithFailMessageContainsExactly() {
assertThat(new double[] {2.0}).withFailMessage("foo").containsExactly(new double[] {1.0});
}
void testAssertThatDoubleArrayWithFailMessageSupplierContainsExactly() {
assertThat(new double[] {2.0}).withFailMessage(() -> "foo").containsExactly(new double[] {1.0});
}
void testAssertThatDoubleArrayContainsExactlyWithOffset() {
assertThat(new double[] {2.0}).containsExactly(new double[] {1.0}, offset(0.1));
}
void testAssertThatDoubleArrayWithFailMessageContainsExactlyWithOffset() {
assertThat(new double[] {2.0})
.withFailMessage("foo")
.containsExactly(new double[] {1.0}, offset(0.1));
}
void testAssertThatDoubleArrayWithFailMessageSupplierContainsExactlyWithOffset() {
assertThat(new double[] {2.0})
.withFailMessage(() -> "foo")
.containsExactly(new double[] {1.0}, offset(0.1));
}
void testAssertThatObjectArrayContainsExactly() {
assertThat(new Object[] {"bar"}).containsExactly(new Object[] {"foo"});
}
void testAssertThatObjectArrayWithFailMessageContainsExactly() {
assertThat(new Object[] {"bar"}).withFailMessage("foo").containsExactly(new Object[] {"foo"});
}
void testAssertThatObjectArrayWithFailMessageSupplierContainsExactly() {
assertThat(new Object[] {"bar"})
.withFailMessage(() -> "foo")
.containsExactly(new Object[] {"foo"});
}
Object testFail() {
return org.assertj.core.api.Assertions.fail();
}

View File

@@ -1,16 +1,18 @@
package tech.picnic.errorprone.refasterrules;
import static java.util.function.Predicate.not;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSet;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import java.util.function.Predicate;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class NullRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(MoreObjects.class, Optional.class);
return ImmutableSet.of(MoreObjects.class, Optional.class, not(null));
}
ImmutableSet<Boolean> testIsNull() {
@@ -30,11 +32,11 @@ final class NullRulesTest implements RefasterRuleCollectionTestCase {
return Optional.ofNullable("foo").orElseGet(() -> "bar");
}
long testIsNullFunction() {
return Stream.of("foo").filter(s -> s == null).count();
ImmutableSet<Predicate<String>> testIsNullFunction() {
return ImmutableSet.of(s -> s == null, not(Objects::nonNull));
}
long testNonNullFunction() {
return Stream.of("foo").filter(s -> s != null).count();
ImmutableSet<Predicate<String>> testNonNullFunction() {
return ImmutableSet.of(s -> s != null, not(Objects::isNull));
}
}

View File

@@ -2,18 +2,19 @@ package tech.picnic.errorprone.refasterrules;
import static java.util.Objects.requireNonNullElse;
import static java.util.Objects.requireNonNullElseGet;
import static java.util.function.Predicate.not;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSet;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import java.util.function.Predicate;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class NullRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(MoreObjects.class, Optional.class);
return ImmutableSet.of(MoreObjects.class, Optional.class, not(null));
}
ImmutableSet<Boolean> testIsNull() {
@@ -32,11 +33,11 @@ final class NullRulesTest implements RefasterRuleCollectionTestCase {
return requireNonNullElseGet("foo", () -> "bar");
}
long testIsNullFunction() {
return Stream.of("foo").filter(Objects::isNull).count();
ImmutableSet<Predicate<String>> testIsNullFunction() {
return ImmutableSet.of(Objects::isNull, Objects::isNull);
}
long testNonNullFunction() {
return Stream.of("foo").filter(Objects::nonNull).count();
ImmutableSet<Predicate<String>> testNonNullFunction() {
return ImmutableSet.of(Objects::nonNull, Objects::nonNull);
}
}

View File

@@ -9,8 +9,10 @@ import com.google.common.primitives.Floats;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.common.primitives.Shorts;
import com.google.common.primitives.UnsignedBytes;
import com.google.common.primitives.UnsignedInts;
import com.google.common.primitives.UnsignedLongs;
import java.util.Comparator;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
@@ -25,6 +27,7 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
Ints.class,
Longs.class,
Shorts.class,
UnsignedBytes.class,
UnsignedInts.class,
UnsignedLongs.class);
}
@@ -222,4 +225,16 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
String testLongToUnsignedStringWithRadix() {
return UnsignedLongs.toString(1, 2);
}
Comparator<byte[]> testArraysCompareUnsignedBytes() {
return UnsignedBytes.lexicographicalComparator();
}
Comparator<int[]> testArraysCompareUnsignedInts() {
return UnsignedInts.lexicographicalComparator();
}
Comparator<long[]> testArraysCompareUnsignedLongs() {
return UnsignedLongs.lexicographicalComparator();
}
}

View File

@@ -9,8 +9,11 @@ import com.google.common.primitives.Floats;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.common.primitives.Shorts;
import com.google.common.primitives.UnsignedBytes;
import com.google.common.primitives.UnsignedInts;
import com.google.common.primitives.UnsignedLongs;
import java.util.Arrays;
import java.util.Comparator;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
@@ -25,6 +28,7 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
Ints.class,
Longs.class,
Shorts.class,
UnsignedBytes.class,
UnsignedInts.class,
UnsignedLongs.class);
}
@@ -222,4 +226,16 @@ final class PrimitiveRulesTest implements RefasterRuleCollectionTestCase {
String testLongToUnsignedStringWithRadix() {
return Long.toUnsignedString(1, 2);
}
Comparator<byte[]> testArraysCompareUnsignedBytes() {
return Arrays::compareUnsigned;
}
Comparator<int[]> testArraysCompareUnsignedInts() {
return Arrays::compareUnsigned;
}
Comparator<long[]> testArraysCompareUnsignedLongs() {
return Arrays::compareUnsigned;
}
}

View File

@@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
@@ -44,6 +45,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
List.class,
ImmutableCollection.class,
ImmutableMap.class,
assertThat(false),
assertThat(0),
maxBy(null),
minBy(null),
@@ -186,7 +188,12 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
}
ImmutableSet<Flux<Integer>> testFluxJust() {
return ImmutableSet.of(Flux.range(0, 1), Mono.just(2).repeat().take(1));
return ImmutableSet.of(
Flux.range(0, 1),
Mono.just(2).flux(),
Mono.just(3).repeat().take(1),
Flux.fromIterable(ImmutableList.of(4)),
Flux.fromIterable(ImmutableSet.of(5)));
}
ImmutableSet<Mono<?>> testMonoIdentity() {
@@ -324,6 +331,14 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
Flux.just(1).switchMap(n -> Mono.fromSupplier(() -> n * 2)));
}
Flux<String> testFluxMapNotNullTransformationOrElse() {
return Flux.just(1).map(x -> Optional.of(x.toString())).mapNotNull(x -> x.orElse(null));
}
Flux<Integer> testFluxMapNotNullOrElse() {
return Flux.just(Optional.of(1)).filter(Optional::isPresent).map(Optional::orElseThrow);
}
ImmutableSet<Flux<String>> testMonoFlux() {
return ImmutableSet.of(
Mono.just("foo").flatMapMany(Mono::just),
@@ -332,7 +347,11 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
}
ImmutableSet<Mono<Void>> testMonoThen() {
return ImmutableSet.of(Mono.just("foo").ignoreElement().then(), Mono.just("bar").flux().then());
return ImmutableSet.of(
Mono.just("foo").ignoreElement().then(),
Mono.just("bar").flux().then(),
Mono.when(Mono.just("baz")),
Mono.whenDelayError(Mono.just("qux")));
}
ImmutableSet<Mono<Void>> testFluxThen() {
@@ -378,8 +397,10 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
ImmutableSet<Mono<Optional<String>>> testMonoSingleOptional() {
return ImmutableSet.of(
Mono.just("foo").flux().collect(toOptional()),
Mono.just("bar").map(Optional::of).defaultIfEmpty(Optional.empty()),
Mono.just("baz").transform(Mono::singleOptional));
Mono.just("bar").map(Optional::of),
Mono.just("baz").singleOptional().defaultIfEmpty(Optional.empty()),
Mono.just("quux").singleOptional().switchIfEmpty(Mono.just(Optional.empty())),
Mono.just("quuz").transform(Mono::singleOptional));
}
Mono<Number> testMonoCast() {
@@ -589,6 +610,35 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableSet.of(PublisherProbe.of(Mono.empty()), PublisherProbe.of(Flux.empty()));
}
void testPublisherProbeAssertWasSubscribed() {
assertThat(PublisherProbe.of(Mono.just(1)).wasSubscribed()).isTrue();
assertThat(PublisherProbe.of(Mono.just(2)).subscribeCount()).isNotNegative();
assertThat(PublisherProbe.of(Mono.just(3)).subscribeCount()).isNotEqualTo(0);
assertThat(PublisherProbe.of(Mono.just(4)).subscribeCount()).isPositive();
}
void testPublisherProbeAssertWasNotSubscribed() {
assertThat(PublisherProbe.of(Mono.just(1)).wasSubscribed()).isFalse();
assertThat(PublisherProbe.of(Mono.just(2)).subscribeCount()).isEqualTo(0);
assertThat(PublisherProbe.of(Mono.just(3)).subscribeCount()).isNotPositive();
}
void testPublisherProbeAssertWasCancelled() {
assertThat(PublisherProbe.empty().wasCancelled()).isTrue();
}
void testPublisherProbeAssertWasNotCancelled() {
assertThat(PublisherProbe.empty().wasCancelled()).isFalse();
}
void testPublisherProbeAssertWasRequested() {
assertThat(PublisherProbe.empty().wasRequested()).isTrue();
}
void testPublisherProbeAssertWasNotRequested() {
assertThat(PublisherProbe.empty().wasRequested()).isFalse();
}
ImmutableSet<StepVerifier.FirstStep<Integer>> testStepVerifierFromMono() {
return ImmutableSet.of(
StepVerifier.create(Mono.just(1)), Mono.just(2).flux().as(StepVerifier::create));
@@ -707,6 +757,11 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return Mono.fromFuture(() -> ((AsyncLoadingCache<Integer, String>) null).get(0));
}
Mono<Map<Integer, String>> testMonoFromFutureAsyncLoadingCacheGetAll() {
return Mono.fromFuture(
() -> ((AsyncLoadingCache<Integer, String>) null).getAll(ImmutableSet.of()));
}
Flux<Integer> testFluxFromStreamSupplier() {
return Flux.fromStream(Stream.of(1));
}

View File

@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
@@ -47,6 +48,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
List.class,
ImmutableCollection.class,
ImmutableMap.class,
assertThat(false),
assertThat(0),
maxBy(null),
minBy(null),
@@ -190,7 +192,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
}
ImmutableSet<Flux<Integer>> testFluxJust() {
return ImmutableSet.of(Flux.just(0), Flux.just(2));
return ImmutableSet.of(Flux.just(0), Flux.just(2), Flux.just(3), Flux.just(4), Flux.just(5));
}
ImmutableSet<Mono<?>> testMonoIdentity() {
@@ -324,13 +326,25 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
Flux.just(1).mapNotNull(n -> n * 2));
}
Flux<String> testFluxMapNotNullTransformationOrElse() {
return Flux.just(1).mapNotNull(x -> Optional.of(x.toString()).orElse(null));
}
Flux<Integer> testFluxMapNotNullOrElse() {
return Flux.just(Optional.of(1)).mapNotNull(x -> x.orElse(null));
}
ImmutableSet<Flux<String>> testMonoFlux() {
return ImmutableSet.of(
Mono.just("foo").flux(), Mono.just("bar").flux(), Mono.just("baz").flux());
}
ImmutableSet<Mono<Void>> testMonoThen() {
return ImmutableSet.of(Mono.just("foo").then(), Mono.just("bar").then());
return ImmutableSet.of(
Mono.just("foo").then(),
Mono.just("bar").then(),
Mono.just("baz").then(),
Mono.just("qux").then());
}
ImmutableSet<Mono<Void>> testFluxThen() {
@@ -374,7 +388,9 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableSet.of(
Mono.just("foo").singleOptional(),
Mono.just("bar").singleOptional(),
Mono.just("baz").singleOptional());
Mono.just("baz").singleOptional(),
Mono.just("quux").singleOptional(),
Mono.just("quuz").singleOptional());
}
Mono<Number> testMonoCast() {
@@ -577,6 +593,35 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableSet.of(PublisherProbe.empty(), PublisherProbe.empty());
}
void testPublisherProbeAssertWasSubscribed() {
PublisherProbe.of(Mono.just(1)).assertWasSubscribed();
PublisherProbe.of(Mono.just(2)).assertWasSubscribed();
PublisherProbe.of(Mono.just(3)).assertWasSubscribed();
PublisherProbe.of(Mono.just(4)).assertWasSubscribed();
}
void testPublisherProbeAssertWasNotSubscribed() {
PublisherProbe.of(Mono.just(1)).assertWasNotSubscribed();
PublisherProbe.of(Mono.just(2)).assertWasNotSubscribed();
PublisherProbe.of(Mono.just(3)).assertWasNotSubscribed();
}
void testPublisherProbeAssertWasCancelled() {
PublisherProbe.empty().assertWasCancelled();
}
void testPublisherProbeAssertWasNotCancelled() {
PublisherProbe.empty().assertWasNotCancelled();
}
void testPublisherProbeAssertWasRequested() {
PublisherProbe.empty().assertWasRequested();
}
void testPublisherProbeAssertWasNotRequested() {
PublisherProbe.empty().assertWasNotRequested();
}
ImmutableSet<StepVerifier.FirstStep<Integer>> testStepVerifierFromMono() {
return ImmutableSet.of(
Mono.just(1).as(StepVerifier::create), Mono.just(2).as(StepVerifier::create));
@@ -681,6 +726,11 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return Mono.fromFuture(() -> ((AsyncLoadingCache<Integer, String>) null).get(0), true);
}
Mono<Map<Integer, String>> testMonoFromFutureAsyncLoadingCacheGetAll() {
return Mono.fromFuture(
() -> ((AsyncLoadingCache<Integer, String>) null).getAll(ImmutableSet.of()), true);
}
Flux<Integer> testFluxFromStreamSupplier() {
return Flux.fromStream(() -> Stream.of(1));
}

View File

@@ -141,6 +141,12 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
return Stream.of(1).findFirst().isPresent();
}
Stream<Integer> testStreamMapFilter() {
return Stream.of("foo")
.filter(ImmutableMap.of(1, 2)::containsKey)
.map(ImmutableMap.of(1, 2)::get);
}
ImmutableSet<Optional<String>> testStreamMin() {
return ImmutableSet.of(
Stream.of("foo").max(comparingInt(String::length).reversed()),

View File

@@ -141,6 +141,10 @@ final class StreamRulesTest implements RefasterRuleCollectionTestCase {
return Stream.of(1).findAny().isPresent();
}
Stream<Integer> testStreamMapFilter() {
return Stream.of("foo").map(ImmutableMap.of(1, 2)::get).filter(Objects::nonNull);
}
ImmutableSet<Optional<String>> testStreamMin() {
return ImmutableSet.of(
Stream.of("foo").min(comparingInt(String::length)),

View File

@@ -28,9 +28,9 @@ final class StringRulesTest implements RefasterRuleCollectionTestCase {
"foo".length() == 0,
"bar".length() <= 0,
"baz".length() < 1,
"foo".length() != 0,
"bar".length() > 0,
"baz".length() >= 1);
"qux".length() != 0,
"quux".length() > 0,
"corge".length() >= 1);
}
boolean testStringIsEmptyPredicate() {

View File

@@ -31,9 +31,9 @@ final class StringRulesTest implements RefasterRuleCollectionTestCase {
"foo".isEmpty(),
"bar".isEmpty(),
"baz".isEmpty(),
!"foo".isEmpty(),
!"bar".isEmpty(),
!"baz".isEmpty());
!"qux".isEmpty(),
!"quux".isEmpty(),
!"corge".isEmpty());
}
boolean testStringIsEmptyPredicate() {

View File

@@ -1,5 +1,6 @@
package tech.picnic.errorprone.refasterrules;
import com.google.errorprone.VisitorState;
import com.google.errorprone.fixes.SuggestedFix;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.Tree;
@@ -23,7 +24,9 @@ final class SuggestedFixRulesTest implements RefasterRuleCollectionTestCase {
}
SuggestedFix testSuggestedFixSwap() {
return SuggestedFix.builder().swap((Tree) null, (ExpressionTree) null).build();
return SuggestedFix.builder()
.swap((Tree) null, (ExpressionTree) null, (VisitorState) null)
.build();
}
SuggestedFix testSuggestedFixPrefixWith() {

View File

@@ -1,5 +1,6 @@
package tech.picnic.errorprone.refasterrules;
import com.google.errorprone.VisitorState;
import com.google.errorprone.fixes.SuggestedFix;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.Tree;
@@ -23,7 +24,7 @@ final class SuggestedFixRulesTest implements RefasterRuleCollectionTestCase {
}
SuggestedFix testSuggestedFixSwap() {
return SuggestedFix.swap((Tree) null, (ExpressionTree) null);
return SuggestedFix.swap((Tree) null, (ExpressionTree) null, (VisitorState) null);
}
SuggestedFix testSuggestedFixPrefixWith() {

View File

@@ -96,31 +96,79 @@ final class TestNGToAssertJRulesTest implements RefasterRuleCollectionTestCase {
}
void testAssertEqual() {
assertEquals(true, true);
assertEquals((byte) 0, (byte) 0);
assertEquals((char) 0, (char) 0);
assertEquals((short) 0, (short) 0);
assertEquals(0, 0);
assertEquals(0L, 0L);
assertEquals(0.0F, 0.0F);
assertEquals(0.0, 0.0);
assertEquals(new Object(), new Object());
assertEquals(true, false);
assertEquals(true, Boolean.FALSE);
assertEquals(Boolean.TRUE, false);
assertEquals(Boolean.TRUE, Boolean.FALSE);
assertEquals((byte) 0, (byte) 1);
assertEquals((byte) 0, Byte.decode("1"));
assertEquals(Byte.decode("0"), (byte) 1);
assertEquals(Byte.decode("0"), Byte.decode("1"));
assertEquals('a', 'b');
assertEquals('a', Character.valueOf('b'));
assertEquals(Character.valueOf('a'), 'b');
assertEquals(Character.valueOf('a'), Character.valueOf('b'));
assertEquals((short) 0, (short) 1);
assertEquals((short) 0, Short.decode("1"));
assertEquals(Short.decode("0"), (short) 1);
assertEquals(Short.decode("0"), Short.decode("1"));
assertEquals(0, 1);
assertEquals(0, Integer.valueOf(1));
assertEquals(Integer.valueOf(0), 1);
assertEquals(Integer.valueOf(0), Integer.valueOf(1));
assertEquals(0L, 1L);
assertEquals(0L, Long.valueOf(1));
assertEquals(Long.valueOf(0), 1L);
assertEquals(Long.valueOf(0), Long.valueOf(1));
assertEquals(0.0F, 1.0F);
assertEquals(0.0F, Float.valueOf(1.0F));
assertEquals(Float.valueOf(0.0F), 1.0F);
assertEquals(Float.valueOf(0.0F), Float.valueOf(1.0F));
assertEquals(0.0, 1.0);
assertEquals(0.0, Double.valueOf(1.0));
assertEquals(Double.valueOf(0.0), 1.0);
assertEquals(Double.valueOf(0.0), Double.valueOf(1.0));
assertEquals(new Object(), new StringBuilder());
assertEquals("actual", "expected");
assertEquals(ImmutableMap.of(), ImmutableMap.of());
assertEquals(ImmutableMap.of(), ImmutableMap.of(1, 2));
}
void testAssertEqualWithMessage() {
assertEquals(true, true, "foo");
assertEquals((byte) 0, (byte) 0, "bar");
assertEquals((char) 0, (char) 0, "baz");
assertEquals((short) 0, (short) 0, "qux");
assertEquals(0, 0, "quux");
assertEquals(0L, 0L, "quuz");
assertEquals(0.0F, 0.0F, "corge");
assertEquals(0.0, 0.0, "grault");
assertEquals(new Object(), new Object(), "garply");
assertEquals("actual", "expected", "waldo");
assertEquals(ImmutableMap.of(), ImmutableMap.of(), "plugh");
assertEquals(true, false, "foo");
assertEquals(true, Boolean.FALSE, "bar");
assertEquals(Boolean.TRUE, false, "baz");
assertEquals(Boolean.TRUE, Boolean.FALSE, "qux");
assertEquals((byte) 0, (byte) 1, "quux");
assertEquals((byte) 0, Byte.decode("1"), "corge");
assertEquals(Byte.decode("0"), (byte) 1, "grault");
assertEquals(Byte.decode("0"), Byte.decode("1"), "garply");
assertEquals('a', 'b', "waldo");
assertEquals('a', Character.valueOf('b'), "fred");
assertEquals(Character.valueOf('a'), 'b', "plugh");
assertEquals(Character.valueOf('a'), Character.valueOf('b'), "xyzzy");
assertEquals((short) 0, (short) 1, "thud");
assertEquals((short) 0, Short.decode("1"), "foo");
assertEquals(Short.decode("0"), (short) 1, "bar");
assertEquals(Short.decode("0"), Short.decode("1"), "baz");
assertEquals(0, 1, "qux");
assertEquals(0, Integer.valueOf(1), "quux");
assertEquals(Integer.valueOf(0), 1, "corge");
assertEquals(Integer.valueOf(0), Integer.valueOf(1), "grault");
assertEquals(0L, 1L, "garply");
assertEquals(0L, Long.valueOf(1), "waldo");
assertEquals(Long.valueOf(0), 1L, "fred");
assertEquals(Long.valueOf(0), Long.valueOf(1), "plugh");
assertEquals(0.0F, 1.0F, "xyzzy");
assertEquals(0.0F, Float.valueOf(1.0F), "thud");
assertEquals(Float.valueOf(0.0F), 1.0F, "foo");
assertEquals(Float.valueOf(0.0F), Float.valueOf(1.0F), "bar");
assertEquals(0.0, 1.0, "baz");
assertEquals(0.0, Double.valueOf(1.0), "qux");
assertEquals(Double.valueOf(0.0), 1.0, "quux");
assertEquals(Double.valueOf(0.0), Double.valueOf(1.0), "corge");
assertEquals(new Object(), new StringBuilder(), "grault");
assertEquals("actual", "expected", "garply");
assertEquals(ImmutableMap.of(), ImmutableMap.of(1, 2), "waldo");
}
void testAssertEqualFloatsWithDelta() {
@@ -163,6 +211,22 @@ final class TestNGToAssertJRulesTest implements RefasterRuleCollectionTestCase {
assertEquals(new Object[0], new Object[0], "garply");
}
void testAssertEqualFloatArraysWithDelta() {
assertEquals(new float[0], new float[0], 0.0F);
}
void testAssertEqualFloatArraysWithDeltaWithMessage() {
assertEquals(new float[0], new float[0], 0.0F, "foo");
}
void testAssertEqualDoubleArraysWithDelta() {
assertEquals(new double[0], new double[0], 0.0);
}
void testAssertEqualDoubleArraysWithDeltaWithMessage() {
assertEquals(new double[0], new double[0], 0.0, "foo");
}
void testAssertEqualArraysIrrespectiveOfOrder() {
assertEqualsNoOrder(new Object[0], new Object[0]);
}

View File

@@ -101,31 +101,79 @@ final class TestNGToAssertJRulesTest implements RefasterRuleCollectionTestCase {
}
void testAssertEqual() {
assertThat(true).isEqualTo(true);
assertThat((byte) 0).isEqualTo((byte) 0);
assertThat((char) 0).isEqualTo((char) 0);
assertThat((short) 0).isEqualTo((short) 0);
assertThat(0).isEqualTo(0);
assertThat(0L).isEqualTo(0L);
assertThat(0.0F).isEqualTo(0.0F);
assertThat(0.0).isEqualTo(0.0);
assertThat(new Object()).isEqualTo(new Object());
assertThat(true).isEqualTo(false);
assertThat(true).isEqualTo(Boolean.FALSE);
assertThat(Boolean.TRUE).isEqualTo(false);
assertThat(Boolean.TRUE).isEqualTo(Boolean.FALSE);
assertThat((byte) 0).isEqualTo((byte) 1);
assertThat((byte) 0).isEqualTo(Byte.decode("1"));
assertThat(Byte.decode("0")).isEqualTo((byte) 1);
assertThat(Byte.decode("0")).isEqualTo(Byte.decode("1"));
assertThat('a').isEqualTo('b');
assertThat('a').isEqualTo(Character.valueOf('b'));
assertThat(Character.valueOf('a')).isEqualTo('b');
assertThat(Character.valueOf('a')).isEqualTo(Character.valueOf('b'));
assertThat((short) 0).isEqualTo((short) 1);
assertThat((short) 0).isEqualTo(Short.decode("1"));
assertThat(Short.decode("0")).isEqualTo((short) 1);
assertThat(Short.decode("0")).isEqualTo(Short.decode("1"));
assertThat(0).isEqualTo(1);
assertThat(0).isEqualTo(Integer.valueOf(1));
assertThat(Integer.valueOf(0)).isEqualTo(1);
assertThat(Integer.valueOf(0)).isEqualTo(Integer.valueOf(1));
assertThat(0L).isEqualTo(1L);
assertThat(0L).isEqualTo(Long.valueOf(1));
assertThat(Long.valueOf(0)).isEqualTo(1L);
assertThat(Long.valueOf(0)).isEqualTo(Long.valueOf(1));
assertThat(0.0F).isEqualTo(1.0F);
assertThat(0.0F).isEqualTo(Float.valueOf(1.0F));
assertThat(Float.valueOf(0.0F)).isEqualTo(1.0F);
assertThat(Float.valueOf(0.0F)).isEqualTo(Float.valueOf(1.0F));
assertThat(0.0).isEqualTo(1.0);
assertThat(0.0).isEqualTo(Double.valueOf(1.0));
assertThat(Double.valueOf(0.0)).isEqualTo(1.0);
assertThat(Double.valueOf(0.0)).isEqualTo(Double.valueOf(1.0));
assertThat(new Object()).isEqualTo(new StringBuilder());
assertThat("actual").isEqualTo("expected");
assertThat(ImmutableMap.of()).isEqualTo(ImmutableMap.of());
assertThat(ImmutableMap.of()).isEqualTo(ImmutableMap.of(1, 2));
}
void testAssertEqualWithMessage() {
assertThat(true).withFailMessage("foo").isEqualTo(true);
assertThat((byte) 0).withFailMessage("bar").isEqualTo((byte) 0);
assertThat((char) 0).withFailMessage("baz").isEqualTo((char) 0);
assertThat((short) 0).withFailMessage("qux").isEqualTo((short) 0);
assertThat(0).withFailMessage("quux").isEqualTo(0);
assertThat(0L).withFailMessage("quuz").isEqualTo(0L);
assertThat(0.0F).withFailMessage("corge").isEqualTo(0.0F);
assertThat(0.0).withFailMessage("grault").isEqualTo(0.0);
assertThat(new Object()).withFailMessage("garply").isEqualTo(new Object());
assertThat("actual").withFailMessage("waldo").isEqualTo("expected");
assertThat(ImmutableMap.of()).withFailMessage("plugh").isEqualTo(ImmutableMap.of());
assertThat(true).withFailMessage("foo").isEqualTo(false);
assertThat(true).withFailMessage("bar").isEqualTo(Boolean.FALSE);
assertThat(Boolean.TRUE).withFailMessage("baz").isEqualTo(false);
assertThat(Boolean.TRUE).withFailMessage("qux").isEqualTo(Boolean.FALSE);
assertThat((byte) 0).withFailMessage("quux").isEqualTo((byte) 1);
assertThat((byte) 0).withFailMessage("corge").isEqualTo(Byte.decode("1"));
assertThat(Byte.decode("0")).withFailMessage("grault").isEqualTo((byte) 1);
assertThat(Byte.decode("0")).withFailMessage("garply").isEqualTo(Byte.decode("1"));
assertThat('a').withFailMessage("waldo").isEqualTo('b');
assertThat('a').withFailMessage("fred").isEqualTo(Character.valueOf('b'));
assertThat(Character.valueOf('a')).withFailMessage("plugh").isEqualTo('b');
assertThat(Character.valueOf('a')).withFailMessage("xyzzy").isEqualTo(Character.valueOf('b'));
assertThat((short) 0).withFailMessage("thud").isEqualTo((short) 1);
assertThat((short) 0).withFailMessage("foo").isEqualTo(Short.decode("1"));
assertThat(Short.decode("0")).withFailMessage("bar").isEqualTo((short) 1);
assertThat(Short.decode("0")).withFailMessage("baz").isEqualTo(Short.decode("1"));
assertThat(0).withFailMessage("qux").isEqualTo(1);
assertThat(0).withFailMessage("quux").isEqualTo(Integer.valueOf(1));
assertThat(Integer.valueOf(0)).withFailMessage("corge").isEqualTo(1);
assertThat(Integer.valueOf(0)).withFailMessage("grault").isEqualTo(Integer.valueOf(1));
assertThat(0L).withFailMessage("garply").isEqualTo(1L);
assertThat(0L).withFailMessage("waldo").isEqualTo(Long.valueOf(1));
assertThat(Long.valueOf(0)).withFailMessage("fred").isEqualTo(1L);
assertThat(Long.valueOf(0)).withFailMessage("plugh").isEqualTo(Long.valueOf(1));
assertThat(0.0F).withFailMessage("xyzzy").isEqualTo(1.0F);
assertThat(0.0F).withFailMessage("thud").isEqualTo(Float.valueOf(1.0F));
assertThat(Float.valueOf(0.0F)).withFailMessage("foo").isEqualTo(1.0F);
assertThat(Float.valueOf(0.0F)).withFailMessage("bar").isEqualTo(Float.valueOf(1.0F));
assertThat(0.0).withFailMessage("baz").isEqualTo(1.0);
assertThat(0.0).withFailMessage("qux").isEqualTo(Double.valueOf(1.0));
assertThat(Double.valueOf(0.0)).withFailMessage("quux").isEqualTo(1.0);
assertThat(Double.valueOf(0.0)).withFailMessage("corge").isEqualTo(Double.valueOf(1.0));
assertThat(new Object()).withFailMessage("grault").isEqualTo(new StringBuilder());
assertThat("actual").withFailMessage("garply").isEqualTo("expected");
assertThat(ImmutableMap.of()).withFailMessage("waldo").isEqualTo(ImmutableMap.of(1, 2));
}
void testAssertEqualFloatsWithDelta() {
@@ -168,6 +216,22 @@ final class TestNGToAssertJRulesTest implements RefasterRuleCollectionTestCase {
assertThat(new Object[0]).withFailMessage("garply").containsExactly(new Object[0]);
}
void testAssertEqualFloatArraysWithDelta() {
assertThat(new float[0]).containsExactly(new float[0], offset(0.0F));
}
void testAssertEqualFloatArraysWithDeltaWithMessage() {
assertThat(new float[0]).withFailMessage("foo").containsExactly(new float[0], offset(0.0F));
}
void testAssertEqualDoubleArraysWithDelta() {
assertThat(new double[0]).containsExactly(new double[0], offset(0.0));
}
void testAssertEqualDoubleArraysWithDeltaWithMessage() {
assertThat(new double[0]).withFailMessage("foo").containsExactly(new double[0], offset(0.0));
}
void testAssertEqualArraysIrrespectiveOfOrder() {
assertThat(new Object[0]).containsExactlyInAnyOrder(new Object[0]);
}

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.19.2-SNAPSHOT</version>
<version>0.21.1-SNAPSHOT</version>
</parent>
<artifactId>error-prone-experimental</artifactId>
@@ -15,26 +15,6 @@
<url>https://error-prone.picnic.tech</url>
<dependencies>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotation</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_check_api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_test_helpers</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>error-prone-utils</artifactId>
@@ -45,6 +25,31 @@
<artifactId>auto-service-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotation</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_check_api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_test_helpers</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
@@ -68,4 +73,16 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,296 @@
package tech.picnic.errorprone.experimental.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.sun.tools.javac.code.Flags.ENUM;
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.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Streams;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.Var;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.CompilationUnitTreeMatcher;
import com.google.errorprone.fixes.Replacement;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.google.errorprone.util.ErrorProneToken;
import com.google.errorprone.util.ErrorProneTokens;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.parser.Tokens.TokenKind;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.Position;
import java.util.Optional;
import java.util.Set;
import javax.lang.model.element.Modifier;
/**
* A {@link BugChecker} that flags classes with a non-canonical member order.
*
* <p>Class members should be ordered as follows:
*
* <ol>
* <li>Static fields
* <li>Instance fields
* <li>Static initializer blocks
* <li>Instance initializer blocks
* <li>Constructors
* <li>Methods
* <li>Nested classes, interfaces and enums
* </ol>
*
* @see <a
* href="https://checkstyle.sourceforge.io/apidocs/com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheck.html">Checkstyle's
* {@code DeclarationOrderCheck}</a>
*/
// XXX: Consider introducing support for ordering members in records or annotation definitions.
// XXX: Exclude checkstyle from running against (this checker's) test files.
@AutoService(BugChecker.class)
@BugPattern(
summary = "Type members should be defined in a canonical order",
link = BUG_PATTERNS_BASE_URL + "TypeMemberOrder",
linkType = CUSTOM,
severity = WARNING,
tags = STYLE)
public final class TypeMemberOrder extends BugChecker implements CompilationUnitTreeMatcher {
private static final long serialVersionUID = 1L;
/** Instantiates a new {@link TypeMemberOrder} instance. */
public TypeMemberOrder() {}
@Override
public Description matchCompilationUnit(
CompilationUnitTree compilationUnitTree, VisitorState state) {
SuggestedFix.Builder suggestedFixes = SuggestedFix.builder();
for (Tree tree : compilationUnitTree.getTypeDecls()) {
if (!isSuppressed(tree, state) && tree instanceof ClassTree classTree) {
suggestedFixes.merge(
matchClass(classTree, state, (JCTree.JCCompilationUnit) compilationUnitTree));
}
}
SuggestedFix suggestedFix = suggestedFixes.build();
if (suggestedFix.isEmpty()) {
return Description.NO_MATCH;
}
return describeMatch(compilationUnitTree, suggestedFix);
}
private SuggestedFix matchClass(
ClassTree tree, VisitorState state, JCTree.JCCompilationUnit compilationUnit) {
Kind treeKind = tree.getKind();
if (treeKind != Kind.CLASS && treeKind != Kind.INTERFACE && treeKind != Kind.ENUM) {
return SuggestedFix.emptyFix();
}
int bodyStartPos = getBodyStartPos(tree, state);
if (bodyStartPos == Position.NOPOS) {
/*
* We can't determine the type body's start position in the source code. This generally means
* that (part of) its code was generated. Even if the source code for a subset of its members
* is available, dealing with this edge case is not worth the trouble.
*/
return SuggestedFix.emptyFix();
}
ImmutableList<TypeMember> members = getAllTypeMembers(tree, bodyStartPos, state);
boolean topLevelSorted = members.equals(ImmutableList.sortedCopyOf(members));
if (topLevelSorted) {
SuggestedFix.Builder nestedSuggestedFixes = SuggestedFix.builder();
for (TypeMember member : members) {
if (member.tree() instanceof ClassTree memberClassTree) {
SuggestedFix other = matchClass(memberClassTree, state, compilationUnit);
nestedSuggestedFixes.merge(other);
}
}
return nestedSuggestedFixes.build();
}
CharSequence source = requireNonNull(state.getSourceCode(), "Source code");
ImmutableMap.Builder<TypeMember, String> typeMemberSource = ImmutableMap.builder();
for (TypeMember member : members) {
if (member.tree() instanceof ClassTree memberClassTree) {
SuggestedFix suggestedFix = matchClass(memberClassTree, state, compilationUnit);
@Var
String memberSource =
source.subSequence(member.startPosition(), member.endPosition()).toString();
// Diff between memberSource and replacement positions.
@Var int diff = -member.startPosition();
for (Replacement replacement : suggestedFix.getReplacements(compilationUnit.endPositions)) {
memberSource =
memberSource.subSequence(0, replacement.startPosition() + diff)
+ replacement.replaceWith()
+ memberSource.subSequence(
replacement.endPosition() + diff, memberSource.length());
diff +=
replacement.replaceWith().length()
- (replacement.endPosition() - replacement.startPosition());
}
typeMemberSource.put(member, memberSource);
} else {
typeMemberSource.put(
member, source.subSequence(member.startPosition(), member.endPosition()).toString());
}
}
return sortTypeMembers(members, typeMemberSource.build());
}
/** Returns all members that can be moved or may lay between movable ones. */
private ImmutableList<TypeMember> getAllTypeMembers(
ClassTree tree, int bodyStartPos, VisitorState state) {
ImmutableList.Builder<TypeMember> builder = ImmutableList.builder();
@Var int currentStartPos = bodyStartPos;
for (Tree member : tree.getMembers()) {
if (state.getEndPosition(member) == Position.NOPOS) {
continue;
}
int treeStartPos = currentStartPos;
getMemberTypeOrdinal(member, state)
.ifPresent(
e ->
builder.add(
new AutoValue_TypeMemberOrder_TypeMember(
member, treeStartPos, state.getEndPosition(member), e)));
/* XXX: Write explanation about this enum. */
currentStartPos = Math.max(currentStartPos, state.getEndPosition(member));
}
return builder.build();
}
/**
* Returns the preferred ordinal of the given member, or empty if it's unmovable for any reason,
* including it lacking a preferred ordinal.
*/
private Optional<Integer> getMemberTypeOrdinal(Tree tree, VisitorState state) {
if (isSuppressed(tree, state) || isEnumeratorDefinition(tree)) {
return Optional.empty();
}
return switch (tree.getKind()) {
case VARIABLE -> Optional.of(isStatic((VariableTree) tree) ? 1 : 2);
case BLOCK -> Optional.of(isStatic((BlockTree) tree) ? 3 : 4);
case METHOD -> Optional.of(isConstructor((MethodTree) tree) ? 5 : 6);
case CLASS, INTERFACE, ENUM -> Optional.of(7);
default -> Optional.empty();
};
}
/**
* Returns the start position of the body of the given type, in the case of enums, it returns the
* position that follows the enumerated type's enumerations.
*/
private static int getBodyStartPos(ClassTree tree, VisitorState state) {
CharSequence sourceCode = state.getSourceCode();
/*
* To avoid including the type's preceding annotations, use `getPreferredPosition()` rather than
* `ASTHelpers`.
*/
int typeStart = ((JCTree.JCClassDecl) tree).getPreferredPosition();
int typeEnd = state.getEndPosition(tree);
if (sourceCode == null || typeStart == Position.NOPOS || typeEnd == Position.NOPOS) {
return Position.NOPOS;
}
/*
* Returns the source code position of the first token that comes after the first curly left
* bracket.
*/
return ErrorProneTokens.getTokens(
sourceCode.subSequence(typeStart, typeEnd).toString(), typeStart, state.context)
.stream()
.dropWhile(token -> token.kind() != TokenKind.LBRACE)
/*
* To accommodate enums, skip processing their enumerators. This is needed as Error Prone
* has access to the enumerations individually, but not to the whole expression that
* declares them, leaving the semicolon trailing the declarations unaccounted for. The
* current logic would move this trailing semicolon with the first member after the
* enumerations instead of leaving it to close the enumerations' declaration, introducing a
* syntax error.
*/
.dropWhile(token -> tree.getKind() == Kind.ENUM && token.kind() != TokenKind.SEMI)
.findFirst()
.map(ErrorProneToken::endPos)
.orElse(Position.NOPOS);
}
/**
* Returns true if {@link Tree} is an enum or an enumerator definition, false otherwise.
*
* @see com.sun.tools.javac.code.Flags#ENUM
*/
private static boolean isEnumeratorDefinition(Tree tree) {
return tree instanceof JCVariableDecl variableDecl && (variableDecl.mods.flags & ENUM) != 0;
}
/**
* Suggests a different way of ordering the given type members.
*
* @implNote For each member, this method tracks the source code between the end of the definition
* of the member that precedes it (or the start of the type body if there is no such member)
* and the end of the definition of the member itself. This subsequently enables moving
* members around, including any preceding comments and Javadoc. This approach isn't perfect,
* and may at times move too much code or documentation around; users will have to manually
* resolve this.
*/
private static SuggestedFix sortTypeMembers(
ImmutableList<TypeMember> members, ImmutableMap<TypeMember, String> sourceCode) {
return Streams.zip(
members.stream(),
members.stream().sorted(),
(original, replacement) -> {
String replacementSource = requireNonNull(sourceCode.get(replacement), "replacement");
return original.equals(replacement)
? SuggestedFix.emptyFix()
: SuggestedFix.replace(
original.startPosition(), original.endPosition(), replacementSource);
})
.reduce(SuggestedFix.builder(), SuggestedFix.Builder::merge, SuggestedFix.Builder::merge)
.build();
}
private static boolean isStatic(VariableTree variableTree) {
Set<Modifier> modifiers = variableTree.getModifiers().getFlags();
return modifiers.contains(Modifier.STATIC);
}
private static boolean isStatic(BlockTree blockTree) {
return blockTree.isStatic();
}
private static boolean isConstructor(MethodTree methodTree) {
return ASTHelpers.getSymbol(methodTree).isConstructor();
}
@AutoValue
abstract static class TypeMember implements Comparable<TypeMember> {
abstract Tree tree();
abstract int startPosition();
abstract int endPosition();
abstract int preferredOrdinal();
@Override
public int compareTo(TypeMemberOrder.TypeMember o) {
return Integer.compare(preferredOrdinal(), o.preferredOrdinal());
}
}
}

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