Compare commits

...

242 Commits

Author SHA1 Message Date
Rick Ossendrijver
7b31e32aee Introduce MonoSingle Refaster rule 2023-06-29 14:40:55 +02:00
Pieter Dirk Soels
08298c48e3 Specify correct Error Prone version
For first build, run with `-Dversion.error-prone-orig=2.20.0`.
2023-06-22 15:34:50 +02:00
Pieter Dirk Soels
c7aa58409c Post rebase fix 2023-06-22 13:29:49 +02:00
Rick Ossendrijver
727bf4ebe1 Format and migration-util/pom.xml version bump 2023-06-22 13:28:02 +02:00
Rick Ossendrijver
aadffd7c97 Add NotMatches part 2023-06-22 13:28:02 +02:00
Rick Ossendrijver
9f0331e7ef Bring branch up to date with new setup 2023-06-22 13:28:01 +02:00
Rick Ossendrijver
1dddb0917c Add things to make the branch work again 2023-06-22 13:28:01 +02:00
Rick Ossendrijver
0362ad27e8 Resolve conflicts after rebasing, updating stuff 2023-06-22 13:28:00 +02:00
Rick Ossendrijver
1a26a23c2a Add templates for RxJava types #timeout(long, TimeUnit) 2023-06-22 13:28:00 +02:00
Rick Ossendrijver
cbea5d9868 Introduce MonoFlatMapIterable 2023-06-22 13:27:59 +02:00
Rick Ossendrijver
9cc62d2c50 Add Reactor templates based on feedback of Enric and Phil 2023-06-22 13:27:58 +02:00
Rick Ossendrijver
b1da6443f7 Removing Mono<Void> -> Completable identity conversion rewrite with then() 2023-06-22 13:27:58 +02:00
Rick Ossendrijver
a0f3a79c79 Add template to improve ImmutableList#copyOf(Flux#toIterable) 2023-06-22 13:27:57 +02:00
Rick Ossendrijver
4f3956b97f Add MonoThenMany template 2023-06-22 13:27:57 +02:00
Rick Ossendrijver
c89c9267c2 Introduce MonoBlock template 2023-06-22 13:27:56 +02:00
Rick Ossendrijver
61e4a360c7 Remove warnings, improve code, and remove XXXsg 2023-06-22 13:27:56 +02:00
Rick Ossendrijver
1f4f4bb0cd Add assorted tests to fix XXXs 2023-06-22 13:27:55 +02:00
Rick Ossendrijver
491d71a8a9 Improve templates after migrating delivery-service 2023-06-22 13:27:55 +02:00
Rick Ossendrijver
c4bfd2cb87 Add FluxSingle template 2023-06-22 13:27:54 +02:00
Rick Ossendrijver
fff86ea618 Add Reactor improvements based on feedback from Enric 2023-06-22 13:27:53 +02:00
Rick Ossendrijver
091f3ac459 Add Refaster templates to optimize Reactor code 2023-06-22 13:27:53 +02:00
Rick Ossendrijver
d9fa33ee58 Rename Templates and add templates for migration 2023-06-22 13:27:52 +02:00
Rick Ossendrijver
05b6aa03df Add Flowable#blockingIterable Refaster template 2023-06-22 13:27:52 +02:00
Rick Ossendrijver
e6ea9fd37e Improve the Single#never template 2023-06-22 13:27:51 +02:00
Rick Ossendrijver
6a43edcd41 Add Single#never() template 2023-06-22 13:27:51 +02:00
Rick Ossendrijver
12af1e767c Cleanup, sorting, moving, tests 2023-06-22 13:27:50 +02:00
Rick Ossendrijver
edebc1a7d2 Add templates for Gravitee migration 2023-06-22 13:27:50 +02:00
Rick Ossendrijver
3d4ed4daa8 Improve template generic types 2023-06-22 13:27:49 +02:00
Rick Ossendrijver
deb8e9f192 Run textwidth=72 2023-06-22 13:27:48 +02:00
Rick Ossendrijver
218107d486 Many rewrites 2023-06-22 13:27:48 +02:00
Stephan Schroevers
6c406f919f Submit defeat 2023-06-22 13:27:47 +02:00
Rick Ossendrijver
23291e3e76 Add fix for every occurence but is still problem 2023-06-22 13:27:47 +02:00
Rick Ossendrijver
20d787d694 Many rewrite from .as() to RxJava2Adapter.() 2023-06-22 13:27:46 +02:00
Stephan Schroevers
65ca655360 Generalize 2023-06-22 13:27:46 +02:00
Rick Ossendrijver
ebe38e1d0f Push 2023-06-22 13:27:45 +02:00
Rick Ossendrijver
24abb53ef8 Move some templates and temporarily turn off for start of migration 2023-06-22 13:27:44 +02:00
Rick Ossendrijver
a405afb33f Add new templates 2023-06-22 13:27:44 +02:00
Rick Ossendrijver
e86b3b4f8b Add assorted templates to improve the migration 2023-06-22 13:27:43 +02:00
Rick Ossendrijver
bebfb81dd4 Add exttra rule for Flux.array with single element 2023-06-22 13:27:43 +02:00
Rick Ossendrijver
a460591d3f Add extra cases for test.await.assertXXX 2023-06-22 13:27:42 +02:00
Rick Ossendrijver
7eec69cbc3 Add extra templates 2023-06-22 13:27:41 +02:00
Rick Ossendrijver
d4a3083e28 Add extra templates and tests 2023-06-22 13:27:41 +02:00
Rick Ossendrijver
eb482ae5e8 Apply suggestions and add templates 2023-06-22 13:27:40 +02:00
Rick Ossendrijver
6ab90b2ad8 Fix tests and improve imports of the tests 2023-06-22 13:27:40 +02:00
Rick Ossendrijver
0a135c8db0 Add unwrap methods 2023-06-22 13:27:39 +02:00
Rick Ossendrijver
b45e5bf43d Add RxJavaUnwrapTemplates 2023-06-22 13:27:39 +02:00
Rick Ossendrijver
a80ca8b0cd Format for Stephan ;) 2023-06-22 13:27:38 +02:00
Rick Ossendrijver
a8bdaea5d6 Update the templates by removing some flat/concat|maps and adding tests 2023-06-22 13:27:37 +02:00
Rick Ossendrijver
48a463405f Add minor improvements and cleanup 2023-06-22 13:27:37 +02:00
Rick Ossendrijver
623e63b655 Add clean up Refaster templates 2023-06-22 13:27:36 +02:00
Rick Ossendrijver
d48855cbb4 Improve some templates and add one for every RxJava type for assertNoValues() 2023-06-22 13:27:36 +02:00
Rick Ossendrijver
184059a80c Write templates and fix edge cases 2023-06-22 13:27:35 +02:00
Rick Ossendrijver
d9526c886d Improve templates and cleanup tests 2023-06-22 13:27:35 +02:00
Stephan Schroevers
a2ba2a4110 As discussed 2023-06-22 13:27:34 +02:00
Rick Ossendrijver
50beb3aa17 Improve templates so that they match PRP code 2023-06-22 13:27:33 +02:00
Rick Ossendrijver
429e87bec5 Add two examples that work for flatMapCompletable single and flowable 2023-06-22 13:27:33 +02:00
Rick Ossendrijver
6390a02528 YESSSS fix the flatmapCompletable for Single and Flowable 2023-06-22 13:27:32 +02:00
Stephan Schroevers
a790ff0b65 Ideation 2023-06-22 13:27:32 +02:00
Rick Ossendrijver
cc2ce34e2a Quick push 2023-06-22 13:27:31 +02:00
Stephan Schroevers
8c8d6bd466 Pair programming 2023-06-22 13:27:31 +02:00
Rick Ossendrijver
9efb86709e Do more things 2023-06-22 13:27:30 +02:00
Rick Ossendrijver
a2e8f7cc32 Add templates for assertFailure and a MonoNestedPublisher 2023-06-22 13:27:29 +02:00
Rick Ossendrijver
bd7dad5b33 Many tweaks and improvements 2023-06-22 13:27:29 +02:00
Rick Ossendrijver
296ae53d08 Update the templates and format 2023-06-22 13:27:28 +02:00
Rick Ossendrijver
0692fb0c6d Add extra case of assertValue with assertComplete 2023-06-22 13:27:28 +02:00
Rick Ossendrijver
af259ce97e Add AssertValue for 4 types 2023-06-22 13:27:27 +02:00
Rick Ossendrijver
6405f5560b Add the test conversions to StepVerifier for Completable 2023-06-22 13:27:27 +02:00
Rick Ossendrijver
ee89694628 Add RxJava test to StepVerifier tests templates 2023-06-22 13:27:26 +02:00
Rick Ossendrijver
2eaa77799a Add import to test output file 2023-06-22 13:27:25 +02:00
Rick Ossendrijver
d9b06d98e4 Add filtering 2023-06-22 13:27:25 +02:00
Rick Ossendrijver
b856e936ed Cleanup code and tests 2023-06-22 13:27:24 +02:00
Rick Ossendrijver
27d262d5ef Add templates and cleanup todos 2023-06-22 13:27:24 +02:00
Rick Ossendrijver
b825d2653f Fix some todos and remove some annotations sadly enough 2023-06-22 13:27:23 +02:00
Rick Ossendrijver
e40f386bd2 Solve XXXs and add tests and templates 2023-06-22 13:27:23 +02:00
Rick Ossendrijver
374aab3b6b Add todos and information 2023-06-22 13:27:22 +02:00
Rick Ossendrijver
8fc432a49a Update many templates and move the MigrationUtil to own module 2023-06-22 13:27:21 +02:00
Rick Ossendrijver
87c7c8772b Fix bug in test 2023-06-22 13:27:21 +02:00
Rick Ossendrijver
d9dd12c058 Some errors in there, but push many new templates 2023-06-22 13:27:20 +02:00
Rick Ossendrijver
5777c510fd Add many templates and tiny improvements. Note: tests are broken now because building against picnic-error-prone 2023-06-22 13:27:20 +02:00
Rick Ossendrijver
4cc885c6e4 Make EPS use the HEAD-SNAPSHOT of Error Prone for CanBeTransformedTo 2023-06-22 13:27:19 +02:00
Rick Ossendrijver
c36850b10a Add templates and tests 2023-06-22 13:27:19 +02:00
Rick Ossendrijver
b707dfa382 Improve the templates and add many tests 2023-06-22 13:27:18 +02:00
Rick Ossendrijver
a481ba3add Turn on extra tests and fix many issues, but leave a few for next time. 2023-06-22 13:27:17 +02:00
Rick Ossendrijver
583705f0cc Small improvements 2023-06-22 13:27:17 +02:00
Rick Ossendrijver
3bfdd2d550 Add todos and some cases 2023-06-22 13:27:16 +02:00
Rick Ossendrijver
a2a684fa0a Add templates and tests 2023-06-22 13:27:16 +02:00
Rick Ossendrijver
439c0ed71f Add some templates 2023-06-22 13:27:15 +02:00
Rick Ossendrijver
37529ac23b Add FlowableZipWith 2023-06-22 13:27:15 +02:00
Stephan Schroevers
03f6929de8 Pair programming 2023-06-22 13:27:14 +02:00
Rick Ossendrijver
d2927feb52 Add todos and implements some templates 2023-06-22 13:27:14 +02:00
Rick Ossendrijver
93c1f85df8 Remove public modifiers from the XXXs and add two templates 2023-06-22 13:27:13 +02:00
Rick Ossendrijver
ef94344325 Improve templates with assorted methods 2023-06-22 13:27:12 +02:00
Rick Ossendrijver
097939c8c6 Remove old ideation on the CanBeCoercedTo problem and an early version of the MyUtil with conversion of io.reactivex.function to java.util.function 2023-06-22 13:27:12 +02:00
Rick Ossendrijver
6c8d71845b Move every template to their respective class and cleanup RxJavaToReactorTemplates.java 2023-06-22 13:27:11 +02:00
Rick Ossendrijver
7662b67b85 Add test templates to the RefasterCheckTest 2023-06-22 13:27:11 +02:00
Stephan Schroevers
95972a8441 Fix it 2023-06-22 13:27:10 +02:00
Rick Ossendrijver
f0f20702dd Quick commit 2023-06-22 13:27:10 +02:00
Rick Ossendrijver
fe5abe0bec Add the testFlowableCombineLatest 2023-06-22 13:27:09 +02:00
Rick Ossendrijver
254c5bc6a5 Add some templates like fromCallable and FromAction and fromFuture 2023-06-22 13:27:09 +02:00
Rick Ossendrijver
951bad0b03 Add some templates with Amb and add the RxJavaMaybeToReactor file 2023-06-22 13:27:08 +02:00
Rick Ossendrijver
24b2d60c82 Add Templates for the RxJava types that we want to migrate to Reactor 2023-06-22 13:27:07 +02:00
Rick Ossendrijver
5738f87d5b Update the ordering of the Templates and the tests to be in alphabetical order 2023-06-22 13:27:07 +02:00
Rick Ossendrijver
c0ea2c2e51 Remove some warnings 2023-06-22 13:27:06 +02:00
Rick Ossendrijver
3a6416bcdc Try to fix the tests 2023-06-22 13:27:06 +02:00
Rick Ossendrijver
6bd3ae5f3d Add 1/4 templates for Flowable.concatWith 2023-06-22 13:27:05 +02:00
Stephan Schroevers
b7bc2e7581 One more 2023-06-22 13:27:05 +02:00
Stephan Schroevers
73af34f075 Ideation 2023-06-22 13:27:04 +02:00
Rick Ossendrijver
53e9088225 Fix typo 2023-06-22 13:27:04 +02:00
Rick Ossendrijver
3c4bf15c73 Update templates and fix some tests for the new templates and reorder some 2023-06-22 13:27:03 +02:00
Stephan Schroevers
7548ede38f WIP 2023-06-22 13:27:02 +02:00
Rick Ossendrijver
cfaae68a9f Add some examples because im not sure how it works now 2023-06-22 13:27:02 +02:00
Rick Ossendrijver
de9defd58f Add some tests for the new templates 2023-06-22 13:27:01 +02:00
Stephan Schroevers
c66eb42357 Pair programming 2023-06-22 13:27:01 +02:00
Rick Ossendrijver
62ce7c26de Improve some comments and add an extra template 2023-06-22 13:27:00 +02:00
Rick Ossendrijver
fdf202812e Update templates and add a few commented templates to discuss with Stephan 2023-06-22 13:27:00 +02:00
Rick Ossendrijver
33b3d44fe8 Add tests for added templates 2023-06-22 13:26:59 +02:00
Rick Ossendrijver
59e1debdab Change order of functions and add a failing one 2023-06-22 13:26:59 +02:00
Rick Ossendrijver
323138c1d7 Fix last part of test 2023-06-22 13:26:58 +02:00
Rick Ossendrijver
a963e2e887 Do some renaming and improve almost all tests 2023-06-22 13:26:57 +02:00
Stephan Schroevers
299d964e4a Pair programming 2023-06-22 13:26:57 +02:00
Stephan Schroevers
a8c9f1ecc8 Fix it 2023-06-22 13:26:56 +02:00
Rick Ossendrijver
cf450783e3 Improve the templates 2023-06-22 13:26:56 +02:00
Rick Ossendrijver
c7901bc5c7 Update some of the Templates 2023-06-22 13:26:55 +02:00
Rick Ossendrijver
d44d03f9b1 Add RxJavaToReactorTemplates class with Input and Output Test files. 2023-06-22 13:26:55 +02:00
Picnic-Bot
2837a04433 Upgrade New Relic Java Agent 8.3.0 -> 8.4.0 (#690)
See:
- https://github.com/newrelic/newrelic-java-agent/releases/tag/v8.4.0
- https://github.com/newrelic/newrelic-java-agent/compare/v8.3.0...v8.4.0
2023-06-22 11:55:19 +02:00
Picnic-Bot
74222e8fa5 Upgrade Arcmutate 1.0.5 -> 1.1.0 (#692) 2023-06-22 09:25:43 +02:00
Stephan Schroevers
dc36ff2c0b [maven-release-plugin] prepare for next development iteration 2023-06-21 21:15:38 +02:00
Stephan Schroevers
09208aa49a [maven-release-plugin] prepare release v0.12.0 2023-06-21 21:15:38 +02:00
Stephan Schroevers
b81ec973a1 Upgrade Error Prone 2.19.1 -> 2.20.0 (#685)
Summary of key changes:
- The `MissingRefasterAnnotation` check was contributed to Error Prone,
  and so is deleted here (multiple checks with the same name are not
  supported).
- Similarly, Error Prone now supports the `-XepAllSuggestionsAsWarnings`
  flag out of the box. So the `ErrorProneFork` class is deleted, as it
  has currently no further use.

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

Fixes #686.

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

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

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

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

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

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

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

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

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

View File

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

View File

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

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

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

View File

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

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

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

View File

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

View File

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

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

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

View File

@@ -1,5 +1,8 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"helpers:pinGitHubActionDigests"
],
"packageRules": [
{
"matchPackagePatterns": [

View File

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

View File

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

23
SECURITY.md Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.9.0</version>
<version>0.12.1-SNAPSHOT</version>
</parent>
<artifactId>error-prone-contrib</artifactId>
@@ -49,9 +49,22 @@
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-support</artifactId>
<artifactId>migration-util</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-compiler</artifactId>
<!-- This dependency is declared only as a hint to Maven that
compilation depends on it; see the `maven-compiler-plugin`'s
`annotationProcessorPaths` configuration below. -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-support</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-test-support</artifactId>
@@ -126,6 +139,11 @@
<artifactId>jakarta.servlet-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
@@ -236,6 +254,11 @@
<artifactId>refaster-support</artifactId>
<version>${project.version}</version>
</path>
<path>
<groupId>${project.groupId}</groupId>
<artifactId>migration-util</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs combine.children="append">
<arg>-Xplugin:RefasterRuleCompiler</arg>

View File

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

View File

@@ -58,8 +58,8 @@ public final class AutowiredConstructor extends BugChecker implements ClassTreeM
/*
* This is the only `@Autowired` constructor: suggest that it be removed. Note that this likely
* means that the associated import can be removed as well. Rather than adding code for this case we
* leave flagging the unused import to Error Prone's `RemoveUnusedImports` check.
* means that the associated import can be removed as well. Rather than adding code for this
* case we leave flagging the unused import to Error Prone's `RemoveUnusedImports` check.
*/
AnnotationTree annotation = Iterables.getOnlyElement(annotations);
return describeMatch(annotation, SourceCode.deleteWithTrailingWhitespace(annotation, state));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -35,6 +35,8 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
// XXX: Consider detecting cases where a flagged expression is passed to a method, and where removal
// of the identity conversion would cause a different method overload to be selected. Depending on
// the target method such a modification may change the code's semantics or performance.
// XXX: Also flag `Stream#map`, `Mono#map` and `Flux#map` invocations where the given transformation
// is effectively the identity operation.
@AutoService(BugChecker.class)
@BugPattern(
summary = "Avoid or clarify identity conversions",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,15 +16,18 @@ import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AbstractBooleanAssert;
import org.assertj.core.api.AbstractCollectionAssert;
import org.assertj.core.api.AbstractMapAssert;
import org.assertj.core.api.MapAssert;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
@@ -220,6 +223,19 @@ final class AssertJMapRules {
}
}
static final class AssertThatMapContainsOnlyKeys<K, V> {
@BeforeTemplate
AbstractCollectionAssert<?, Collection<? extends K>, K, ?> before(Map<K, V> map, Set<K> keys) {
return assertThat(map.keySet()).hasSameElementsAs(keys);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
MapAssert<K, V> after(Map<K, V> map, Set<K> keys) {
return assertThat(map).containsOnlyKeys(keys);
}
}
static final class AssertThatMapContainsValue<K, V> {
@BeforeTemplate
AbstractBooleanAssert<?> before(Map<K, V> map, V value) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ package tech.picnic.errorprone.refasterrules;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChooser;
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
@@ -25,6 +26,7 @@ final class BugCheckerRules {
}
@AfterTemplate
@CanIgnoreReturnValue
BugCheckerRefactoringTestHelper after(BugCheckerRefactoringTestHelper helper) {
return helper;
}

View File

@@ -35,6 +35,7 @@ final class CollectionRules {
*/
static final class CollectionIsEmpty<T> {
@BeforeTemplate
@SuppressWarnings("java:S1155" /* This violation will be rewritten. */)
boolean before(Collection<T> collection) {
return Refaster.anyOf(
collection.size() == 0,
@@ -163,6 +164,8 @@ final class CollectionRules {
}
/** Prefer {@link ArrayList#ArrayList(Collection)} over the Guava alternative. */
@SuppressWarnings(
"NonApiType" /* Matching against `List` would unnecessarily constrain the rule. */)
static final class NewArrayListFromCollection<T> {
@BeforeTemplate
ArrayList<T> before(Collection<T> collection) {

View File

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

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.collect.Streams;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
@@ -27,6 +28,7 @@ final class DoubleStreamRules {
}
@AfterTemplate
@CanIgnoreReturnValue
DoubleStream after(DoubleStream stream) {
return stream;
}
@@ -237,6 +239,7 @@ final class DoubleStreamRules {
/** Prefer {@link DoubleStream#anyMatch(DoublePredicate)} over more contrived alternatives. */
static final class DoubleStreamAnyMatch {
@BeforeTemplate
@SuppressWarnings("java:S4034" /* This violation will be rewritten. */)
boolean before(DoubleStream stream, DoublePredicate predicate) {
return Refaster.anyOf(
!stream.noneMatch(predicate), stream.filter(predicate).findAny().isPresent());

View File

@@ -1,5 +1,6 @@
package tech.picnic.errorprone.refasterrules;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.AlsoNegation;
@@ -30,6 +31,7 @@ final class EqualityRules {
@AfterTemplate
@AlsoNegation
@SuppressWarnings("java:S1698" /* Reference comparison is valid for enums. */)
boolean after(T a, T b) {
return a == b;
}
@@ -56,11 +58,13 @@ final class EqualityRules {
/** Avoid double negations; this is not Javascript. */
static final class DoubleNegation {
@BeforeTemplate
@SuppressWarnings("java:S2761" /* This violation will be rewritten. */)
boolean before(boolean b) {
return !!b;
}
@AfterTemplate
@CanIgnoreReturnValue
boolean after(boolean b) {
return b;
}
@@ -72,6 +76,7 @@ final class EqualityRules {
*/
// XXX: Replacing `a ? !b : b` with `a != b` changes semantics if both `a` and `b` are boxed
// booleans.
@SuppressWarnings("java:S1940" /* This violation will be rewritten. */)
static final class Negation {
@BeforeTemplate
boolean before(boolean a, boolean b) {
@@ -79,6 +84,8 @@ final class EqualityRules {
}
@BeforeTemplate
@SuppressWarnings(
"java:S1244" /* The equality check is fragile, but may be seen in the wild. */)
boolean before(double a, double b) {
return !(a == b);
}
@@ -100,6 +107,7 @@ final class EqualityRules {
*/
// XXX: Replacing `a ? b : !b` with `a == b` changes semantics if both `a` and `b` are boxed
// booleans.
@SuppressWarnings("java:S1940" /* This violation will be rewritten. */)
static final class IndirectDoubleNegation {
@BeforeTemplate
boolean before(boolean a, boolean b) {
@@ -107,6 +115,8 @@ final class EqualityRules {
}
@BeforeTemplate
@SuppressWarnings(
"java:S1244" /* The inequality check is fragile, but may be seen in the wild. */)
boolean before(double a, double b) {
return !(a != b);
}

View File

@@ -188,6 +188,8 @@ final class ImmutableMapRules {
/**
* Prefer creating an immutable copy of the result of {@link Maps#transformValues(Map,
* com.google.common.base.Function)} over more contrived alternatives.
*
* <p>Additionally, this way it is easier to see that only values are being transformed.
*/
abstract static class TransformMapValuesToImmutableMap<K, V1, V2> {
@Placeholder(allowsIdentity = true)
@@ -250,7 +252,8 @@ final class ImmutableMapRules {
* Prefer {@link ImmutableMap#of(Object, Object, Object, Object)} over alternatives that don't
* communicate the immutability of the resulting map at the type level.
*/
// XXX: Also rewrite the `ImmutableMap.builder()` variant?
// XXX: Consider introducing a `BugChecker` to replace these `ImmutableMapOfX` rules. That will
// also make it easier to rewrite various `ImmutableMap.builder()` variants.
static final class ImmutableMapOf2<K, V> {
@BeforeTemplate
Map<K, V> before(K k1, V v1, K k2, V v2) {
@@ -267,7 +270,8 @@ final class ImmutableMapRules {
* Prefer {@link ImmutableMap#of(Object, Object, Object, Object, Object, Object)} over
* alternatives that don't communicate the immutability of the resulting map at the type level.
*/
// XXX: Also rewrite the `ImmutableMap.builder()` variant?
// XXX: Consider introducing a `BugChecker` to replace these `ImmutableMapOfX` rules. That will
// also make it easier to rewrite various `ImmutableMap.builder()` variants.
static final class ImmutableMapOf3<K, V> {
@BeforeTemplate
Map<K, V> before(K k1, V v1, K k2, V v2, K k3, V v3) {
@@ -285,7 +289,9 @@ final class ImmutableMapRules {
* over alternatives that don't communicate the immutability of the resulting map at the type
* level.
*/
// XXX: Also rewrite the `ImmutableMap.builder()` variant?
// XXX: Consider introducing a `BugChecker` to replace these `ImmutableMapOfX` rules. That will
// also make it easier to rewrite various `ImmutableMap.builder()` variants.
@SuppressWarnings("java:S107" /* Can't avoid many method parameters here. */)
static final class ImmutableMapOf4<K, V> {
@BeforeTemplate
Map<K, V> before(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
@@ -303,7 +309,9 @@ final class ImmutableMapRules {
* Object, Object)} over alternatives that don't communicate the immutability of the resulting map
* at the type level.
*/
// XXX: Also rewrite the `ImmutableMap.builder()` variant?
// XXX: Consider introducing a `BugChecker` to replace these `ImmutableMapOfX` rules. That will
// also make it easier to rewrite various `ImmutableMap.builder()` variants.
@SuppressWarnings("java:S107" /* Can't avoid many method parameters here. */)
static final class ImmutableMapOf5<K, V> {
@BeforeTemplate
Map<K, V> before(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {

View File

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

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.collect.Streams;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
@@ -40,6 +41,7 @@ final class IntStreamRules {
}
@AfterTemplate
@CanIgnoreReturnValue
IntStream after(IntStream stream) {
return stream;
}
@@ -250,6 +252,7 @@ final class IntStreamRules {
/** Prefer {@link IntStream#anyMatch(IntPredicate)} over more contrived alternatives. */
static final class IntStreamAnyMatch {
@BeforeTemplate
@SuppressWarnings("java:S4034" /* This violation will be rewritten. */)
boolean before(IntStream stream, IntPredicate predicate) {
return Refaster.anyOf(
!stream.noneMatch(predicate), stream.filter(predicate).findAny().isPresent());

View File

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

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.collect.Streams;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
@@ -40,6 +41,7 @@ final class LongStreamRules {
}
@AfterTemplate
@CanIgnoreReturnValue
LongStream after(LongStream stream) {
return stream;
}
@@ -250,6 +252,7 @@ final class LongStreamRules {
/** Prefer {@link LongStream#anyMatch(LongPredicate)} over more contrived alternatives. */
static final class LongStreamAnyMatch {
@BeforeTemplate
@SuppressWarnings("java:S4034" /* This violation will be rewritten. */)
boolean before(LongStream stream, LongPredicate predicate) {
return Refaster.anyOf(
!stream.noneMatch(predicate), stream.filter(predicate).findAny().isPresent());

View File

@@ -3,10 +3,12 @@ package tech.picnic.errorprone.refasterrules;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
import com.google.common.collect.Streams;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
import com.google.errorprone.refaster.annotation.NotMatches;
import com.google.errorprone.refaster.annotation.Placeholder;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Comparator;
@@ -18,6 +20,7 @@ import java.util.function.Supplier;
import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
import tech.picnic.errorprone.refaster.matchers.IsLikelyTrivialComputation;
/** Refaster rules related to expressions dealing with {@link Optional}s. */
@OnlineDocumentation
@@ -68,7 +71,10 @@ final class OptionalRules {
/** Prefer {@link Optional#orElseThrow()} over the less explicit {@link Optional#get()}. */
static final class OptionalOrElseThrow<T> {
@BeforeTemplate
@SuppressWarnings("NullAway")
@SuppressWarnings({
"java:S3655" /* Matched expressions are in practice embedded in a larger context. */,
"NullAway"
})
T before(Optional<T> optional) {
return optional.get();
}
@@ -114,7 +120,7 @@ final class OptionalRules {
/** Prefer {@link Optional#filter(Predicate)} over usage of the ternary operator. */
// XXX: This rule may introduce a compilation error: the `test` expression may reference a
// non-effectively final variable, which is not allowed in the replacement lambda expression.
// Maybe our `Refaster` checker should test `compilesWithFix`?
// Review whether a `@Matcher` can be used to avoid this.
abstract static class TernaryOperatorOptionalPositiveFiltering<T> {
@Placeholder
abstract boolean test(T value);
@@ -134,7 +140,7 @@ final class OptionalRules {
/** Prefer {@link Optional#filter(Predicate)} over usage of the ternary operator. */
// XXX: This rule may introduce a compilation error: the `test` expression may reference a
// non-effectively final variable, which is not allowed in the replacement lambda expression.
// Maybe our `Refaster` checker should test `compilesWithFix`?
// Review whether a `@Matcher` can be used to avoid this.
abstract static class TernaryOperatorOptionalNegativeFiltering<T> {
@Placeholder
abstract boolean test(T value);
@@ -157,6 +163,7 @@ final class OptionalRules {
*/
static final class MapOptionalToBoolean<T> {
@BeforeTemplate
@SuppressWarnings("OptionalOrElseGet" /* Rule is confused by `Refaster#anyOf` usage. */)
boolean before(Optional<T> optional, Function<? super T, Boolean> predicate) {
return optional.map(predicate).orElse(Refaster.anyOf(false, Boolean.FALSE));
}
@@ -220,6 +227,28 @@ final class OptionalRules {
}
}
/**
* Prefer {@link Optional#orElseGet(Supplier)} over {@link Optional#orElse(Object)} if the
* fallback value is not the result of a trivial computation.
*/
// XXX: This rule may introduce a compilation error: the `value` expression may reference a
// non-effectively final variable, which is not allowed in the replacement lambda expression.
// Review whether a `@Matcher` can be used to avoid this.
// XXX: Once `MethodReferenceUsage` is "production ready", replace
// `@NotMatches(IsLikelyTrivialComputation.class)` with `@Matches(RequiresComputation.class)` (and
// reimplement the matcher accordingly).
static final class OptionalOrElseGet<T> {
@BeforeTemplate
T before(Optional<T> optional, @NotMatches(IsLikelyTrivialComputation.class) T value) {
return optional.orElse(value);
}
@AfterTemplate
T after(Optional<T> optional, T value) {
return optional.orElseGet(() -> value);
}
}
/**
* Flatten a stream of {@link Optional}s using {@link Optional#stream()}, rather than using one of
* the more verbose alternatives.
@@ -304,14 +333,12 @@ final class OptionalRules {
abstract Optional<S> toOptionalFunction(@MayOptionallyUse T element);
@BeforeTemplate
Optional<R> before(
Optional<T> optional, Function<? super S, ? extends Optional<? extends R>> function) {
Optional<R> before(Optional<T> optional, Function<? super S, Optional<? extends R>> function) {
return optional.flatMap(v -> toOptionalFunction(v).flatMap(function));
}
@AfterTemplate
Optional<R> after(
Optional<T> optional, Function<? super S, ? extends Optional<? extends R>> function) {
Optional<R> after(Optional<T> optional, Function<? super S, Optional<? extends R>> function) {
return optional.flatMap(v -> toOptionalFunction(v)).flatMap(function);
}
}
@@ -323,6 +350,9 @@ final class OptionalRules {
Optional<T> before(Optional<T> optional1, Optional<T> optional2) {
// XXX: Note that rewriting the first and third variant will change the code's behavior if
// `optional2` has side-effects.
// XXX: Note that rewriting the first and third variant will introduce a compilation error if
// `optional2` is not effectively final. Review whether a `@Matcher` can be used to avoid
// this.
return Refaster.anyOf(
optional1.map(Optional::of).orElse(optional2),
optional1.map(Optional::of).orElseGet(() -> optional2),
@@ -350,6 +380,7 @@ final class OptionalRules {
}
@AfterTemplate
@CanIgnoreReturnValue
Optional<T> after(Optional<T> optional) {
return optional;
}

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.MoreCollectors.toOptional;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
@@ -12,6 +13,8 @@ import static reactor.function.TupleUtils.function;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
@@ -33,6 +36,7 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collector;
import org.jspecify.annotations.Nullable;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
@@ -94,7 +98,7 @@ final class ReactorRules {
}
/** Prefer {@link Mono#justOrEmpty(Object)} over more contrived alternatives. */
static final class MonoJustOrEmpty<@Nullable T> {
static final class MonoJustOrEmptyObject<@Nullable T> {
@BeforeTemplate
Mono<T> before(T value) {
return Mono.justOrEmpty(Optional.ofNullable(value));
@@ -107,9 +111,25 @@ final class ReactorRules {
}
/** Prefer {@link Mono#justOrEmpty(Optional)} over more verbose alternatives. */
static final class MonoJustOrEmptyOptional<T> {
@BeforeTemplate
Mono<T> before(Optional<T> optional) {
return Mono.just(optional).filter(Optional::isPresent).map(Optional::orElseThrow);
}
@AfterTemplate
Mono<T> after(Optional<T> optional) {
return Mono.justOrEmpty(optional);
}
}
/**
* Prefer {@link Mono#defer(Supplier) deferring} {@link Mono#justOrEmpty(Optional)} over more
* verbose alternatives.
*/
// XXX: If `optional` is a constant and effectively-final expression then the `Mono.defer` can be
// dropped. Should look into Refaster support for identifying this.
static final class MonoFromOptional<T> {
static final class MonoDeferMonoJustOrEmpty<T> {
@BeforeTemplate
@SuppressWarnings(
"MonoFromSupplier" /* `optional` may match a checked exception-throwing expression. */)
@@ -384,12 +404,36 @@ final class ReactorRules {
return mono.then();
}
// XXX: Replace this rule with an extension of the `IdentityConversion` rule, supporting
// `Stream#map`, `Mono#map` and `Flux#map`.
@BeforeTemplate
Mono<ImmutableList<T>> before3(Mono<ImmutableList<T>> mono) {
return mono.map(ImmutableList::copyOf);
}
@AfterTemplate
@CanIgnoreReturnValue
Mono<T> after(Mono<T> mono) {
return mono;
}
}
/**
* Don't unnecessarily transform a {@link Mono} to a {@link Flux} before calling {@link
* Mono#single()}.
*/
static final class MonoSingle<T> {
@BeforeTemplate
Mono<T> before(Mono<T> mono) {
return mono.flux().single();
}
@AfterTemplate
Mono<T> after(Mono<T> mono) {
return mono.single();
}
}
/** Don't unnecessarily pass an empty publisher to {@link Flux#switchIfEmpty(Publisher)}. */
static final class FluxSwitchIfEmptyOfEmptyPublisher<T> {
@BeforeTemplate
@@ -398,6 +442,7 @@ final class ReactorRules {
}
@AfterTemplate
@CanIgnoreReturnValue
Flux<T> after(Flux<T> flux) {
return flux;
}
@@ -594,6 +639,7 @@ final class ReactorRules {
abstract S transformation(@MayOptionallyUse T value);
@BeforeTemplate
@SuppressWarnings("java:S138" /* Method is long, but not complex. */)
Publisher<S> before(Flux<T> flux, boolean delayUntilEnd, int maxConcurrency, int prefetch) {
return Refaster.anyOf(
flux.concatMap(
@@ -1150,6 +1196,40 @@ final class ReactorRules {
}
}
/**
* Prefer {@link Flux#collect(Collector)} with {@link ImmutableList#toImmutableList()} over
* alternatives that do not explicitly return an immutable collection.
*/
static final class FluxCollectToImmutableList<T> {
@BeforeTemplate
Mono<List<T>> before(Flux<T> flux) {
return flux.collectList();
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
Mono<ImmutableList<T>> after(Flux<T> flux) {
return flux.collect(toImmutableList());
}
}
/**
* Prefer {@link Flux#collect(Collector)} with {@link ImmutableSet#toImmutableSet()} over more
* contrived alternatives.
*/
static final class FluxCollectToImmutableSet<T> {
@BeforeTemplate
Mono<ImmutableSet<T>> before(Flux<T> flux) {
return flux.collect(toImmutableList()).map(ImmutableSet::copyOf);
}
@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
Mono<ImmutableSet<T>> after(Flux<T> flux) {
return flux.collect(toImmutableSet());
}
}
/** Prefer {@link reactor.util.context.Context#empty()}} over more verbose alternatives. */
// XXX: Consider introducing an `IsEmpty` matcher that identifies a wide range of guaranteed-empty
// `Collection` and `Map` expressions.
@@ -1215,6 +1295,7 @@ final class ReactorRules {
}
@AfterTemplate
@CanIgnoreReturnValue
StepVerifier.Step<T> after(StepVerifier.Step<T> step) {
return step;
}

View File

@@ -0,0 +1,435 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.MoreCollectors.toOptional;
import com.google.common.collect.MoreCollectors;
import com.google.errorprone.refaster.ImportPolicy;
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.time.Duration;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import reactor.test.publisher.PublisherProbe;
/** Refaster templates related to Reactor expressions and statements. */
final class ReactorTemplates {
private ReactorTemplates() {}
/** Prefer {@link Mono#justOrEmpty(Optional)} over more verbose alternatives. */
// XXX: If `optional` is a constant and effectively-final expression then the `Mono.defer` can be
// dropped. Should look into Refaster support for identifying this.
static final class MonoFromOptional<T> {
@BeforeTemplate
Mono<T> before(Optional<T> optional) {
return Refaster.anyOf(
Mono.fromCallable(() -> optional.orElse(null)),
Mono.fromSupplier(() -> optional.orElse(null)));
}
@AfterTemplate
Mono<T> after(Optional<T> optional) {
return Mono.defer(() -> Mono.justOrEmpty(optional));
}
}
// XXX: Fix this after knowing how to add Matches for CanBeCoercedToRunnable.
//
// abstract static class MonoFromRunnable<T> {
// @Placeholder
// abstract boolean test(); // Improve return type here and naming of method.
//
// @BeforeTemplate
// Mono<Void> before(Supplier<T> supplier) {
// return Mono.fromSupplier(() -> test()).then();
// }
//
// @AfterTemplate
// Mono<Void> after(Runnable supplier) {
// return Mono.fromRunnable(supplier);
// }
// }
/** Don't unnecessarily defer {@link Mono#error(Throwable)}. */
static final class MonoDeferredError<T> {
@BeforeTemplate
Mono<T> before(Throwable throwable) {
return Mono.defer(() -> Mono.error(throwable));
}
@AfterTemplate
Mono<T> after(Throwable throwable) {
return Mono.error(() -> throwable);
}
}
/** Don't unnecessarily defer {@link Flux#error(Throwable)}. */
static final class FluxDeferredError<T> {
@BeforeTemplate
Flux<T> before(Throwable throwable) {
return Flux.defer(() -> Flux.error(throwable));
}
@AfterTemplate
Flux<T> after(Throwable throwable) {
return Flux.error(() -> throwable);
}
}
/**
* Don't unnecessarily pass {@link Mono#error(Supplier)} a method reference or lambda expression.
*/
// XXX: Drop this rule once the more general rule `AssortedTemplates#SupplierAsSupplier` works
// reliably.
static final class MonoErrorSupplier<T, E extends Throwable> {
@BeforeTemplate
Mono<T> before(Supplier<E> supplier) {
return Mono.error(() -> supplier.get());
}
@AfterTemplate
Mono<T> after(Supplier<E> supplier) {
return Mono.error(supplier);
}
}
/**
* Don't unnecessarily pass {@link Flux#error(Supplier)} a method reference or lambda expression.
*/
// XXX: Drop this rule once the more general rule `AssortedTemplates#SupplierAsSupplier` works
// reliably.
static final class FluxErrorSupplier<T, E extends Throwable> {
@BeforeTemplate
Flux<T> before(Supplier<E> supplier) {
return Flux.error(() -> supplier.get());
}
@AfterTemplate
Flux<T> after(Supplier<E> supplier) {
return Flux.error(supplier);
}
}
/** Prefer {@link Mono#thenReturn(Object)} over more verbose alternatives. */
static final class MonoThenReturn<T, S> {
@BeforeTemplate
Mono<S> before(Mono<T> mono, S object) {
return mono.then(Mono.just(object));
}
@AfterTemplate
Mono<S> after(Mono<T> mono, S object) {
return mono.thenReturn(object);
}
}
/** Don't unnecessarily pass an empty publisher to {@link Mono#switchIfEmpty(Mono)}. */
static final class MonoSwitchIfEmptyOfEmptyPublisher<T> {
@BeforeTemplate
Mono<T> before(Mono<T> mono) {
return mono.switchIfEmpty(Mono.empty());
}
@AfterTemplate
Mono<T> after(Mono<T> mono) {
return mono;
}
}
/** Don't unnecessarily pass an empty publisher to {@link Flux#switchIfEmpty(Publisher)}. */
static final class FluxSwitchIfEmptyOfEmptyPublisher<T> {
@BeforeTemplate
Flux<T> before(Flux<T> flux) {
return flux.switchIfEmpty(Refaster.anyOf(Mono.empty(), Flux.empty()));
}
@AfterTemplate
Flux<T> after(Flux<T> flux) {
return flux;
}
}
/** Prefer {@link Flux#concatMap(Function)} over more contrived alternatives. */
static final class FluxConcatMap<T, S> {
@BeforeTemplate
Flux<S> before(Flux<T> flux, Function<? super T, ? extends Publisher<? extends S>> function) {
return Refaster.anyOf(flux.flatMap(function, 1), flux.flatMapSequential(function, 1));
}
@AfterTemplate
Flux<S> after(Flux<T> flux, Function<? super T, ? extends Publisher<? extends S>> function) {
return flux.concatMap(function);
}
}
/**
* Prefer {@link Flux#concatMapIterable(Function)} over {@link Flux#concatMapIterable(Function)},
* as the former has equivalent semantics but a clearer name.
*/
static final class FluxConcatMapIterable<T, S> {
@BeforeTemplate
Flux<S> before(Flux<T> flux, Function<? super T, ? extends Iterable<? extends S>> function) {
return flux.flatMapIterable(function);
}
@AfterTemplate
Flux<S> after(Flux<T> flux, Function<? super T, ? extends Iterable<? extends S>> function) {
return flux.concatMapIterable(function);
}
}
/** Don't unnecessarily call {@link Mono#flux()}. */
static final class MonoFlatMapIterable<T, R> {
@BeforeTemplate
Flux<R> before(Mono<T> mono, Function<? super T, ? extends Iterable<? extends R>> mapper) {
return mono.flux().concatMapIterable(mapper);
}
@AfterTemplate
Flux<R> after(Mono<T> mono, Function<? super T, ? extends Iterable<? extends R>> mapper) {
return mono.flatMapIterable(mapper);
}
}
/**
* Don't use {@link Mono#flatMapMany(Function)} to implicitly convert a {@link Mono} to a {@link
* Flux}.
*/
abstract static class MonoFlatMapToFlux<T, S> {
@Placeholder(allowsIdentity = true)
abstract Mono<S> valueTransformation(@MayOptionallyUse T value);
@BeforeTemplate
Flux<S> before(Mono<T> mono) {
return mono.flatMapMany(v -> valueTransformation(v));
}
@AfterTemplate
Flux<S> after(Mono<T> mono) {
return mono.flatMap(v -> valueTransformation(v)).flux();
}
}
/** Prefer {@link Mono#flux()}} over more contrived alternatives. */
static final class MonoFlux<T> {
@BeforeTemplate
Flux<T> before(Mono<T> mono) {
return Flux.concat(mono);
}
@AfterTemplate
Flux<T> after(Mono<T> mono) {
return mono.flux();
}
}
/** Don't unnecessarily invoke {@link Flux#concat(Publisher)}. */
static final class FluxIdentity<T> {
@BeforeTemplate
Flux<T> before(Flux<T> flux) {
return Flux.concat(flux);
}
@AfterTemplate
Flux<T> after(Flux<T> flux) {
return flux;
}
}
/**
* Prefer a collection using {@link MoreCollectors#toOptional()} over more contrived alternatives.
*/
// XXX: Consider creating a plugin which flags/discourages `Mono<Optional<T>>` method return
// types, just as we discourage nullable `Boolean`s and `Optional`s.
static final class MonoCollectToOptional<T> {
@BeforeTemplate
Mono<Optional<T>> before(Mono<T> mono) {
return Refaster.anyOf(
mono.map(Optional::of).defaultIfEmpty(Optional.empty()),
mono.map(Optional::of).switchIfEmpty(Mono.just(Optional.empty())));
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
Mono<Optional<T>> after(Mono<T> mono) {
return mono.flux().collect(toOptional());
}
}
/** Prefer {@link PublisherProbe#empty()}} over more verbose alternatives. */
static final class PublisherProbeEmpty<T> {
@BeforeTemplate
PublisherProbe<T> before() {
return Refaster.anyOf(PublisherProbe.of(Mono.empty()), PublisherProbe.of(Flux.empty()));
}
@AfterTemplate
PublisherProbe<T> after() {
return PublisherProbe.empty();
}
}
/** Prefer {@link Mono#as(Function)} when creating a {@link StepVerifier}. */
static final class StepVerifierFromMono<T> {
@BeforeTemplate
StepVerifier.FirstStep<? extends T> before(Mono<T> mono) {
return StepVerifier.create(mono);
}
@AfterTemplate
StepVerifier.FirstStep<? extends T> after(Mono<T> mono) {
return mono.as(StepVerifier::create);
}
}
/** Prefer {@link Flux#as(Function)} when creating a {@link StepVerifier}. */
static final class StepVerifierFromFlux<T> {
@BeforeTemplate
StepVerifier.FirstStep<? extends T> before(Flux<T> flux) {
return StepVerifier.create(flux);
}
@AfterTemplate
StepVerifier.FirstStep<? extends T> after(Flux<T> flux) {
return flux.as(StepVerifier::create);
}
}
/** Don't unnecessarily call {@link StepVerifier.Step#expectNext(Object[])}. */
static final class StepVerifierStepExpectNextEmpty<T> {
@BeforeTemplate
@SuppressWarnings("unchecked")
StepVerifier.Step<T> before(StepVerifier.Step<T> step) {
return step.expectNext();
}
@AfterTemplate
StepVerifier.Step<T> after(StepVerifier.Step<T> step) {
return step;
}
}
/** Prefer {@link StepVerifier.Step#expectNext(Object)} over more verbose alternatives. */
static final class StepVerifierStepExpectNext<T> {
@BeforeTemplate
StepVerifier.Step<T> before(StepVerifier.Step<T> step, T object) {
return Refaster.anyOf(
step.expectNextMatches(e -> e.equals(object)), step.expectNextMatches(object::equals));
}
@AfterTemplate
StepVerifier.Step<T> after(StepVerifier.Step<T> step, T object) {
return step.expectNext(object);
}
}
/** Prefer {@link StepVerifier.LastStep#verifyComplete()} over more verbose alternatives. */
static final class StepVerifierLastStepVerifyComplete {
@BeforeTemplate
Duration before(StepVerifier.LastStep step) {
return step.expectComplete().verify();
}
@AfterTemplate
Duration after(StepVerifier.LastStep step) {
return step.verifyComplete();
}
}
/** Prefer {@link StepVerifier.LastStep#verifyError()} over more verbose alternatives. */
static final class StepVerifierLastStepVerifyError {
@BeforeTemplate
Duration before(StepVerifier.LastStep step) {
return step.expectError().verify();
}
@AfterTemplate
Duration after(StepVerifier.LastStep step) {
return step.verifyError();
}
}
/** Prefer {@link StepVerifier.LastStep#verifyError(Class)} over more verbose alternatives. */
static final class StepVerifierLastStepVerifyErrorClass<T extends Throwable> {
@BeforeTemplate
Duration before(StepVerifier.LastStep step, Class<T> clazz) {
return step.expectError(clazz).verify();
}
@AfterTemplate
Duration after(StepVerifier.LastStep step, Class<T> clazz) {
return step.verifyError(clazz);
}
}
/**
* Prefer {@link StepVerifier.LastStep#verifyErrorMatches(Predicate)} over more verbose
* alternatives.
*/
static final class StepVerifierLastStepVerifyErrorMatches {
@BeforeTemplate
Duration before(StepVerifier.LastStep step, Predicate<Throwable> predicate) {
return step.expectErrorMatches(predicate).verify();
}
@AfterTemplate
Duration after(StepVerifier.LastStep step, Predicate<Throwable> predicate) {
return step.verifyErrorMatches(predicate);
}
}
/**
* Prefer {@link StepVerifier.LastStep#verifyErrorSatisfies(Consumer)} over more verbose
* alternatives.
*/
static final class StepVerifierLastStepVerifyErrorSatisfies {
@BeforeTemplate
Duration before(StepVerifier.LastStep step, Consumer<Throwable> consumer) {
return step.expectErrorSatisfies(consumer).verify();
}
@AfterTemplate
Duration after(StepVerifier.LastStep step, Consumer<Throwable> consumer) {
return step.verifyErrorSatisfies(consumer);
}
}
/**
* Prefer {@link StepVerifier.LastStep#verifyErrorMessage(String)} over more verbose alternatives.
*/
static final class StepVerifierLastStepVerifyErrorMessage {
@BeforeTemplate
Duration before(StepVerifier.LastStep step, String message) {
return step.expectErrorMessage(message).verify();
}
@AfterTemplate
Duration after(StepVerifier.LastStep step, String message) {
return step.verifyErrorMessage(message);
}
}
/**
* Prefer {@link StepVerifier.LastStep#verifyTimeout(Duration)} over more verbose alternatives.
*/
static final class StepVerifierLastStepVerifyTimeout {
@BeforeTemplate
Duration before(StepVerifier.LastStep step, Duration duration) {
return step.expectTimeout(duration).verify();
}
@AfterTemplate
Duration after(StepVerifier.LastStep step, Duration duration) {
return step.verifyTimeout(duration);
}
}
}

View File

@@ -20,6 +20,19 @@ import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
final class RxJava2AdapterRules {
private RxJava2AdapterRules() {}
/** Remove double conversion of ... */
static final class FluxToFlowableToFlux<T> {
@BeforeTemplate
Flux<T> before(Flux<T> flux) {
return RxJava2Adapter.flowableToFlux(RxJava2Adapter.fluxToFlowable(flux));
}
@AfterTemplate
Flux<T> after(Flux<T> flux) {
return flux;
}
}
/** Use the fluent API style when using {@link RxJava2Adapter#completableToMono}. */
static final class CompletableToMono {
@BeforeTemplate

View File

@@ -0,0 +1,525 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.Streams;
import com.google.errorprone.refaster.ImportPolicy;
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.UseImportPolicy;
import io.reactivex.Completable;
import io.reactivex.CompletableSource;
import io.reactivex.Flowable;
import io.reactivex.Maybe;
import io.reactivex.MaybeSource;
import io.reactivex.Single;
import io.reactivex.SingleSource;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Predicate;
import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.reactivestreams.Publisher;
import reactor.adapter.rxjava.RxJava2Adapter;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import tech.picnic.errorprone.migration.util.RxJavaReactorMigrationUtil;
/** The Refaster templates for the migration of the RxJava {@link Completable} to Reactor. */
final class RxJavaCompletableToReactorTemplates {
private RxJavaCompletableToReactorTemplates() {}
static final class CompletableAmb {
@BeforeTemplate
Completable before(Iterable<? extends Completable> sources) {
return Completable.amb(sources);
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.IMPORT_CLASS_DIRECTLY)
Completable after(Iterable<? extends Completable> sources) {
return RxJava2Adapter.monoToCompletable(
Mono.firstWithSignal(
Streams.stream(sources)
.map(RxJava2Adapter::completableToMono)
.collect(toImmutableList())));
}
}
// XXX: public static Completable ambArray(CompletableSource[])
static final class CompletableComplete {
@BeforeTemplate
Completable before() {
return Completable.complete();
}
@AfterTemplate
Completable after() {
return RxJava2Adapter.monoToCompletable(Mono.empty());
}
}
// XXX: public static Completable concat(Iterable)
// XXX: public static Completable concat(Publisher)
// XXX: public static Completable concat(Publisher,int)
// XXX: public static Completable concatArray(CompletableSource[])
// XXX: public static Completable create(CompletableOnSubscribe)
// XXX: The types of the @Before and @After are not matching
static final class CompletableDefer {
@BeforeTemplate
Completable before(Callable<? extends CompletableSource> supplier) {
return Completable.defer(supplier);
}
@AfterTemplate
Completable after(Callable<? extends Completable> supplier) {
return RxJava2Adapter.monoToCompletable(
Mono.defer(
() ->
RxJava2Adapter.completableToMono(
RxJavaReactorMigrationUtil.callableAsSupplier(supplier).get())));
}
}
static final class CompletableErrorCallable {
@BeforeTemplate
Completable before(Callable<? extends Throwable> throwable) {
return Completable.error(throwable);
}
@AfterTemplate
Completable after(Supplier<? extends Throwable> throwable) {
return RxJava2Adapter.monoToCompletable(Mono.error(throwable));
}
}
static final class CompletableErrorThrowable {
@BeforeTemplate
Completable before(Throwable throwable) {
return Completable.error(throwable);
}
@AfterTemplate
Completable after(Throwable throwable) {
return RxJava2Adapter.monoToCompletable(Mono.error(throwable));
}
}
static final class CompletableFromAction {
@BeforeTemplate
Completable before(Action action) {
return Completable.fromAction(action);
}
@AfterTemplate
Completable after(Action action) {
return RxJava2Adapter.monoToCompletable(
Mono.fromRunnable(RxJavaReactorMigrationUtil.toRunnable(action)));
}
}
static final class CompletableFromCallable {
@BeforeTemplate
Completable before(Callable<?> supplier) {
return Completable.fromCallable(supplier);
}
@AfterTemplate
Completable after(Callable<?> supplier) {
return RxJava2Adapter.monoToCompletable(Mono.fromCallable(supplier));
}
}
// XXX: public static Completable fromFuture(Future)
// XXX: public static Completable fromMaybe(MaybeSource)
// XXX: public static Completable fromObservable(ObservableSource)
static final class CompletableFromPublisher<T> {
@BeforeTemplate
Completable before(Publisher<T> source) {
return Completable.fromPublisher(source);
}
@AfterTemplate
Completable after(Publisher<T> source) {
return RxJava2Adapter.monoToCompletable(Mono.from(source));
}
}
static final class CompletableFromRunnable {
@BeforeTemplate
Completable before(Runnable runnable) {
return Completable.fromRunnable(runnable);
}
@AfterTemplate
Completable after(Runnable runnable) {
return RxJava2Adapter.monoToCompletable(Mono.fromRunnable(runnable));
}
}
// XXX: public static Completable fromSingle(SingleSource)
// XXX: public static Completable merge(Iterable)
// XXX: public static Completable merge(Publisher)
// XXX: public static Completable merge(Publisher,int)
// XXX: public static Completable mergeArray(CompletableSource[])
// XXX: public static Completable mergeArrayDelayError(CompletableSource[])
// XXX: public static Completable mergeDelayError(Iterable)
// XXX: public static Completable mergeDelayError(Publisher)
// XXX: public static Completable mergeDelayError(Publisher,int)
// XXX: public static Completable never()
// XXX: public static Completable timer(long,TimeUnit)
// XXX: public static Completable timer(long,TimeUnit,Scheduler)
// XXX: public static Completable unsafeCreate(CompletableSource)
// XXX: public static Completable using(Callable,Function,Consumer)
// XXX: public static Completable using(Callable,Function,Consumer,boolean)
static final class CompletableWrap {
@BeforeTemplate
Completable before(Completable source) {
return Completable.wrap(source);
}
@AfterTemplate
Completable after(Completable source) {
return source;
}
}
// XXX: public final Completable ambWith(CompletableSource)
static final class CompletableAndThenCompletable {
@BeforeTemplate
Completable before(Completable completable, CompletableSource source) {
return completable.andThen(source);
}
@AfterTemplate
Completable after(Completable completable, CompletableSource source) {
return RxJava2Adapter.monoToCompletable(
RxJava2Adapter.completableToMono(completable)
.then(RxJava2Adapter.completableToMono(Completable.wrap(source))));
}
}
static final class CompletableAndThenMaybe<T> {
@BeforeTemplate
Maybe<T> before(Completable completable, MaybeSource<T> source) {
return completable.andThen(source);
}
@AfterTemplate
Maybe<T> after(Completable completable, MaybeSource<T> source) {
return RxJava2Adapter.monoToMaybe(
RxJava2Adapter.completableToMono(completable)
.then(RxJava2Adapter.maybeToMono(Maybe.wrap(source))));
}
}
// XXX: public final Observable andThen(ObservableSource)
static final class CompletableAndThenPublisher<T> {
@BeforeTemplate
Flowable<T> before(Completable completable, Publisher<T> source) {
return completable.andThen(source);
}
@AfterTemplate
Flowable<T> after(Completable completable, Publisher<T> source) {
return RxJava2Adapter.fluxToFlowable(
RxJava2Adapter.completableToMono(completable).thenMany(source));
}
}
static final class CompletableAndThenSingle<T> {
@BeforeTemplate
Single<T> before(Completable completable, SingleSource<T> source) {
return completable.andThen(source);
}
@AfterTemplate
Single<T> after(Completable completable, SingleSource<T> source) {
return RxJava2Adapter.monoToSingle(
RxJava2Adapter.completableToMono(completable)
.then(RxJava2Adapter.singleToMono(Single.wrap(source))));
}
}
// XXX: public final Object as(CompletableConverter)
static final class CompletableBlockingAwait {
@BeforeTemplate
void before(Completable completable) {
completable.blockingAwait();
}
@AfterTemplate
void after(Completable completable) {
RxJava2Adapter.completableToMono(completable).block();
}
}
// XXX: public final boolean blockingAwait(long,TimeUnit)
// XXX: public final Throwable blockingGet()
// XXX: public final Throwable blockingGet(long,TimeUnit)
// XXX: public final Completable cache()
// XXX: public final Completable compose(CompletableTransformer)
// XXX: public final Completable concatWith(CompletableSource)
// XXX: public final Completable delay(long,TimeUnit)
// XXX: public final Completable delay(long,TimeUnit,Scheduler)
// XXX: public final Completable delay(long,TimeUnit,Scheduler,boolean)
// XXX: public final Completable delaySubscription(long,TimeUnit)
// XXX: public final Completable delaySubscription(long,TimeUnit,Scheduler)
// XXX: public final Completable doAfterTerminate(Action)
// XXX: public final Completable doFinally(Action)
// XXX: public final Completable doOnComplete(Action)
// XXX: public final Completable doOnDispose(Action)
// XXX: public final Completable doOnError(Consumer)
// XXX: public final Completable doOnEvent(Consumer)
// XXX: public final Completable doOnSubscribe(Consumer)
static final class CompletableDoOnError {
@BeforeTemplate
Completable before(Completable completable, Consumer<? super Throwable> consumer) {
return completable.doOnError(consumer);
}
@AfterTemplate
Completable after(Completable completable, Consumer<? super Throwable> consumer) {
return RxJava2Adapter.monoToCompletable(
RxJava2Adapter.completableToMono(completable)
.doOnError(RxJavaReactorMigrationUtil.toJdkConsumer(consumer)));
}
}
// XXX: public final Completable doOnTerminate(Action)
// XXX: public final Completable hide()
// XXX: public final Completable lift(CompletableOperator)
// XXX: public final Single materialize()
// XXX: public final Completable mergeWith(CompletableSource)
// XXX: public final Completable observeOn(Scheduler)
// XXX: Verify whether this is the correct equivalent.
static final class CompletableOnErrorComplete {
@BeforeTemplate
Completable before(Completable completable) {
return completable.onErrorComplete();
}
@AfterTemplate
Completable after(Completable completable) {
return RxJava2Adapter.monoToCompletable(
RxJava2Adapter.completableToMono(completable).onErrorStop());
}
}
static final class CompletableOnErrorCompletePredicate {
@BeforeTemplate
Completable before(Completable completable, Predicate<? super Throwable> predicate) {
return completable.onErrorComplete(predicate);
}
@AfterTemplate
Completable after(Completable completable, Predicate<? super Throwable> predicate) {
return RxJava2Adapter.monoToCompletable(
RxJava2Adapter.completableToMono(completable)
.onErrorResume(
RxJavaReactorMigrationUtil.toJdkPredicate(predicate), t -> Mono.empty()));
}
}
// XXX: public final Completable onErrorComplete(Predicate)
// XXX: public final Completable onErrorResumeNext(Function)
// XXX: public final Completable onTerminateDetach()
// XXX: public final Completable repeat()
// XXX: public final Completable repeat(long)
// XXX: public final Completable repeatUntil(BooleanSupplier)
// XXX: public final Completable repeatWhen(Function)
// XXX: public final Completable retry()
// XXX: public final Completable retry(BiPredicate)
// XXX: public final Completable retry(long)
// XXX: public final Completable retry(long,Predicate)
// XXX: public final Completable retry(Predicate)
// XXX: public final Completable retryWhen(Function)
// XXX: public final Completable startWith(CompletableSource)
// XXX: public final Observable startWith(Observable)
// XXX: public final Flowable startWith(Publisher)
// XXX: public final Disposable subscribe()
// XXX: public final Disposable subscribe(Action)
// XXX: public final Disposable subscribe(Action,Consumer)
// XXX: public final void subscribe(CompletableObserver)
// XXX: public final Completable subscribeOn(Scheduler)
// XXX: public final CompletableObserver subscribeWith(CompletableObserver)
// XXX: public final Completable takeUntil(CompletableSource)
static final class CompletableTimeoutLongTimeUnit {
@BeforeTemplate
Completable before(Completable completable, long timeout, TimeUnit unit) {
return completable.timeout(timeout, unit);
}
@AfterTemplate
Completable after(Completable completable, long timeout, TimeUnit unit) {
return RxJava2Adapter.monoToCompletable(
RxJava2Adapter.completableToMono(completable)
.timeout(Duration.of(timeout, unit.toChronoUnit())));
}
}
// XXX: public final Completable timeout(long,TimeUnit,CompletableSource)
// XXX: public final Completable timeout(long,TimeUnit,Scheduler)
// XXX: public final Completable timeout(long,TimeUnit,Scheduler,CompletableSource)
// XXX: public final Object to(Function)
static final class CompletableToFlowable {
@BeforeTemplate
Flowable<Void> before(Completable completable) {
return completable.toFlowable();
}
@AfterTemplate
Flowable<Void> after(Completable completable) {
return RxJava2Adapter.fluxToFlowable(RxJava2Adapter.completableToMono(completable).flux());
}
}
// XXX: Requires investigation. Should not be Void...
static final class CompletableToMaybe {
@BeforeTemplate
Maybe<Void> before(Completable completable) {
return completable.toMaybe();
}
@AfterTemplate
Maybe<Void> after(Completable completable) {
return RxJava2Adapter.monoToMaybe(RxJava2Adapter.completableToMono(completable));
}
}
// XXX: public final Observable toObservable()
// XXX: public final Single toSingle(Callable)
// XXX: public final Single toSingleDefault(Object)
// XXX: public final Completable unsubscribeOn(Scheduler)
static final class CompletableTestAssertResult {
@BeforeTemplate
void before(Completable completable) throws InterruptedException {
Refaster.anyOf(
completable.test().await().assertResult(),
completable.test().assertResult(),
completable.test().await());
}
@AfterTemplate
void after(Completable completable) {
RxJava2Adapter.completableToMono(completable).as(StepVerifier::create).verifyComplete();
}
}
static final class CompletableTestAssertComplete {
@BeforeTemplate
void before(Completable completable) throws InterruptedException {
Refaster.anyOf(
completable.test().await().assertComplete(), completable.test().assertComplete());
}
@AfterTemplate
void after(Completable completable) {
RxJava2Adapter.completableToMono(completable).as(StepVerifier::create).verifyComplete();
}
}
static final class CompletableTestAssertErrorClass {
@BeforeTemplate
void before(Completable completable, Class<? extends Throwable> errorClass)
throws InterruptedException {
Refaster.anyOf(
completable.test().await().assertError(errorClass),
completable.test().assertError(errorClass));
}
@AfterTemplate
void after(Completable completable, Class<? extends Throwable> errorClass) {
RxJava2Adapter.completableToMono(completable)
.as(StepVerifier::create)
.verifyError(errorClass);
}
}
static final class CompletableTestAssertNoErrors {
@BeforeTemplate
void before(Completable completable) throws InterruptedException {
completable.test().await().assertNoErrors();
}
@AfterTemplate
void after(Completable completable) {
RxJava2Adapter.completableToMono(completable).as(StepVerifier::create).verifyComplete();
}
}
static final class CompletableTestAssertValueCount {
@BeforeTemplate
void before(Completable completable, int count) throws InterruptedException {
completable.test().await().assertValueCount(count);
}
@AfterTemplate
void after(Completable completable, int count) {
RxJava2Adapter.completableToMono(completable)
.as(StepVerifier::create)
.expectNextCount(count)
.verifyComplete();
}
}
static final class CompletableTestAssertFailure {
@BeforeTemplate
void before(Completable completable, Class<? extends Throwable> error)
throws InterruptedException {
completable.test().await().assertFailure(error);
}
@AfterTemplate
void after(Completable completable, Class<? extends Throwable> error) {
RxJava2Adapter.completableToMono(completable).as(StepVerifier::create).verifyError(error);
}
}
static final class CompletableTestAssertNoValues {
@BeforeTemplate
void before(Completable completable) throws InterruptedException {
completable.test().await().assertNoValues();
}
@AfterTemplate
void after(Completable completable) {
RxJava2Adapter.completableToMono(completable).as(StepVerifier::create).verifyComplete();
}
}
static final class CompletableTestAssertFailureAndMessage {
@BeforeTemplate
void before(Completable completable, Class<? extends Throwable> error, String message)
throws InterruptedException {
completable.test().await().assertFailureAndMessage(error, message);
}
@AfterTemplate
void after(Completable completable, Class<? extends Throwable> error, String message) {
RxJava2Adapter.completableToMono(completable)
.as(StepVerifier::create)
.expectErrorSatisfies(
t -> assertThat(t).isInstanceOf(error).hasMessageContaining(message))
.verify();
}
}
// XXX: public final TestObserver test(boolean)
}

View File

@@ -0,0 +1,944 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.Streams;
import com.google.errorprone.refaster.ImportPolicy;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.Repeated;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Completable;
import io.reactivex.Flowable;
import io.reactivex.Maybe;
import io.reactivex.MaybeSource;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.functions.Function;
import io.reactivex.functions.Predicate;
import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.reactivestreams.Publisher;
import reactor.adapter.rxjava.RxJava2Adapter;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import tech.picnic.errorprone.migration.util.RxJavaReactorMigrationUtil;
/** The Refaster templates for the migration of the RxJava {@link Observable} to Reactor. */
final class RxJavaObservableToReactorTemplates {
private RxJavaObservableToReactorTemplates() {}
static final class ObservableAmb<T> {
@BeforeTemplate
Observable<T> before(Iterable<? extends Observable<T>> sources) {
return Observable.amb(sources);
}
@AfterTemplate
Observable<T> after(Iterable<? extends Observable<T>> sources) {
return RxJava2Adapter.fluxToObservable(
Flux.<T>firstWithSignal(
Streams.stream(sources)
.map(e -> e.toFlowable(BackpressureStrategy.BUFFER))
.map(RxJava2Adapter::flowableToFlux)
.collect(toImmutableList())));
}
}
// XXX: public static Observable ambArray(ObservableSource[])
// XXX: public static int bufferSize()
// XXX: public static Observable combineLatest(Function,int,ObservableSource[])
// XXX: public static Observable combineLatest(Iterable,Function)
// XXX: public static Observable combineLatest(Iterable,Function,int)
// XXX: public static Observable combineLatest(ObservableSource[],Function)
// XXX: public static Observable combineLatest(ObservableSource[],Function,int)
// XXX: public static Observable combineLatest(ObservableSource,ObservableSource,BiFunction)
// XXX: public static Observable
// combineLatest(ObservableSource,ObservableSource,ObservableSource,Function3)
// XXX: public static Observable
// combineLatest(ObservableSource,ObservableSource,ObservableSource,ObservableSource,Function4)
// XXX: public static Observable
// combineLatest(ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,Function5)
// XXX: public static Observable
// combineLatest(ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,Function6)
// XXX: public static Observable
// combineLatest(ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,Function7)
// XXX: public static Observable
// combineLatest(ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,Function8)
// XXX: public static Observable
// combineLatest(ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,Function9)
// XXX: public static Observable combineLatestDelayError(Function,int,ObservableSource[])
// XXX: public static Observable combineLatestDelayError(Iterable,Function)
// XXX: public static Observable combineLatestDelayError(Iterable,Function,int)
// XXX: public static Observable combineLatestDelayError(ObservableSource[],Function)
// XXX: public static Observable combineLatestDelayError(ObservableSource[],Function,int)
// XXX: public static Observable concat(Iterable)
// XXX: public static Observable concat(ObservableSource)
// XXX: public static Observable concat(ObservableSource,int)
// XXX: public static Observable concat(ObservableSource,ObservableSource)
// XXX: public static Observable concat(ObservableSource,ObservableSource,ObservableSource)
// XXX: public static Observable
// concat(ObservableSource,ObservableSource,ObservableSource,ObservableSource)
// XXX: public static Observable concatArray(ObservableSource[])
// XXX: public static Observable concatArrayDelayError(ObservableSource[])
// XXX: public static Observable concatArrayEager(int,int,ObservableSource[])
// XXX: public static Observable concatArrayEager(ObservableSource[])
// XXX: public static Observable concatArrayEagerDelayError(int,int,ObservableSource[])
// XXX: public static Observable concatArrayEagerDelayError(ObservableSource[])
// XXX: public static Observable concatDelayError(Iterable)
// XXX: public static Observable concatDelayError(ObservableSource)
// XXX: public static Observable concatDelayError(ObservableSource,int,boolean)
// XXX: public static Observable concatEager(Iterable)
// XXX: public static Observable concatEager(Iterable,int,int)
// XXX: public static Observable concatEager(ObservableSource)
// XXX: public static Observable concatEager(ObservableSource,int,int)
// XXX: public static Observable create(ObservableOnSubscribe)
// XXX: public static Observable defer(Callable)
static final class ObservableEmpty<T> {
@BeforeTemplate
Observable<T> before() {
return Observable.empty();
}
@AfterTemplate
Observable<T> after() {
return RxJava2Adapter.fluxToObservable(Flux.empty());
}
}
// XXX: public static Observable error(Callable)
// XXX: public static Observable error(Throwable)
// XXX: public static Observable fromArray(Object[])
static final class ObservableFromCallable<T> {
@BeforeTemplate
Observable<? extends T> before(Callable<? extends T> callable) {
return Observable.fromCallable(callable);
}
@AfterTemplate
Observable<? extends T> after(Callable<? extends T> callable) {
return RxJava2Adapter.fluxToObservable(
Mono.fromSupplier(RxJavaReactorMigrationUtil.callableAsSupplier(callable)).flux());
}
}
// XXX: public static Observable fromFuture(Future)
// XXX: public static Observable fromFuture(Future,long,TimeUnit)
// XXX: public static Observable fromFuture(Future,long,TimeUnit,Scheduler)
// XXX: public static Observable fromFuture(Future,Scheduler)
// XXX: public static Observable fromIterable(Iterable)
static final class ObservableFromPublisher<T> {
@BeforeTemplate
Observable<T> before(Publisher<? extends T> source) {
return Observable.fromPublisher(source);
}
@AfterTemplate
Observable<T> after(Publisher<? extends T> source) {
return RxJava2Adapter.fluxToObservable(Flux.from(source));
}
}
// XXX: public static Observable generate(Callable,BiConsumer)
// XXX: public static Observable generate(Callable,BiConsumer,Consumer)
// XXX: public static Observable generate(Callable,BiFunction)
// XXX: public static Observable generate(Callable,BiFunction,Consumer)
// XXX: public static Observable generate(Consumer)
// XXX: public static Observable interval(long,long,TimeUnit)
// XXX: public static Observable interval(long,long,TimeUnit,Scheduler)
// XXX: public static Observable interval(long,TimeUnit)
// XXX: public static Observable interval(long,TimeUnit,Scheduler)
// XXX: public static Observable intervalRange(long,long,long,long,TimeUnit)
// XXX: public static Observable intervalRange(long,long,long,long,TimeUnit,Scheduler)
static final class ObservableJust<T> {
@BeforeTemplate
Observable<T> before(T t) {
return Observable.just(t);
}
@AfterTemplate
Observable<T> after(T t) {
return RxJava2Adapter.fluxToObservable(Flux.just(t));
}
}
static final class ObservableJustTwo<T> {
@BeforeTemplate
Observable<T> before(T t, T t2) {
return Observable.just(t, t2);
}
@AfterTemplate
Observable<T> after(T t, T t2) {
return RxJava2Adapter.fluxToObservable(Flux.just(t, t2));
}
}
static final class ObservableJustThree<T> {
@BeforeTemplate
Observable<T> before(T t, T t2, T t3) {
return Observable.just(t, t2, t3);
}
@AfterTemplate
Observable<T> after(T t, T t2, T t3) {
return RxJava2Adapter.fluxToObservable(Flux.just(t, t2, t3));
}
}
// XXX: public static Observable just(Object,Object,Object,Object)
// XXX: public static Observable just(Object,Object,Object,Object,Object)
// XXX: public static Observable just(Object,Object,Object,Object,Object,Object)
// XXX: public static Observable just(Object,Object,Object,Object,Object,Object,Object)
// XXX: public static Observable just(Object,Object,Object,Object,Object,Object,Object,Object)
// XXX: public static Observable
// just(Object,Object,Object,Object,Object,Object,Object,Object,Object)
// XXX: public static Observable
// just(Object,Object,Object,Object,Object,Object,Object,Object,Object,Object)
// XXX: public static Observable merge(Iterable)
// XXX: public static Observable merge(Iterable,int)
// XXX: public static Observable merge(Iterable,int,int)
// XXX: public static Observable merge(ObservableSource)
// XXX: public static Observable merge(ObservableSource,int)
// XXX: public static Observable merge(ObservableSource,ObservableSource)
// XXX: public static Observable merge(ObservableSource,ObservableSource,ObservableSource)
// XXX: public static Observable
// merge(ObservableSource,ObservableSource,ObservableSource,ObservableSource)
// XXX: public static Observable mergeArray(int,int,ObservableSource[])
// XXX: public static Observable mergeArray(ObservableSource[])
// XXX: public static Observable mergeArrayDelayError(int,int,ObservableSource[])
// XXX: public static Observable mergeArrayDelayError(ObservableSource[])
// XXX: public static Observable mergeDelayError(Iterable)
// XXX: public static Observable mergeDelayError(Iterable,int)
// XXX: public static Observable mergeDelayError(Iterable,int,int)
// XXX: public static Observable mergeDelayError(ObservableSource)
// XXX: public static Observable mergeDelayError(ObservableSource,int)
// XXX: public static Observable mergeDelayError(ObservableSource,ObservableSource)
// XXX: public static Observable
// mergeDelayError(ObservableSource,ObservableSource,ObservableSource)
// XXX: public static Observable
// mergeDelayError(ObservableSource,ObservableSource,ObservableSource,ObservableSource)
// XXX: public static Observable never()
// XXX: public static Observable range(int,int)
// XXX: public static Observable rangeLong(long,long)
// XXX: public static Single sequenceEqual(ObservableSource,ObservableSource)
// XXX: public static Single sequenceEqual(ObservableSource,ObservableSource,BiPredicate)
// XXX: public static Single sequenceEqual(ObservableSource,ObservableSource,BiPredicate,int)
// XXX: public static Single sequenceEqual(ObservableSource,ObservableSource,int)
// XXX: public static Observable switchOnNext(ObservableSource)
// XXX: public static Observable switchOnNext(ObservableSource,int)
// XXX: public static Observable switchOnNextDelayError(ObservableSource)
// XXX: public static Observable switchOnNextDelayError(ObservableSource,int)
// XXX: public static Observable timer(long,TimeUnit)
// XXX: public static Observable timer(long,TimeUnit,Scheduler)
// XXX: public static Observable unsafeCreate(ObservableSource)
// XXX: public static Observable using(Callable,Function,Consumer)
// XXX: public static Observable using(Callable,Function,Consumer,boolean)
// XXX: public static Observable wrap(ObservableSource)
// XXX: public static Observable zip(Iterable,Function)
// XXX: public static Observable zip(ObservableSource,Function)
// XXX: public static Observable zip(ObservableSource,ObservableSource,BiFunction)
// XXX: public static Observable zip(ObservableSource,ObservableSource,BiFunction,boolean)
// XXX: public static Observable zip(ObservableSource,ObservableSource,BiFunction,boolean,int)
// XXX: public static Observable zip(ObservableSource,ObservableSource,ObservableSource,Function3)
// XXX: public static Observable
// zip(ObservableSource,ObservableSource,ObservableSource,ObservableSource,Function4)
// XXX: public static Observable
// zip(ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,Function5)
// XXX: public static Observable
// zip(ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,Function6)
// XXX: public static Observable
// zip(ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,Function7)
// XXX: public static Observable
// zip(ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,Function8)
// XXX: public static Observable
// zip(ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,ObservableSource,Function9)
// XXX: public static Observable zipArray(Function,boolean,int,ObservableSource[])
// XXX: public static Observable zipIterable(Iterable,Function,boolean,int)
// XXX: public final Single all(Predicate)
// XXX: public final Observable ambWith(ObservableSource)
// XXX: public final Single any(Predicate)
// XXX: public final Object as(ObservableConverter)
// XXX: public final Object blockingFirst()
// XXX: public final Object blockingFirst(Object)
// XXX: public final void blockingForEach(Consumer)
// XXX: public final Iterable blockingIterable()
// XXX: public final Iterable blockingIterable(int)
// XXX: public final Object blockingLast()
// XXX: public final Object blockingLast(Object)
// XXX: public final Iterable blockingLatest()
// XXX: public final Iterable blockingMostRecent(Object)
// XXX: public final Iterable blockingNext()
// XXX: public final Object blockingSingle()
// XXX: public final Object blockingSingle(Object)
// XXX: public final void blockingSubscribe()
// XXX: public final void blockingSubscribe(Consumer)
// XXX: public final void blockingSubscribe(Consumer,Consumer)
// XXX: public final void blockingSubscribe(Consumer,Consumer,Action)
// XXX: public final void blockingSubscribe(Observer)
// XXX: public final Observable buffer(Callable)
// XXX: public final Observable buffer(Callable,Callable)
// XXX: public final Observable buffer(int)
// XXX: public final Observable buffer(int,Callable)
// XXX: public final Observable buffer(int,int)
// XXX: public final Observable buffer(int,int,Callable)
// XXX: public final Observable buffer(long,long,TimeUnit)
// XXX: public final Observable buffer(long,long,TimeUnit,Scheduler)
// XXX: public final Observable buffer(long,long,TimeUnit,Scheduler,Callable)
// XXX: public final Observable buffer(long,TimeUnit)
// XXX: public final Observable buffer(long,TimeUnit,int)
// XXX: public final Observable buffer(long,TimeUnit,Scheduler)
// XXX: public final Observable buffer(long,TimeUnit,Scheduler,int)
// XXX: public final Observable buffer(long,TimeUnit,Scheduler,int,Callable,boolean)
// XXX: public final Observable buffer(ObservableSource)
// XXX: public final Observable buffer(ObservableSource,Callable)
// XXX: public final Observable buffer(ObservableSource,Function)
// XXX: public final Observable buffer(ObservableSource,Function,Callable)
// XXX: public final Observable buffer(ObservableSource,int)
// XXX: public final Observable cache()
// XXX: public final Observable cacheWithInitialCapacity(int)
// XXX: public final Observable cast(Class)
// XXX: public final Single collect(Callable,BiConsumer)
// XXX: public final Single collectInto(Object,BiConsumer)
// XXX: public final Observable compose(ObservableTransformer)
// XXX: public final Observable concatMap(Function)
// XXX: public final Observable concatMap(Function,int)
// XXX: public final Completable concatMapCompletable(Function)
// XXX: public final Completable concatMapCompletable(Function,int)
// XXX: public final Completable concatMapCompletableDelayError(Function)
// XXX: public final Completable concatMapCompletableDelayError(Function,boolean)
// XXX: public final Completable concatMapCompletableDelayError(Function,boolean,int)
// XXX: public final Observable concatMapDelayError(Function)
// XXX: public final Observable concatMapDelayError(Function,int,boolean)
// XXX: public final Observable concatMapEager(Function)
// XXX: public final Observable concatMapEager(Function,int,int)
// XXX: public final Observable concatMapEagerDelayError(Function,boolean)
// XXX: public final Observable concatMapEagerDelayError(Function,int,int,boolean)
// XXX: public final Observable concatMapIterable(Function)
// XXX: public final Observable concatMapIterable(Function,int)
// XXX: public final Observable concatMapMaybe(Function)
// XXX: public final Observable concatMapMaybe(Function,int)
// XXX: public final Observable concatMapMaybeDelayError(Function)
// XXX: public final Observable concatMapMaybeDelayError(Function,boolean)
// XXX: public final Observable concatMapMaybeDelayError(Function,boolean,int)
// XXX: public final Observable concatMapSingle(Function)
// XXX: public final Observable concatMapSingle(Function,int)
// XXX: public final Observable concatMapSingleDelayError(Function)
// XXX: public final Observable concatMapSingleDelayError(Function,boolean)
// XXX: public final Observable concatMapSingleDelayError(Function,boolean,int)
// XXX: public final Observable concatWith(CompletableSource)
// XXX: public final Observable concatWith(MaybeSource)
// XXX: public final Observable concatWith(ObservableSource)
// XXX: public final Observable concatWith(SingleSource)
// XXX: public final Single contains(Object)
// XXX: public final Single count()
// XXX: public final Observable debounce(Function)
// XXX: public final Observable debounce(long,TimeUnit)
// XXX: public final Observable debounce(long,TimeUnit,Scheduler)
// XXX: public final Observable defaultIfEmpty(Object)
// XXX: public final Observable delay(Function)
// XXX: public final Observable delay(long,TimeUnit)
// XXX: public final Observable delay(long,TimeUnit,boolean)
// XXX: public final Observable delay(long,TimeUnit,Scheduler)
// XXX: public final Observable delay(long,TimeUnit,Scheduler,boolean)
// XXX: public final Observable delay(ObservableSource,Function)
// XXX: public final Observable delaySubscription(long,TimeUnit)
// XXX: public final Observable delaySubscription(long,TimeUnit,Scheduler)
// XXX: public final Observable delaySubscription(ObservableSource)
// XXX: public final Observable dematerialize()
// XXX: public final Observable dematerialize(Function)
// XXX: public final Observable distinct()
// XXX: public final Observable distinct(Function)
// XXX: public final Observable distinct(Function,Callable)
// XXX: public final Observable distinctUntilChanged()
// XXX: public final Observable distinctUntilChanged(BiPredicate)
// XXX: public final Observable distinctUntilChanged(Function)
// XXX: public final Observable doAfterNext(Consumer)
// XXX: public final Observable doAfterTerminate(Action)
// XXX: public final Observable doFinally(Action)
// XXX: public final Observable doOnComplete(Action)
// XXX: public final Observable doOnDispose(Action)
// XXX: public final Observable doOnEach(Consumer)
// XXX: public final Observable doOnEach(Observer)
// XXX: public final Observable doOnError(Consumer)
// XXX: public final Observable doOnLifecycle(Consumer,Action)
// XXX: public final Observable doOnNext(Consumer)
// XXX: public final Observable doOnSubscribe(Consumer)
// XXX: public final Observable doOnTerminate(Action)
// XXX: public final Maybe elementAt(long)
// XXX: public final Single elementAt(long,Object)
// XXX: public final Single elementAtOrError(long)
// XXX: Default BackPressureStrategy.BUFFER is set.
static final class ObservableFilter<T> {
@BeforeTemplate
Observable<T> before(Observable<T> observable, Predicate<T> predicate) {
return observable.filter(predicate);
}
@AfterTemplate
Observable<T> after(Observable<T> observable, Predicate<T> predicate) {
return RxJava2Adapter.fluxToObservable(
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.filter(RxJavaReactorMigrationUtil.toJdkPredicate(predicate)));
}
}
// XXX: public final Single first(Object)
// XXX: Default BUFFER is chosen here.
static final class MaybeFirstElement<T> {
@BeforeTemplate
Maybe<T> before(Observable<T> observable) {
return observable.firstElement();
}
@AfterTemplate
Maybe<T> after(Observable<T> observable) {
return RxJava2Adapter.monoToMaybe(
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER).next());
}
}
// XXX: public final Single firstOrError()
// XXX: public final Observable flatMap(Function)
// XXX: Add test
// XXX: Default BUFFER is set here.
static final class ObservableFlatMap<I, T extends I, O, P extends ObservableSource<O>> {
@BeforeTemplate
Observable<O> before(
Observable<T> observable,
Function<? super T, ? extends ObservableSource<? extends O>> function) {
return observable.flatMap(function);
}
@UseImportPolicy(ImportPolicy.IMPORT_CLASS_DIRECTLY)
@AfterTemplate
Observable<O> after(Observable<T> observable, Function<I, P> function) {
return RxJava2Adapter.fluxToObservable(
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.flatMap(
z ->
RxJava2Adapter.observableToFlux(
Observable.wrap(
RxJavaReactorMigrationUtil.<I, P>toJdkFunction(function).apply(z)),
BackpressureStrategy.BUFFER)));
}
}
// XXX: public final Observable flatMap(Function,BiFunction)
// XXX: public final Observable flatMap(Function,BiFunction,boolean)
// XXX: public final Observable flatMap(Function,BiFunction,boolean,int)
// XXX: public final Observable flatMap(Function,BiFunction,boolean,int,int)
// XXX: public final Observable flatMap(Function,BiFunction,int)
// XXX: public final Observable flatMap(Function,boolean)
// XXX: public final Observable flatMap(Function,boolean,int)
// XXX: public final Observable flatMap(Function,boolean,int,int)
// XXX: public final Observable flatMap(Function,Function,Callable)
// XXX: public final Observable flatMap(Function,Function,Callable,int)
// XXX: public final Observable flatMap(Function,int)
// XXX: public final Completable flatMapCompletable(Function)
// XXX: public final Completable flatMapCompletable(Function,boolean)
static final class ObservableFromIterable<T> {
@BeforeTemplate
Observable<T> before(Iterable<? extends T> iterable) {
return Observable.fromIterable(iterable);
}
@AfterTemplate
Observable<T> after(Iterable<? extends T> iterable) {
return RxJava2Adapter.fluxToObservable(Flux.fromIterable(iterable));
}
}
// XXX: public final Observable flatMapIterable(Function,BiFunction)
static final class ObservableFlatMapMaybe<T, R, O extends R, M extends MaybeSource<O>> {
Observable<O> before(
Observable<T> observable, Function<? super T, ? extends MaybeSource<? extends O>> mapper) {
return observable.flatMapMaybe(mapper);
}
Observable<O> after(Observable<T> observable, Function<T, M> mapper) {
return RxJava2Adapter.fluxToObservable(
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.flatMap(
t ->
RxJava2Adapter.maybeToMono(
Maybe.wrap(
RxJavaReactorMigrationUtil.<T, M>toJdkFunction(mapper).apply(t)))));
}
} // XXX: public final Observable flatMapMaybe(Function,boolean)
// XXX: public final Observable flatMapSingle(Function)
// XXX: public final Observable flatMapSingle(Function,boolean)
// XXX: public final Disposable forEach(Consumer)
// XXX: public final Disposable forEachWhile(Predicate)
// XXX: public final Disposable forEachWhile(Predicate,Consumer)
// XXX: public final Disposable forEachWhile(Predicate,Consumer,Action)
// XXX: public final Observable groupBy(Function)
// XXX: public final Observable groupBy(Function,boolean)
// XXX: public final Observable groupBy(Function,Function)
// XXX: public final Observable groupBy(Function,Function,boolean)
// XXX: public final Observable groupBy(Function,Function,boolean,int)
// XXX: public final Observable groupJoin(ObservableSource,Function,Function,BiFunction)
// XXX: public final Observable hide()
static final class ObservableIgnoreElements<T> {
@BeforeTemplate
Completable before(Observable<T> observable) {
return observable.ignoreElements();
}
@AfterTemplate
Completable after(Observable<T> observable) {
return RxJava2Adapter.monoToCompletable(
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.ignoreElements()
.then());
}
}
// XXX: public final Single isEmpty()
// XXX: public final Observable join(ObservableSource,Function,Function,BiFunction)
// XXX: public final Single last(Object)
// XXX: public final Maybe lastElement()
// XXX: public final Single lastOrError()
// XXX: public final Observable lift(ObservableOperator)
// XXX: public final Observable map(Function)
// XXX: public final Observable materialize()
// XXX: public final Observable mergeWith(CompletableSource)
// XXX: public final Observable mergeWith(MaybeSource)
// XXX: public final Observable mergeWith(ObservableSource)
// XXX: public final Observable mergeWith(SingleSource)
// XXX: public final Observable observeOn(Scheduler)
// XXX: public final Observable observeOn(Scheduler,boolean)
// XXX: public final Observable observeOn(Scheduler,boolean,int)
// XXX: public final Observable ofType(Class)
// XXX: public final Observable onErrorResumeNext(Function)
// XXX: public final Observable onErrorResumeNext(ObservableSource)
// XXX: public final Observable onErrorReturn(Function)
// XXX: public final Observable onErrorReturnItem(Object)
// XXX: public final Observable onExceptionResumeNext(ObservableSource)
// XXX: public final Observable onTerminateDetach()
// XXX: public final ConnectableObservable publish()
// XXX: public final Observable publish(Function)
// XXX: public final Maybe reduce(BiFunction)
// XXX: public final Single reduce(Object,BiFunction)
// XXX: public final Single reduceWith(Callable,BiFunction)
// XXX: public final Observable repeat()
// XXX: public final Observable repeat(long)
// XXX: public final Observable repeatUntil(BooleanSupplier)
// XXX: public final Observable repeatWhen(Function)
// XXX: public final ConnectableObservable replay()
// XXX: public final Observable replay(Function)
// XXX: public final Observable replay(Function,int)
// XXX: public final Observable replay(Function,int,long,TimeUnit)
// XXX: public final Observable replay(Function,int,long,TimeUnit,Scheduler)
// XXX: public final Observable replay(Function,int,Scheduler)
// XXX: public final Observable replay(Function,long,TimeUnit)
// XXX: public final Observable replay(Function,long,TimeUnit,Scheduler)
// XXX: public final Observable replay(Function,Scheduler)
// XXX: public final ConnectableObservable replay(int)
// XXX: public final ConnectableObservable replay(int,long,TimeUnit)
// XXX: public final ConnectableObservable replay(int,long,TimeUnit,Scheduler)
// XXX: public final ConnectableObservable replay(int,Scheduler)
// XXX: public final ConnectableObservable replay(long,TimeUnit)
// XXX: public final ConnectableObservable replay(long,TimeUnit,Scheduler)
// XXX: public final ConnectableObservable replay(Scheduler)
// XXX: public final Observable retry()
// XXX: public final Observable retry(BiPredicate)
// XXX: public final Observable retry(long)
// XXX: public final Observable retry(long,Predicate)
// XXX: public final Observable retry(Predicate)
// XXX: public final Observable retryUntil(BooleanSupplier)
// XXX: public final Observable retryWhen(Function)
// XXX: public final void safeSubscribe(Observer)
// XXX: public final Observable sample(long,TimeUnit)
// XXX: public final Observable sample(long,TimeUnit,boolean)
// XXX: public final Observable sample(long,TimeUnit,Scheduler)
// XXX: public final Observable sample(long,TimeUnit,Scheduler,boolean)
// XXX: public final Observable sample(ObservableSource)
// XXX: public final Observable sample(ObservableSource,boolean)
// XXX: public final Observable scan(BiFunction)
// XXX: public final Observable scan(Object,BiFunction)
// XXX: public final Observable scanWith(Callable,BiFunction)
// XXX: public final Observable serialize()
// XXX: public final Observable share()
// XXX: public final Single single(Object)
// XXX: public final Maybe singleElement()
// XXX: public final Single singleOrError()
// XXX: public final Observable skip(long)
// XXX: public final Observable skip(long,TimeUnit)
// XXX: public final Observable skip(long,TimeUnit,Scheduler)
// XXX: public final Observable skipLast(int)
// XXX: public final Observable skipLast(long,TimeUnit)
// XXX: public final Observable skipLast(long,TimeUnit,boolean)
// XXX: public final Observable skipLast(long,TimeUnit,Scheduler)
// XXX: public final Observable skipLast(long,TimeUnit,Scheduler,boolean)
// XXX: public final Observable skipLast(long,TimeUnit,Scheduler,boolean,int)
// XXX: public final Observable skipUntil(ObservableSource)
// XXX: public final Observable skipWhile(Predicate)
// XXX: public final Observable sorted()
// XXX: public final Observable sorted(Comparator)
// XXX: public final Observable startWith(Iterable)
// XXX: public final Observable startWith(Object)
// XXX: public final Observable startWith(ObservableSource)
// XXX: public final Observable startWithArray(Object[])
// XXX: public final Disposable subscribe()
// XXX: public final Disposable subscribe(Consumer)
// XXX: public final Disposable subscribe(Consumer,Consumer)
// XXX: public final Disposable subscribe(Consumer,Consumer,Action)
// XXX: public final Disposable subscribe(Consumer,Consumer,Action,Consumer)
// XXX: public final void subscribe(Observer)
// XXX: public final Observable subscribeOn(Scheduler)
// XXX: public final Observer subscribeWith(Observer)
// XXX: public final Observable switchIfEmpty(ObservableSource)
// XXX: public final Observable switchMap(Function)
// XXX: public final Observable switchMap(Function,int)
// XXX: public final Completable switchMapCompletable(Function)
// XXX: public final Completable switchMapCompletableDelayError(Function)
// XXX: public final Observable switchMapDelayError(Function)
// XXX: public final Observable switchMapDelayError(Function,int)
// XXX: public final Observable switchMapMaybe(Function)
// XXX: public final Observable switchMapMaybeDelayError(Function)
// XXX: public final Observable switchMapSingle(Function)
// XXX: public final Observable switchMapSingleDelayError(Function)
// XXX: public final Observable take(long)
// XXX: public final Observable take(long,TimeUnit)
// XXX: public final Observable take(long,TimeUnit,Scheduler)
// XXX: public final Observable takeLast(int)
// XXX: public final Observable takeLast(long,long,TimeUnit)
// XXX: public final Observable takeLast(long,long,TimeUnit,Scheduler)
// XXX: public final Observable takeLast(long,long,TimeUnit,Scheduler,boolean,int)
// XXX: public final Observable takeLast(long,TimeUnit)
// XXX: public final Observable takeLast(long,TimeUnit,boolean)
// XXX: public final Observable takeLast(long,TimeUnit,Scheduler)
// XXX: public final Observable takeLast(long,TimeUnit,Scheduler,boolean)
// XXX: public final Observable takeLast(long,TimeUnit,Scheduler,boolean,int)
// XXX: public final Observable takeUntil(ObservableSource)
// XXX: public final Observable takeUntil(Predicate)
// XXX: public final Observable takeWhile(Predicate)
// XXX: public final Observable throttleFirst(long,TimeUnit)
// XXX: public final Observable throttleFirst(long,TimeUnit,Scheduler)
// XXX: public final Observable throttleLast(long,TimeUnit)
// XXX: public final Observable throttleLast(long,TimeUnit,Scheduler)
// XXX: public final Observable throttleLatest(long,TimeUnit)
// XXX: public final Observable throttleLatest(long,TimeUnit,boolean)
// XXX: public final Observable throttleLatest(long,TimeUnit,Scheduler)
// XXX: public final Observable throttleLatest(long,TimeUnit,Scheduler,boolean)
// XXX: public final Observable throttleWithTimeout(long,TimeUnit)
// XXX: public final Observable throttleWithTimeout(long,TimeUnit,Scheduler)
// XXX: public final Observable timeInterval()
// XXX: public final Observable timeInterval(Scheduler)
// XXX: public final Observable timeInterval(TimeUnit)
// XXX: public final Observable timeInterval(TimeUnit,Scheduler)
// XXX: public final Observable timeout(Function)
// XXX: public final Observable timeout(Function,ObservableSource)
// Default BackpressureStrategy.BUFFER is set
static final class ObservableTimeoutLongTimeUnit<T> {
@BeforeTemplate
Observable<T> before(Observable<T> observable, long timeout, TimeUnit unit) {
return observable.timeout(timeout, unit);
}
@AfterTemplate
Observable<T> after(Observable<T> observable, long timeout, TimeUnit unit) {
return RxJava2Adapter.fluxToObservable(
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.timeout(Duration.of(timeout, unit.toChronoUnit())));
}
}
// XXX: public final Observable timeout(long,TimeUnit,ObservableSource)
// XXX: public final Observable timeout(long,TimeUnit,Scheduler)
// XXX: public final Observable timeout(long,TimeUnit,Scheduler,ObservableSource)
// XXX: public final Observable timeout(ObservableSource,Function)
// XXX: public final Observable timeout(ObservableSource,Function,ObservableSource)
// XXX: public final Observable timestamp()
// XXX: public final Observable timestamp(Scheduler)
// XXX: public final Observable timestamp(TimeUnit)
// XXX: public final Observable timestamp(TimeUnit,Scheduler)
// XXX: public final Object to(Function)
static final class ObservableToFlowable<T> {
@BeforeTemplate
Flowable<T> before(Observable<T> observable, BackpressureStrategy strategy) {
return observable.toFlowable(strategy);
}
@AfterTemplate
Flowable<T> after(Observable<T> observable, BackpressureStrategy strategy) {
return RxJava2Adapter.fluxToFlowable(RxJava2Adapter.observableToFlux(observable, strategy));
}
}
// XXX: public final Future toFuture()
// XXX: public final Single toList()
// XXX: public final Single toList(Callable)
// XXX: public final Single toList(int)
// XXX: public final Single toMap(Function)
// XXX: public final Single toMap(Function,Function)
// XXX: public final Single toMap(Function,Function,Callable)
// XXX: public final Single toMultimap(Function)
// XXX: public final Single toMultimap(Function,Function)
// XXX: public final Single toMultimap(Function,Function,Callable)
// XXX: public final Single toMultimap(Function,Function,Callable,Function)
// XXX: public final Single toSortedList()
// XXX: public final Single toSortedList(Comparator)
// XXX: public final Single toSortedList(Comparator,int)
// XXX: public final Single toSortedList(int)
// XXX: public final Observable unsubscribeOn(Scheduler)
// XXX: public final Observable window(Callable)
// XXX: public final Observable window(Callable,int)
// XXX: public final Observable window(long)
// XXX: public final Observable window(long,long)
// XXX: public final Observable window(long,long,int)
// XXX: public final Observable window(long,long,TimeUnit)
// XXX: public final Observable window(long,long,TimeUnit,Scheduler)
// XXX: public final Observable window(long,long,TimeUnit,Scheduler,int)
// XXX: public final Observable window(long,TimeUnit)
// XXX: public final Observable window(long,TimeUnit,long)
// XXX: public final Observable window(long,TimeUnit,long,boolean)
// XXX: public final Observable window(long,TimeUnit,Scheduler)
// XXX: public final Observable window(long,TimeUnit,Scheduler,long)
// XXX: public final Observable window(long,TimeUnit,Scheduler,long,boolean)
// XXX: public final Observable window(long,TimeUnit,Scheduler,long,boolean,int)
// XXX: public final Observable window(ObservableSource)
// XXX: public final Observable window(ObservableSource,Function)
// XXX: public final Observable window(ObservableSource,Function,int)
// XXX: public final Observable window(ObservableSource,int)
// XXX: public final Observable withLatestFrom(Iterable,Function)
// XXX: public final Observable withLatestFrom(ObservableSource,BiFunction)
// XXX: public final Observable withLatestFrom(ObservableSource[],Function)
// XXX: public final Observable withLatestFrom(ObservableSource,ObservableSource,Function3)
// XXX: public final Observable
// withLatestFrom(ObservableSource,ObservableSource,ObservableSource,Function4)
// XXX: public final Observable
// withLatestFrom(ObservableSource,ObservableSource,ObservableSource,ObservableSource,Function5)
// XXX: public final Observable zipWith(Iterable,BiFunction)
// XXX: public final Observable zipWith(ObservableSource,BiFunction)
// XXX: public final Observable zipWith(ObservableSource,BiFunction,boolean)
// XXX: public final Observable zipWith(ObservableSource,BiFunction,boolean,int)
// XXX: Default BackpressureStrategy.BUFFER is set
@SuppressWarnings("unchecked")
static final class ObservableTestAssertResultItem<T> {
@BeforeTemplate
void before(Observable<T> observable, T item) throws InterruptedException {
Refaster.anyOf(
observable.test().await().assertResult(item),
observable.test().await().assertValue(item));
}
@AfterTemplate
void after(Observable<T> observable, T item) {
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.as(StepVerifier::create)
.expectNext(item)
.verifyComplete();
}
}
// XXX: Default BackpressureStrategy.BUFFER is set
@SuppressWarnings("unchecked")
static final class ObservableTestAssertResult<T> {
@BeforeTemplate
void before(Observable<T> observable) throws InterruptedException {
Refaster.anyOf(observable.test().await().assertResult(), observable.test().await());
}
@AfterTemplate
void after(Observable<T> observable) {
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.as(StepVerifier::create)
.verifyComplete();
}
}
@SuppressWarnings("unchecked")
static final class ObservableTestAssertResultTwoItems<T> {
@BeforeTemplate
void before(Observable<T> observable, T t1, T t2) throws InterruptedException {
observable.test().await().assertResult(t1, t2);
}
@AfterTemplate
void after(Observable<T> observable, T t1, T t2) {
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.as(StepVerifier::create)
.expectNext(t1, t2)
.verifyComplete();
}
}
// XXX: Default BackpressureStrategy.BUFFER is set
static final class ObservableTestAssertValue<T> {
@BeforeTemplate
void before(Observable<T> observable, Predicate<T> predicate) throws InterruptedException {
Refaster.anyOf(
observable.test().await().assertValue(predicate),
observable.test().await().assertValue(predicate).assertNoErrors().assertComplete(),
observable.test().await().assertComplete().assertValue(predicate),
observable.test().await().assertValue(predicate).assertComplete());
}
@AfterTemplate
void after(Observable<T> observable, Predicate<T> predicate) {
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.as(StepVerifier::create)
.expectNextMatches(RxJavaReactorMigrationUtil.toJdkPredicate(predicate))
.verifyComplete();
}
}
// XXX: Default BackpressureStrategy.BUFFER is set
static final class ObservableTestAssertResultValues<T> {
@BeforeTemplate
void before(Observable<T> observable, @Repeated T item) throws InterruptedException {
Refaster.anyOf(
observable.test().await().assertResult(Refaster.asVarargs(item)),
observable.test().await().assertValues(Refaster.asVarargs(item)));
}
@AfterTemplate
void after(Observable<T> observable, @Repeated T item) {
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.as(StepVerifier::create)
.expectNext(item)
.verifyComplete();
}
}
// XXX: Default BackpressureStrategy.BUFFER is set
static final class ObservableTestAssertComplete<T> {
@BeforeTemplate
void before(Observable<T> observable) throws InterruptedException {
observable.test().await().assertComplete();
}
@AfterTemplate
void after(Observable<T> observable) {
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.as(StepVerifier::create)
.verifyComplete();
}
}
// XXX: Default BackpressureStrategy.BUFFER is set
static final class ObservableTestAssertErrorClass<T> {
@BeforeTemplate
void before(Observable<T> observable, Class<? extends Throwable> errorClass)
throws InterruptedException {
observable.test().await().assertError(errorClass);
}
@AfterTemplate
void after(Observable<T> observable, Class<? extends Throwable> errorClass) {
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.as(StepVerifier::create)
.verifyError(errorClass);
}
}
// XXX: Default BackpressureStrategy.BUFFER is set
static final class ObservableTestAssertNoErrors<T> {
@BeforeTemplate
void before(Observable<T> observable) throws InterruptedException {
observable.test().await().assertNoErrors();
}
@AfterTemplate
void after(Observable<T> observable) {
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.as(StepVerifier::create)
.verifyComplete();
}
}
// XXX: Default BackpressureStrategy.BUFFER is set
static final class ObservableTestAssertValueCount<T> {
@BeforeTemplate
void before(Observable<T> observable, int count) throws InterruptedException {
observable.test().await().assertValueCount(count);
}
@AfterTemplate
void after(Observable<T> observable, int count) {
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.as(StepVerifier::create)
.expectNextCount(count)
.verifyComplete();
}
}
// XXX: Add test
// XXX: Default BackpressureStrategy.BUFFER is set
@SuppressWarnings("unchecked")
static final class ObservableTestAssertFailure<T> {
@BeforeTemplate
void before(Observable<T> observable, Class<? extends Throwable> error)
throws InterruptedException {
observable.test().await().assertFailure(error);
}
@AfterTemplate
void after(Observable<T> observable, Class<? extends Throwable> error) {
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.as(StepVerifier::create)
.verifyError(error);
}
}
// XXX: Add test
// XXX: Default BackpressureStrategy.BUFFER is set
static final class ObservableTestAssertNoValues<T> {
@BeforeTemplate
void before(Observable<T> observable) throws InterruptedException {
Refaster.anyOf(
observable.test().await().assertNoValues(),
observable.test().await().assertNoValues().assertComplete());
}
@AfterTemplate
void after(Observable<T> observable) {
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.as(StepVerifier::create)
.verifyComplete();
}
}
// XXX: Add test
// XXX: This introduces AssertJ dependency
@SuppressWarnings("unchecked")
static final class ObservableTestAssertFailureAndMessage<T> {
@BeforeTemplate
void before(Observable<T> observable, Class<? extends Throwable> error, String message)
throws InterruptedException {
observable.test().await().assertFailureAndMessage(error, message);
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.IMPORT_CLASS_DIRECTLY)
void after(Observable<T> observable, Class<? extends Throwable> error, String message) {
RxJava2Adapter.observableToFlux(observable, BackpressureStrategy.BUFFER)
.as(StepVerifier::create)
.expectErrorSatisfies(
t -> assertThat(t).isInstanceOf(error).hasMessageContaining(message))
.verify();
}
}
// XXX: public final TestObserver test(boolean)
}

View File

@@ -0,0 +1,816 @@
package tech.picnic.errorprone.refasterrules;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.refaster.ImportPolicy;
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 io.reactivex.Completable;
import io.reactivex.CompletableSource;
import io.reactivex.Flowable;
import io.reactivex.Maybe;
import io.reactivex.MaybeSource;
import io.reactivex.Single;
import io.reactivex.SingleSource;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.functions.Predicate;
import io.reactivex.schedulers.Schedulers;
import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.reactivestreams.Publisher;
import reactor.adapter.rxjava.RxJava2Adapter;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import tech.picnic.errorprone.migration.util.RxJavaReactorMigrationUtil;
/** The Refaster templates for the migration of the RxJava {@link Single} to Reactor. */
final class RxJavaSingleToReactorTemplates {
private RxJavaSingleToReactorTemplates() {}
// XXX: public static Single amb(Iterable)
// XXX: public static Single ambArray(SingleSource[])
// XXX: public static Flowable concat(Iterable)
// XXX: public static Observable concat(ObservableSource)
// XXX: public static Flowable concat(Publisher)
// XXX: public static Flowable concat(Publisher,int)
// XXX: public static Flowable concat(SingleSource,SingleSource)
// XXX: public static Flowable concat(SingleSource,SingleSource,SingleSource)
// XXX: public static Flowable concat(SingleSource,SingleSource,SingleSource,SingleSource)
// XXX: public static Flowable concatArray(SingleSource[])
// XXX: public static Flowable concatArrayEager(SingleSource[])
// XXX: public static Flowable concatEager(Iterable)
// XXX: public static Flowable concatEager(Publisher)
// XXX: public static Single create(SingleOnSubscribe)
abstract static class SingleDeferFirst<T> {
@Placeholder
abstract Single<? extends T> singleProducer();
@BeforeTemplate
@SuppressWarnings("Convert2MethodRef")
Single<T> before() {
return Single.defer(() -> singleProducer());
}
@AfterTemplate
Single<? extends T> after() {
return RxJava2Adapter.monoToSingle(
Mono.defer(() -> RxJava2Adapter.singleToMono(singleProducer())));
}
}
// XXX: public static Single equals(SingleSource,SingleSource)
static final class SingleErrorCallable<T> {
@BeforeTemplate
Single<T> before(Callable<? extends Throwable> throwable) {
return Single.error(throwable);
}
@UseImportPolicy(ImportPolicy.IMPORT_CLASS_DIRECTLY)
@AfterTemplate
Single<T> after(Callable<? extends Throwable> throwable) {
return RxJava2Adapter.monoToSingle(
Mono.error(RxJavaReactorMigrationUtil.callableAsSupplier(throwable)));
}
}
static final class SingleErrorThrowable<T> {
@BeforeTemplate
Single<T> before(Throwable throwable) {
return Single.error(throwable);
}
@AfterTemplate
Single<T> after(Throwable throwable) {
return RxJava2Adapter.monoToSingle(Mono.error(throwable));
}
}
static final class SingleFromCallable<T> {
@BeforeTemplate
Single<T> before(Callable<? extends T> callable) {
return Single.fromCallable(callable);
}
@UseImportPolicy(ImportPolicy.IMPORT_CLASS_DIRECTLY)
@AfterTemplate
Single<T> after(Callable<? extends T> callable) {
return RxJava2Adapter.monoToSingle(
Mono.fromSupplier(RxJavaReactorMigrationUtil.callableAsSupplier(callable)));
}
}
// XXX: public static Single fromFuture(Future)
// XXX: public static Single fromFuture(Future,long,TimeUnit)
// XXX: public static Single fromFuture(Future,long,TimeUnit,Scheduler)
// XXX: public static Single fromFuture(Future,Scheduler)
// XXX: public static Single fromObservable(ObservableSource)
static final class SingleFromPublisher<T> {
@BeforeTemplate
Single<T> before(Publisher<? extends T> source) {
return Single.fromPublisher(source);
}
@AfterTemplate
Single<T> after(Publisher<? extends T> source) {
return RxJava2Adapter.monoToSingle(Mono.from(source));
}
}
static final class SingleJust<T> {
@BeforeTemplate
Single<T> before(T item) {
return Single.just(item);
}
@AfterTemplate
Single<T> after(T item) {
return RxJava2Adapter.monoToSingle(Mono.just(item));
}
}
// XXX: public static Flowable merge(Iterable)
// XXX: public static Flowable merge(Publisher)
// XXX: public static Single merge(SingleSource)
// XXX: public static Flowable merge(SingleSource,SingleSource)
// XXX: public static Flowable merge(SingleSource,SingleSource,SingleSource)
// XXX: public static Flowable merge(SingleSource,SingleSource,SingleSource,SingleSource)
// XXX: public static Flowable mergeDelayError(Iterable)
// XXX: public static Flowable mergeDelayError(Publisher)
// XXX: public static Flowable mergeDelayError(SingleSource,SingleSource)
// XXX: public static Flowable mergeDelayError(SingleSource,SingleSource,SingleSource)
// XXX: public static Flowable
// mergeDelayError(SingleSource,SingleSource,SingleSource,SingleSource)
static final class SingleNever<T> {
@BeforeTemplate
Single<T> before() {
return Single.never();
}
@AfterTemplate
Single<T> after() {
return RxJava2Adapter.monoToSingle(Mono.never());
}
}
// XXX: public static Single timer(long,TimeUnit)
// XXX: public static Single timer(long,TimeUnit,Scheduler)
// XXX: public static Single unsafeCreate(SingleSource)
// XXX: public static Single using(Callable,Function,Consumer)
// XXX: public static Single using(Callable,Function,Consumer,boolean)
static final class SingleWrap<T> {
@BeforeTemplate
Single<T> before(Single<T> single) {
return Single.wrap(single);
}
@AfterTemplate
Single<T> after(Single<T> single) {
return single;
}
}
// XXX: public static Single zip(Iterable,Function)
// XXX: public static Single zip(SingleSource,SingleSource,BiFunction)
// XXX: public static Single zip(SingleSource,SingleSource,SingleSource,Function3)
// XXX: public static Single zip(SingleSource,SingleSource,SingleSource,SingleSource,Function4)
// XXX: public static Single
// zip(SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,Function5)
// XXX: public static Single
// zip(SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,Function6)
// XXX: public static Single
// zip(SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,Function7)
// XXX: public static Single
// zip(SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,Function8)
// XXX: public static Single
// zip(SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,SingleSource,Function9)
// XXX: public static Single zipArray(Function,SingleSource[])
// XXX: public final Single ambWith(SingleSource)
// XXX: public final Object as(SingleConverter)
static final class SingleBlockingGet<T> {
@BeforeTemplate
Object before(Single<T> single) {
return single.blockingGet();
}
@AfterTemplate
Object after(Single<T> single) {
return RxJava2Adapter.singleToMono(single).block();
}
}
// XXX: public final Single cache()
// XXX: public final Single cast(Class)
// XXX: public final Single compose(SingleTransformer)
static final class SingleConcatWith<T> {
@BeforeTemplate
Flowable<T> before(Single<T> single, SingleSource<? extends T> source) {
return single.concatWith(source);
}
@AfterTemplate
Flowable<T> after(Single<T> single, SingleSource<? extends T> source) {
return RxJava2Adapter.fluxToFlowable(
RxJava2Adapter.singleToMono(single)
.concatWith(RxJava2Adapter.singleToMono(Single.wrap(source))));
}
}
// XXX: public final Single contains(Object)
// XXX: public final Single contains(Object,BiPredicate)
// XXX: public final Single delay(long,TimeUnit)
// XXX: public final Single delay(long,TimeUnit,boolean)
// XXX: public final Single delay(long,TimeUnit,Scheduler)
// XXX: public final Single delay(long,TimeUnit,Scheduler,boolean)
// XXX: public final Single delaySubscription(CompletableSource)
// XXX: public final Single delaySubscription(long,TimeUnit)
// XXX: public final Single delaySubscription(long,TimeUnit,Scheduler)
// XXX: public final Single delaySubscription(ObservableSource)
// XXX: public final Single delaySubscription(Publisher)
// XXX: public final Single delaySubscription(SingleSource)
// XXX: public final Maybe dematerialize(Function)
// XXX: public final Single doAfterSuccess(Consumer)
// XXX: public final Single doAfterTerminate(Action)
// XXX: public final Single doFinally(Action)
// XXX: public final Single doOnDispose(Action)
static final class SingleDoOnError<T> {
@BeforeTemplate
Single<T> before(Single<T> single, Consumer<? super Throwable> consumer) {
return single.doOnError(consumer);
}
@AfterTemplate
Single<T> after(Single<T> single, Consumer<? super Throwable> consumer) {
return RxJava2Adapter.monoToSingle(
RxJava2Adapter.singleToMono(single)
.doOnError(RxJavaReactorMigrationUtil.toJdkConsumer(consumer)));
}
}
// XXX: public final Single doOnEvent(BiConsumer)
// XXX: public final Single doOnSubscribe(Consumer)
static final class SingleDoOnSuccess<T> {
@BeforeTemplate
Single<T> before(Single<T> single, Consumer<T> consumer) {
return single.doOnSuccess(consumer);
}
@AfterTemplate
Single<T> after(Single<T> single, Consumer<T> consumer) {
return RxJava2Adapter.monoToSingle(
RxJava2Adapter.singleToMono(single)
.doOnSuccess(RxJavaReactorMigrationUtil.toJdkConsumer(consumer)));
}
}
// XXX: public final Single doOnTerminate(Action)
static final class SingleFilter<S, T extends S> {
@BeforeTemplate
Maybe<T> before(Single<T> single, Predicate<S> predicate) {
return single.filter(predicate);
}
@AfterTemplate
Maybe<T> after(Single<T> single, Predicate<S> predicate) {
return RxJava2Adapter.monoToMaybe(
RxJava2Adapter.singleToMono(single)
.filter(RxJavaReactorMigrationUtil.toJdkPredicate(predicate)));
}
}
// XXX: Add test
static final class SingleFlatMapFunction<I, T extends I, O, M extends SingleSource<O>> {
@BeforeTemplate
Single<O> before(
Single<T> single, Function<? super I, ? extends SingleSource<? extends O>> function) {
return single.flatMap(function);
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.IMPORT_CLASS_DIRECTLY)
Single<O> after(Single<T> single, Function<I, M> function) {
return RxJava2Adapter.monoToSingle(
RxJava2Adapter.singleToMono(single)
.flatMap(
v ->
RxJava2Adapter.singleToMono(
Single.wrap(
RxJavaReactorMigrationUtil.<I, M>toJdkFunction(function).apply(v)))));
}
}
abstract static class SingleFlatMapLambda<S, T> {
@Placeholder
abstract Single<T> toSingleFunction(@MayOptionallyUse S element);
@BeforeTemplate
Single<T> before(Single<S> single) {
return Refaster.anyOf(
single.flatMap(v -> toSingleFunction(v)), single.flatMap((S v) -> toSingleFunction(v)));
}
@AfterTemplate
Single<T> after(Single<S> single) {
return RxJava2Adapter.monoToSingle(
RxJava2Adapter.singleToMono(single)
.flatMap(v -> RxJava2Adapter.singleToMono(toSingleFunction(v))));
}
}
static final class SingleFlatMapCompletable<T, R extends CompletableSource> {
@BeforeTemplate
Completable before(
Single<T> single, Function<? super T, ? extends CompletableSource> function) {
return single.flatMapCompletable(function);
}
@AfterTemplate
Completable after(Single<T> single, Function<T, R> function) {
return RxJava2Adapter.monoToCompletable(
RxJava2Adapter.singleToMono(single)
.flatMap(
z ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, R>toJdkFunction(function).apply(z))))
.then());
}
}
// XXX: Add test
static final class SingleFlatMapMaybe<T, R, M extends MaybeSource<R>> {
@BeforeTemplate
Maybe<R> before(
Single<T> single, Function<? super T, ? extends MaybeSource<? extends R>> mapper) {
return single.flatMapMaybe(mapper);
}
@AfterTemplate
Maybe<R> after(Single<T> single, Function<T, M> mapper) {
return RxJava2Adapter.monoToMaybe(
RxJava2Adapter.singleToMono(single)
.flatMap(
e ->
RxJava2Adapter.maybeToMono(
Maybe.wrap(
RxJavaReactorMigrationUtil.<T, M>toJdkFunction(mapper).apply(e)))));
}
}
// XXX: public final Observable flatMapObservable(Function)
static final class SingleFlatMapPublisher<T, R> {
@BeforeTemplate
Flowable<R> before(
Single<T> single, Function<? super T, ? extends Publisher<? extends R>> mapper) {
return single.flatMapPublisher(mapper);
}
@AfterTemplate
Flowable<R> after(
Single<T> single, Function<? super T, ? extends Publisher<? extends R>> mapper) {
return RxJava2Adapter.fluxToFlowable(
RxJava2Adapter.singleToMono(single)
.flatMapMany(RxJavaReactorMigrationUtil.toJdkFunction(mapper)));
}
}
// XXX: public final Flowable flattenAsFlowable(Function)
// XXX: public final Observable flattenAsObservable(Function)
// XXX: public final Single hide()
static final class CompletableIgnoreElement<T> {
@BeforeTemplate
Completable before(Single<T> single) {
return single.ignoreElement();
}
@AfterTemplate
Completable after(Single<T> single) {
return RxJava2Adapter.monoToCompletable(RxJava2Adapter.singleToMono(single).then());
}
}
// XXX: public final Single lift(SingleOperator)
static final class SingleMap<I, T extends I, O> {
@BeforeTemplate
Single<O> before(Single<T> single, Function<? super I, ? extends O> function) {
return single.map(function);
}
@AfterTemplate
Single<O> after(Single<T> single, Function<I, O> function) {
return RxJava2Adapter.monoToSingle(
RxJava2Adapter.singleToMono(single)
.map(RxJavaReactorMigrationUtil.toJdkFunction(function)));
}
}
// XXX: public final Single materialize()
// XXX: public final Flowable mergeWith(SingleSource)
// XXX: public final Single observeOn(Scheduler)
static final class SingleOnErrorResumeNext<
S, T extends S, R, P extends Throwable, Q extends Single<T>> {
@BeforeTemplate
Single<T> before(
Single<T> single,
Function<? super Throwable, ? extends SingleSource<? extends T>> function) {
return single.onErrorResumeNext(function);
}
@AfterTemplate
Single<T> after(Single<T> single, Function<Throwable, Q> function) {
return RxJava2Adapter.monoToSingle(
RxJava2Adapter.singleToMono(single)
.onErrorResume(
err ->
RxJava2Adapter.singleToMono(
RxJavaReactorMigrationUtil.<Throwable, Q>toJdkFunction(function)
.apply(err))));
}
}
// XXX: public final Single onErrorResumeNext(Single)
// XXX: public final Single onErrorReturn(Function)
// XXX: Add test -> Works in PRP.
abstract static class SingleOnErrorReturn<T, S extends T> {
@Placeholder
abstract S placeholder(@MayOptionallyUse Throwable throwable);
@BeforeTemplate
Single<T> before(Single<T> single) {
return single.onErrorReturn(t -> placeholder(t));
}
@AfterTemplate
Single<T> after(Single<T> single) {
return RxJava2Adapter.monoToSingle(
RxJava2Adapter.singleToMono(single).onErrorResume(t -> Mono.just(placeholder(t))));
}
}
// XXX: public final Single onErrorReturnItem(Object)
// XXX: public final Single onTerminateDetach()
// XXX: public final Flowable repeat()
// XXX: public final Flowable repeat(long)
// XXX: public final Flowable repeatUntil(BooleanSupplier)
// XXX: public final Flowable repeatWhen(Function)
// XXX: public final Single retry()
// XXX: public final Single retry(BiPredicate)
// XXX: public final Single retry(long)
// XXX: public final Single retry(long,Predicate)
// XXX: public final Single retry(Predicate)
// XXX: public final Single retryWhen(Function)
// XXX: Add test
static final class SingleSubscribe<T> {
@BeforeTemplate
Disposable before(Single<T> single) {
return single.subscribe();
}
@AfterTemplate
reactor.core.Disposable after(Single<T> single) {
return RxJava2Adapter.singleToMono(single).subscribe();
}
}
// XXX: public final Disposable subscribe(BiConsumer)
// XXX: Add test
static final class SingleSubscribeConsumer<T> {
@BeforeTemplate
Disposable before(Single<T> single, Consumer<? super T> consumer) {
return single.subscribe(consumer);
}
@AfterTemplate
reactor.core.Disposable after(Single<T> single, Consumer<? super T> consumer) {
return RxJava2Adapter.singleToMono(single)
.subscribe(RxJavaReactorMigrationUtil.toJdkConsumer(consumer));
}
}
// XXX: Add test
static final class SingleSubscribeTwoConsumers<T> {
@BeforeTemplate
Disposable before(
Single<T> single, Consumer<? super T> consumer1, Consumer<? super Throwable> consumer2) {
return single.subscribe(consumer1, consumer2);
}
@AfterTemplate
reactor.core.Disposable after(
Single<T> single, Consumer<? super T> consumer1, Consumer<? super Throwable> consumer2) {
return RxJava2Adapter.singleToMono(single)
.subscribe(
RxJavaReactorMigrationUtil.toJdkConsumer(consumer1),
RxJavaReactorMigrationUtil.toJdkConsumer(consumer2));
}
}
// XXX: public final void subscribe(SingleObserver)
// XXX: We are currently not accounting for the Schedulers.computation()
static final class SingleSubscribeOn<T> {
@BeforeTemplate
Single<T> before(Single<T> single) {
return single.subscribeOn(Schedulers.io());
}
@AfterTemplate
Single<T> after(Single<T> single) {
return RxJava2Adapter.monoToSingle(
RxJava2Adapter.singleToMono(single)
.subscribeOn(reactor.core.scheduler.Schedulers.boundedElastic()));
}
}
// XXX: public final SingleObserver subscribeWith(SingleObserver)
// XXX: public final Single takeUntil(CompletableSource)
// XXX: public final Single takeUntil(Publisher)
// XXX: public final Single takeUntil(SingleSource)
static final class SingleTimeoutLongTimeUnit<T> {
@BeforeTemplate
Single<T> before(Single<T> single, long timeout, TimeUnit unit) {
return single.timeout(timeout, unit);
}
@AfterTemplate
Single<T> after(Single<T> single, long timeout, TimeUnit unit) {
return RxJava2Adapter.monoToSingle(
RxJava2Adapter.singleToMono(single).timeout(Duration.of(timeout, unit.toChronoUnit())));
}
}
// XXX: public final Single timeout(long,TimeUnit,Scheduler)
// XXX: public final Single timeout(long,TimeUnit,Scheduler,SingleSource)
static final class SingleTimeoutLongTimeUnitSingleSource<T> {
@BeforeTemplate
Single<T> before(
Single<T> single, long timeout, TimeUnit unit, SingleSource<? extends T> other) {
return single.timeout(timeout, unit, other);
}
@AfterTemplate
Single<T> after(
Single<T> single, long timeout, TimeUnit unit, SingleSource<? extends T> other) {
return RxJava2Adapter.monoToSingle(
RxJava2Adapter.singleToMono(single)
.timeout(
Duration.of(timeout, unit.toChronoUnit()),
RxJava2Adapter.singleToMono(Single.wrap(other))));
}
}
// XXX: public final Single timeout(long,TimeUnit,SingleSource)
// XXX: public final Object to(Function)
// XXX: public final Completable toCompletable()
static final class SingleToFlowable<T> {
@BeforeTemplate
Flowable<T> before(Single<T> single) {
return single.toFlowable();
}
@AfterTemplate
Flowable<T> after(Single<T> single) {
return RxJava2Adapter.fluxToFlowable(RxJava2Adapter.singleToMono(single).flux());
}
}
// XXX: public final Future toFuture()
static final class SingleToMaybe<T> {
@BeforeTemplate
Maybe<T> before(Single<T> single) {
return single.toMaybe();
}
@AfterTemplate
Maybe<T> after(Single<T> single) {
return RxJava2Adapter.monoToMaybe(RxJava2Adapter.singleToMono(single));
}
}
// XXX: public final Observable toObservable()
// XXX: public final Single unsubscribeOn(Scheduler)
static final class SingleZipWith<T, R, U> {
@BeforeTemplate
Single<R> before(
Single<T> single,
SingleSource<U> source,
BiFunction<? super T, ? super U, ? extends R> biFunction) {
return single.zipWith(source, biFunction);
}
@AfterTemplate
Single<R> after(
Single<T> single,
SingleSource<U> source,
BiFunction<? super T, ? super U, ? extends R> biFunction) {
return RxJava2Adapter.monoToSingle(
RxJava2Adapter.singleToMono(single)
.zipWith(
RxJava2Adapter.singleToMono(Single.wrap(source)),
RxJavaReactorMigrationUtil.toJdkBiFunction(biFunction)));
}
}
@SuppressWarnings("unchecked")
static final class SingleTestAssertResultItem<T> {
@BeforeTemplate
void before(Single<T> single, T item) throws InterruptedException {
Refaster.anyOf(
single.test().assertResult(item),
single.test().await().assertResult(item),
single.test().await().assertComplete().assertResult(item),
single.test().await().assertResult(item).assertComplete(),
single.test().await().assertValue(item),
single.test().await().assertComplete().assertValue(item),
single.test().assertValue(item),
single.test().await().assertValue(item).assertComplete());
}
@AfterTemplate
void after(Single<T> single, T item) {
RxJava2Adapter.singleToMono(single)
.as(StepVerifier::create)
.expectNext(item)
.verifyComplete();
}
}
static final class SingleAssertValueSet<T> {
@BeforeTemplate
void before(Single<T> single, ImmutableSet<? extends T> set) throws InterruptedException {
single.test().await().assertNoErrors().assertValueSet(set).assertComplete();
}
@AfterTemplate
void after(Single<T> single, ImmutableSet<? extends T> set) {
RxJava2Adapter.singleToMono(single)
.map(ImmutableSet::of)
.as(StepVerifier::create)
.expectNext(ImmutableSet.copyOf(set))
.verifyComplete();
}
}
@SuppressWarnings("unchecked")
static final class SingleTestAssertResult<T> {
@BeforeTemplate
void before(Single<T> single) throws InterruptedException {
single.test().await().assertResult();
}
@AfterTemplate
void after(Single<T> single) {
RxJava2Adapter.singleToMono(single).as(StepVerifier::create).verifyComplete();
}
}
static final class SingleTestAssertValue<T> {
@BeforeTemplate
void before(Single<T> single, Predicate<T> predicate) throws InterruptedException {
Refaster.anyOf(
single.test().await().assertValue(predicate),
single.test().await().assertValue(predicate).assertComplete(),
single.test().await().assertValue(predicate).assertNoErrors().assertComplete(),
single.test().await().assertComplete().assertValue(predicate));
}
@AfterTemplate
void after(Single<T> single, Predicate<T> predicate) {
RxJava2Adapter.singleToMono(single)
.as(StepVerifier::create)
.expectNextMatches(RxJavaReactorMigrationUtil.toJdkPredicate(predicate))
.verifyComplete();
}
}
static final class SingleTestAssertComplete<T> {
@BeforeTemplate
void before(Single<T> single) throws InterruptedException {
single.test().await().assertComplete();
}
@AfterTemplate
void after(Single<T> single) {
RxJava2Adapter.singleToMono(single).as(StepVerifier::create).verifyComplete();
}
}
static final class SingleTestAssertErrorClass<T> {
@BeforeTemplate
void before(Single<T> single, Class<? extends Throwable> errorClass)
throws InterruptedException {
single.test().await().assertError(errorClass);
}
@AfterTemplate
void after(Single<T> single, Class<? extends Throwable> errorClass) {
RxJava2Adapter.singleToMono(single).as(StepVerifier::create).verifyError(errorClass);
}
}
static final class SingleTestAssertNoErrors<T> {
@BeforeTemplate
void before(Single<T> single) throws InterruptedException {
single.test().await().assertNoErrors();
}
@AfterTemplate
void after(Single<T> single) {
RxJava2Adapter.singleToMono(single).as(StepVerifier::create).verifyComplete();
}
}
static final class SingleTestAssertValueCount<T> {
@BeforeTemplate
void before(Single<T> single, int count) throws InterruptedException {
single.test().await().assertValueCount(count);
}
@AfterTemplate
void after(Single<T> single, int count) {
RxJava2Adapter.singleToMono(single)
.as(StepVerifier::create)
.expectNextCount(count)
.verifyComplete();
}
}
// XXX: Add test
@SuppressWarnings("unchecked")
static final class SingleTestAssertFailure<T> {
@BeforeTemplate
void before(Single<T> single, Class<? extends Throwable> error) throws InterruptedException {
single.test().await().assertFailure(error);
}
@AfterTemplate
void after(Single<T> single, Class<? extends Throwable> error) {
RxJava2Adapter.singleToMono(single).as(StepVerifier::create).verifyError(error);
}
}
// XXX: Add test
static final class SingleTestAssertNoValues<T> {
@BeforeTemplate
void before(Single<T> single) throws InterruptedException {
Refaster.anyOf(
single.test().await().assertNoValues(),
single.test().await().assertNoValues().assertComplete());
}
@AfterTemplate
void after(Single<T> single) {
RxJava2Adapter.singleToMono(single).as(StepVerifier::create).verifyComplete();
}
}
// XXX: Add test
// XXX: This introduces AssertJ dependency
@SuppressWarnings("unchecked")
static final class SingleTestAssertFailureAndMessage<T> {
@BeforeTemplate
void before(Single<T> single, Class<? extends Throwable> error, String message)
throws InterruptedException {
single.test().await().assertFailureAndMessage(error, message);
}
@AfterTemplate
void after(Single<T> single, Class<? extends Throwable> error, String message) {
RxJava2Adapter.singleToMono(single)
.as(StepVerifier::create)
.expectErrorSatisfies(
t -> assertThat(t).isInstanceOf(error).hasMessageContaining(message))
.verify();
}
}
// XXX: public final TestObserver test(boolean)
}

View File

@@ -0,0 +1,495 @@
package tech.picnic.errorprone.refasterrules;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.util.function.Function.identity;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.matchers.IsMethodReferenceOrLambdaHasReturnStatement;
import com.google.errorprone.refaster.ImportPolicy;
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.CanTransformToTargetType;
import com.google.errorprone.refaster.annotation.NotMatches;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.functions.Action;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Predicate;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.Function;
import java.util.function.Supplier;
import org.reactivestreams.Publisher;
import reactor.adapter.rxjava.RxJava2Adapter;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import tech.picnic.errorprone.migration.util.RxJavaReactorMigrationUtil;
/** Assorted Refaster templates for the migration of RxJava to Reactor. */
final class RxJavaToReactorTemplates {
private RxJavaToReactorTemplates() {}
@SuppressWarnings("NullableProblems")
static final class FluxToFlowableToFlux<T> {
@BeforeTemplate
Flux<T> before(Flux<T> flux, BackpressureStrategy strategy) {
return Refaster.anyOf(
RxJava2Adapter.fluxToFlowable(flux).as(RxJava2Adapter::flowableToFlux),
RxJava2Adapter.flowableToFlux(RxJava2Adapter.fluxToFlowable(flux)),
RxJava2Adapter.flowableToFlux(flux.as(RxJava2Adapter::fluxToFlowable)),
RxJava2Adapter.observableToFlux(flux.as(RxJava2Adapter::fluxToObservable), strategy),
flux.as(RxJava2Adapter::fluxToObservable)
.toFlowable(strategy)
.as(RxJava2Adapter::flowableToFlux),
RxJava2Adapter.observableToFlux(RxJava2Adapter.fluxToObservable(flux), strategy),
flux.as(RxJava2Adapter::fluxToFlowable).as(RxJava2Adapter::flowableToFlux));
}
@AfterTemplate
Flux<T> after(Flux<T> flux) {
return flux;
}
}
@SuppressWarnings("NullableProblems")
static final class MonoToFlowableToMono<T> {
@BeforeTemplate
Mono<T> before(Mono<T> mono) {
return Refaster.anyOf(
RxJava2Adapter.monoToMaybe(mono).as(RxJava2Adapter::maybeToMono),
RxJava2Adapter.maybeToMono(RxJava2Adapter.monoToMaybe(mono)),
RxJava2Adapter.maybeToMono(mono.as(RxJava2Adapter::monoToMaybe)),
mono.as(RxJava2Adapter::monoToMaybe).as(RxJava2Adapter::maybeToMono),
RxJava2Adapter.monoToSingle(mono).as(RxJava2Adapter::singleToMono),
RxJava2Adapter.singleToMono(RxJava2Adapter.monoToSingle(mono)),
RxJava2Adapter.singleToMono(mono.as(RxJava2Adapter::monoToSingle)),
mono.as(RxJava2Adapter::monoToSingle).as(RxJava2Adapter::singleToMono));
}
@AfterTemplate
Mono<T> after(Mono<T> mono) {
return mono;
}
}
@SuppressWarnings("NullableProblems")
static final class MonoToFlowableToMonoThen<T> {
@BeforeTemplate
Mono<Void> before(Mono<Void> mono) {
return Refaster.anyOf(
RxJava2Adapter.monoToCompletable(mono).as(RxJava2Adapter::completableToMono),
mono.as(RxJava2Adapter::monoToCompletable).as(RxJava2Adapter::completableToMono),
RxJava2Adapter.completableToMono(RxJava2Adapter.monoToCompletable(mono)),
RxJava2Adapter.completableToMono(mono.as(RxJava2Adapter::monoToCompletable)));
}
@BeforeTemplate
Mono<Void> before2(Mono<T> mono) {
return Refaster.anyOf(
RxJava2Adapter.completableToMono(RxJava2Adapter.monoToCompletable(mono)),
RxJava2Adapter.completableToMono(mono.as(RxJava2Adapter::monoToCompletable)),
RxJava2Adapter.monoToCompletable(mono).as(RxJava2Adapter::completableToMono));
}
@AfterTemplate
Mono<Void> after(Mono<T> mono) {
return mono.then();
}
}
// XXX: Add test cases
static final class MonoToFlowableToFlux<T> {
@BeforeTemplate
@SuppressWarnings("NullableProblems")
Flux<T> before(Mono<T> mono) {
return mono.as(RxJava2Adapter::monoToFlowable).as(RxJava2Adapter::flowableToFlux);
}
@AfterTemplate
Flux<T> after(Mono<T> mono) {
return mono.flux();
}
}
static final class MonoErrorCallableSupplierUtil<T> {
@BeforeTemplate
Mono<T> before(@CanTransformToTargetType Callable<? extends Throwable> callable) {
return Mono.error(RxJavaReactorMigrationUtil.callableAsSupplier(callable));
}
@AfterTemplate
Mono<T> after(Supplier<? extends Throwable> callable) {
return Mono.error(callable);
}
}
@SuppressWarnings({"NoFunctionalReturnType", "FunctionalInterfaceClash"})
static final class RemoveUtilCallable<T> {
@BeforeTemplate
Supplier<T> before(
@NotMatches(IsMethodReferenceOrLambdaHasReturnStatement.class) @CanTransformToTargetType
Callable<T> callable) {
return RxJavaReactorMigrationUtil.callableAsSupplier(callable);
}
@AfterTemplate
Supplier<T> before(Supplier<T> callable) {
return callable;
}
}
@SuppressWarnings("NoFunctionalReturnType")
static final class UnnecessaryFunctionConversion<I, O> {
@BeforeTemplate
java.util.function.Function<? extends I, ? extends O> before(
@NotMatches(IsMethodReferenceOrLambdaHasReturnStatement.class) @CanTransformToTargetType
io.reactivex.functions.Function<? extends I, ? extends O> function) {
return RxJavaReactorMigrationUtil.toJdkFunction(function);
}
@AfterTemplate
java.util.function.Function<? extends I, ? extends O> after(
java.util.function.Function<? extends I, ? extends O> function) {
return function;
}
}
@SuppressWarnings("NoFunctionalReturnType")
static final class UnnecessaryBiFunctionConversion<T, U, R> {
@BeforeTemplate
java.util.function.BiFunction<? super T, ? super U, ? extends R> before(
@CanTransformToTargetType BiFunction<? super T, ? super U, ? extends R> zipper) {
return RxJavaReactorMigrationUtil.toJdkBiFunction(zipper);
}
@AfterTemplate
java.util.function.BiFunction<? super T, ? super U, ? extends R> after(
java.util.function.BiFunction<? super T, ? super U, ? extends R> zipper) {
return zipper;
}
}
@SuppressWarnings("NoFunctionalReturnType")
static final class UnnecessaryConsumerConversion<T> {
@BeforeTemplate
java.util.function.Consumer<? extends T> before(
@CanTransformToTargetType Consumer<? extends T> consumer) {
return RxJavaReactorMigrationUtil.toJdkConsumer(consumer);
}
@AfterTemplate
java.util.function.Consumer<? extends T> after(
java.util.function.Consumer<? extends T> consumer) {
return consumer;
}
}
static final class UnnecessaryRunnableConversion {
@BeforeTemplate
Runnable before(@CanTransformToTargetType Action action) {
return RxJavaReactorMigrationUtil.toRunnable(action);
}
@AfterTemplate
Runnable after(Runnable action) {
return action;
}
}
@SuppressWarnings("NoFunctionalReturnType")
static final class UnnecessaryPredicateConversion<T> {
@BeforeTemplate
java.util.function.Predicate<? extends T> before(
@CanTransformToTargetType Predicate<? extends T> predicate) {
return RxJavaReactorMigrationUtil.toJdkPredicate(predicate);
}
@AfterTemplate
java.util.function.Predicate<? extends T> after(
java.util.function.Predicate<? extends T> predicate) {
return predicate;
}
}
static final class FlowableBiFunctionRemoveUtil<T, U, R> {
@BeforeTemplate
Flowable<R> before(
Publisher<? extends T> source1,
Publisher<? extends U> source2,
@CanTransformToTargetType BiFunction<? super T, ? super U, ? extends R> zipper) {
return RxJava2Adapter.fluxToFlowable(
Flux.<T, U, R>zip(source1, source2, RxJavaReactorMigrationUtil.toJdkBiFunction(zipper)));
}
@AfterTemplate
Flowable<R> after(
Publisher<? extends T> source1,
Publisher<? extends U> source2,
java.util.function.BiFunction<? super T, ? super U, ? extends R> zipper) {
return RxJava2Adapter.fluxToFlowable(Flux.<T, U, R>zip(source1, source2, zipper));
}
}
///////////////////////////////////////////
//////////// ASSORTED TEMPLATES ///////////
///////////////////////////////////////////
static final class MonoFromNestedPublisher<T> {
@BeforeTemplate
Mono<T> before(Flux<T> flux) {
return Mono.from(RxJava2Adapter.fluxToFlowable(flux));
}
@AfterTemplate
Mono<T> after(Flux<T> flux) {
return Mono.from(flux);
}
}
static final class FlowableToFluxWithFilter<T> {
@BeforeTemplate
Flux<T> before(Flux<T> flux, Predicate<T> predicate) {
return RxJava2Adapter.flowableToFlux(
flux.filter(RxJavaReactorMigrationUtil.toJdkPredicate(predicate))
.as(RxJava2Adapter::fluxToFlowable));
}
@AfterTemplate
Flux<T> after(Flux<T> flux, Predicate<T> predicate) {
return flux.filter(RxJavaReactorMigrationUtil.toJdkPredicate(predicate));
}
}
static final class ObservableToFlux<T> {
@BeforeTemplate
Flux<T> before(Flux<T> flux, Predicate<T> predicate, BackpressureStrategy strategy) {
return RxJava2Adapter.observableToFlux(
RxJava2Adapter.fluxToObservable(flux).filter(predicate), strategy);
}
@AfterTemplate
Flux<T> after(Flux<T> flux, Predicate<T> predicate, BackpressureStrategy strategy) {
return flux.filter(RxJavaReactorMigrationUtil.toJdkPredicate(predicate));
}
}
static final class MonoFromToFlowableToFlux<T> {
@BeforeTemplate
Flux<T> before(Mono<T> mono) {
return RxJava2Adapter.flowableToFlux(mono.as(RxJava2Adapter::monoToFlowable));
}
@AfterTemplate
Flux<T> after(Mono<T> mono) {
return mono.flux();
}
}
/** Remove unnecessary {@code Mono#then}. */
static final class MonoThen<T, S> {
@BeforeTemplate
Mono<S> before(Mono<T> mono, Mono<S> other) {
return mono.then().then(other);
}
@AfterTemplate
Mono<S> after(Mono<T> mono, Mono<S> other) {
return mono.then(other);
}
}
/** Prefer {@link Mono#thenMany(Publisher)} without first calling {@code Mono#then}. */
static final class MonoThenMany<T> {
@BeforeTemplate
Flux<T> before(Mono<T> mono, Publisher<T> publisher) {
return mono.then().thenMany(publisher);
}
@AfterTemplate
Flux<T> after(Mono<T> mono, Publisher<T> publisher) {
return mono.thenMany(publisher);
}
}
/** Remove unnecessary {@code Flux#ignoreElements} */
static final class FluxThen<T> {
@BeforeTemplate
Mono<Void> before(Flux<T> flux) {
return flux.ignoreElements().then();
}
@AfterTemplate
Mono<Void> after(Flux<T> flux) {
return flux.then();
}
}
static final class MonoCollectToImmutableList<T> {
@BeforeTemplate
Mono<List<T>> before(Flux<T> flux) {
return flux.collectList();
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
Mono<List<T>> after(Flux<T> flux) {
return flux.collect(toImmutableList());
}
}
static final class MonoDefaultIfEmpty<T> {
@BeforeTemplate
Mono<T> before(Mono<T> mono, T item) {
return mono.switchIfEmpty(Mono.just(item));
}
@AfterTemplate
Mono<T> after(Mono<T> mono, T item) {
return mono.defaultIfEmpty(item);
}
}
static final class FluxDefaultIfEmpty<T> {
@BeforeTemplate
Flux<T> before(Flux<T> flux, T item) {
return flux.switchIfEmpty(Flux.just(item));
}
@AfterTemplate
Flux<T> after(Flux<T> flux, T item) {
return flux.defaultIfEmpty(item);
}
}
/** Remove unnecessary {@code Mono#then} */
static final class MonoVoid {
@BeforeTemplate
Mono<Void> before(Mono<Void> mono) {
return mono.then();
}
@AfterTemplate
Mono<Void> after(Mono<Void> mono) {
return mono;
}
}
static final class FlatMapFluxFromArray<T> {
@BeforeTemplate
@SuppressWarnings("unchecked")
Flux<Object> before(Flux<T[]> flux) {
return flux.flatMap(Flowable::fromArray);
}
@UseImportPolicy(ImportPolicy.IMPORT_CLASS_DIRECTLY)
@AfterTemplate
Flux<Object> after(Flux<T[]> flux) {
return flux.flatMap(Flux::fromArray);
}
}
// XXX: Move this to correct class later on.
static final class FluxToImmutableSet<T> {
@BeforeTemplate
Mono<ImmutableSet<T>> before(Flux<T> flux) {
return flux.collect(toImmutableList()).map(ImmutableSet::copyOf);
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
Mono<ImmutableSet<T>> after(Flux<T> flux) {
return flux.collect(toImmutableSet());
}
}
static final class MonoBlock<T> {
@BeforeTemplate
T before(Mono<T> mono, Duration timeout) {
return mono.timeout(timeout).block();
}
@AfterTemplate
T after(Mono<T> mono, Duration timeout) {
return mono.block(timeout);
}
}
ImmutableSet<Flux<String>> testConcatMapIterable() {
return ImmutableSet.of(
Flux.just(ImmutableList.of("1")).flatMap(Flux::fromIterable),
Flux.just(ImmutableList.of("2")).concatMap(Flux::fromIterable));
}
static final class FluxCollectBlock<T> {
@BeforeTemplate
ImmutableList<T> before(Flux<T> flux) {
return ImmutableList.copyOf(flux.toIterable());
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
ImmutableList<T> after(Flux<T> flux) {
return flux.collect(toImmutableList()).block();
}
}
static final class ConcatMapIterable<T> {
@BeforeTemplate
Flux<T> before(Flux<? extends Iterable<? extends T>> flux) {
return Refaster.anyOf(flux.flatMap(Flux::fromIterable), flux.concatMap(Flux::fromIterable));
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
Flux<T> after(Flux<? extends Iterable<? extends T>> flux) {
return flux.concatMapIterable(identity());
}
}
static final class CollectToImmutableMap<K, V> {
@BeforeTemplate
Mono<Map<K, V>> before(Flux<V> flux, Function<? super V, ? extends K> keyExtractor) {
return flux.collectMap(keyExtractor);
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
Mono<Map<K, V>> after(Flux<V> flux, Function<? super V, ? extends K> keyExtractor) {
return flux.collect(toImmutableMap(keyExtractor, identity()));
}
}
// /** Remove unnecessary {@code Flux#next}. This is not *strictly* behavior preserving. */
// static final class FluxSingle<T> {
// @BeforeTemplate
// Mono<T> before(Flux<T> flux) {
// return flux.next().single();
// }
//
// @AfterTemplate
// Mono<T> after(Flux<T> flux) {
// return flux.single();
// }
// }
// XXX: Find out how we can use this in the future.
// static final class RemoveRedundantCast<T> {
// @BeforeTemplate
// T before(T object) {
// return (T) object;
// }
//
// @AfterTemplate
// T after(T object) {
// return object;
// }
// }
}

View File

@@ -0,0 +1,441 @@
package tech.picnic.errorprone.refasterrules;
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 io.reactivex.Completable;
import io.reactivex.CompletableSource;
import io.reactivex.Maybe;
import io.reactivex.MaybeSource;
import io.reactivex.Single;
import io.reactivex.SingleSource;
import io.reactivex.functions.Function;
import org.reactivestreams.Publisher;
import reactor.adapter.rxjava.RxJava2Adapter;
import reactor.core.publisher.Mono;
import tech.picnic.errorprone.migration.util.RxJavaReactorMigrationUtil;
/** Templates to clean up nested lambdas. */
@SuppressWarnings({"Convert2MethodRef", "NoFunctionalReturnType"})
final class RxJavaToReactorUnwrapTemplates {
private RxJavaToReactorUnwrapTemplates() {}
// XXX: Add test
abstract static class FlowableConcatMapCompletableUnwrapLambda<T> {
@Placeholder
abstract Mono<?> placeholder(@MayOptionallyUse T input);
@BeforeTemplate
java.util.function.Function<? super T, ? extends Publisher<?>> before() {
return e ->
RxJava2Adapter.completableToMono(
RxJavaReactorMigrationUtil.toJdkFunction(
(T ident) -> RxJava2Adapter.monoToCompletable(placeholder(ident)))
.apply(e));
}
@AfterTemplate
java.util.function.Function<T, ? extends Publisher<?>> after() {
return v -> placeholder(v);
}
}
// XXX: Add test
abstract static class MaybeFlatMapUnwrapLambda<I, T extends I, O> {
@Placeholder
abstract Mono<? extends O> placeholder(@MayOptionallyUse T input);
@BeforeTemplate
@SuppressWarnings("unchecked")
java.util.function.Function<? super T, ? extends Mono<? extends O>> before() {
return Refaster.anyOf(
v ->
RxJava2Adapter.maybeToMono(
(Maybe<O>)
RxJavaReactorMigrationUtil.toJdkFunction(
(T ident) -> RxJava2Adapter.monoToMaybe(placeholder(ident)))
.apply(v)),
v ->
RxJava2Adapter.maybeToMono(
RxJavaReactorMigrationUtil.toJdkFunction(
(T ident) -> RxJava2Adapter.monoToMaybe(placeholder(ident)))
.apply(v)));
}
@AfterTemplate
java.util.function.Function<? super T, ? extends Mono<? extends O>> after() {
return v -> placeholder(v);
}
}
// XXX: Add test
abstract static class MaybeFlatMapSingleElementUnwrapLambda<T, R> {
@Placeholder
abstract Mono<R> placeholder(@MayOptionallyUse T input);
@BeforeTemplate
java.util.function.Function<T, ? extends Mono<? extends R>> before() {
return Refaster.anyOf(
e ->
RxJava2Adapter.singleToMono(
Single.wrap(
RxJavaReactorMigrationUtil.toJdkFunction(
(Function<T, SingleSource<R>>)
(T ident) -> RxJava2Adapter.monoToSingle(placeholder(ident)))
.apply(e))),
e ->
RxJava2Adapter.singleToMono(
Single.wrap(
RxJavaReactorMigrationUtil.<T, SingleSource<R>>toJdkFunction(
(T ident) -> RxJava2Adapter.monoToSingle(placeholder(ident)))
.apply(e))));
}
@AfterTemplate
java.util.function.Function<T, ? extends Mono<? extends R>> after() {
return e -> placeholder(e);
}
}
// XXX: Add test
abstract static class SingleFlatMapMaybeUnwrapLambda<T, R> {
@Placeholder
abstract Mono<R> placeholder(@MayOptionallyUse T input);
@BeforeTemplate
java.util.function.Function<? super T, ? extends Mono<? extends R>> before() {
return Refaster.anyOf(
e ->
RxJava2Adapter.maybeToMono(
Maybe.wrap(
RxJavaReactorMigrationUtil.toJdkFunction(
(Function<T, MaybeSource<R>>)
(T ident) -> RxJava2Adapter.monoToMaybe(placeholder(ident)))
.apply(e))),
e ->
RxJava2Adapter.maybeToMono(
Maybe.wrap(
RxJavaReactorMigrationUtil.toJdkFunction(
(Function<T, MaybeSource<R>>)
ident -> RxJava2Adapter.monoToMaybe(placeholder(ident)))
.apply(e))),
e ->
RxJava2Adapter.maybeToMono(
Maybe.wrap(
RxJavaReactorMigrationUtil.<T, MaybeSource<R>>toJdkFunction(
ident -> RxJava2Adapter.monoToMaybe(placeholder(ident)))
.apply(e))));
}
@AfterTemplate
java.util.function.Function<? super T, ? extends Mono<? extends R>> after() {
return e -> placeholder(e);
}
}
// XXX: Add test
@SuppressWarnings("unchecked")
abstract static class SingleOnResumeUnwrapLambda<T, R> {
@Placeholder
abstract Mono<? extends R> placeholder(@MayOptionallyUse Throwable input);
@BeforeTemplate
java.util.function.Function<? extends Throwable, ? extends Mono<? extends R>> before() {
return Refaster.anyOf(
e ->
RxJava2Adapter.singleToMono(
RxJavaReactorMigrationUtil.toJdkFunction(
ident -> RxJava2Adapter.monoToSingle(placeholder(e)))
.apply(e)),
e ->
RxJava2Adapter.singleToMono(
Single.wrap(
RxJavaReactorMigrationUtil.toJdkFunction(
(Function<Throwable, ? extends SingleSource<? extends R>>)
placeholder(e))
.apply(e))),
e ->
RxJava2Adapter.singleToMono(
Single.wrap(
RxJavaReactorMigrationUtil.<Throwable, SingleSource<R>>toJdkFunction(
(Function<Throwable, SingleSource<R>>) placeholder(e))
.apply(e))));
}
@AfterTemplate
java.util.function.Function<Throwable, ? extends Mono<? extends R>> after() {
return v -> placeholder(v);
}
}
// XXX: Add test
@SuppressWarnings("unchecked")
abstract static class SingleOnResumeUnwrapLambdaSpecialCase<T, R> {
@Placeholder
abstract Mono<R> placeholder(@MayOptionallyUse Throwable input);
@BeforeTemplate
java.util.function.Function<? extends Throwable, ? extends Mono<? extends R>> before() {
return e ->
RxJava2Adapter.singleToMono(
RxJavaReactorMigrationUtil.<Throwable, Single<R>>toJdkFunction(
t -> RxJava2Adapter.monoToSingle(placeholder(t)))
.apply(e));
}
@AfterTemplate
java.util.function.Function<Throwable, ? extends Mono<? extends R>> after() {
return v -> placeholder(v);
}
}
// XXX: Add test
abstract static class FlowableConcatMapMaybeDelayErrorUnwrapLambda<T, R> {
@Placeholder
abstract Mono<R> placeholder(@MayOptionallyUse T input);
@BeforeTemplate
java.util.function.Function<? super T, ? extends Publisher<? extends R>> after() {
return Refaster.anyOf(
e ->
Maybe.wrap(
RxJavaReactorMigrationUtil.toJdkFunction(
(Function<T, MaybeSource<R>>)
ident -> RxJava2Adapter.monoToMaybe(placeholder(ident)))
.apply(e))
.toFlowable(),
e ->
Maybe.wrap(
RxJavaReactorMigrationUtil.<T, MaybeSource<R>>toJdkFunction(
ident -> RxJava2Adapter.monoToMaybe(placeholder(ident)))
.apply(e))
.toFlowable());
}
@AfterTemplate
java.util.function.Function<? super T, ? extends Publisher<? extends R>> before() {
return e -> placeholder(e);
}
}
// XXX: Add test
abstract static class FlowableFlatMapCompletableUnwrapLambda<T> {
@Placeholder
abstract Mono<?> placeholder(@MayOptionallyUse T input);
@BeforeTemplate
java.util.function.Function<? super T, ? extends Publisher<? extends Void>> before() {
return Refaster.anyOf(
e ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, Completable>toJdkFunction(
(Function<T, Completable>)
(T ident) -> RxJava2Adapter.monoToCompletable(placeholder(ident)))
.apply(e))),
e ->
RxJava2Adapter.completableToMono(
RxJavaReactorMigrationUtil.<T, Completable>toJdkFunction(
(Function<T, Completable>)
(T ident) -> RxJava2Adapter.monoToCompletable(placeholder(ident)))
.apply(e)),
e ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, Completable>toJdkFunction(
(T ident) -> RxJava2Adapter.monoToCompletable(placeholder(ident)))
.apply(e))),
e ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, CompletableSource>toJdkFunction(
(T ident) -> RxJava2Adapter.monoToCompletable(placeholder(ident)))
.apply(e))),
e ->
RxJava2Adapter.completableToMono(
RxJavaReactorMigrationUtil.<T, Completable>toJdkFunction(
(T ident) -> RxJava2Adapter.monoToCompletable(placeholder(ident)))
.apply(e)));
}
@AfterTemplate
java.util.function.Function<T, Mono<?>> after() {
return v -> placeholder(v);
}
}
// XXX: Improve naming and add test case
abstract static class FlowableUnwrapLambda<T> {
@Placeholder
abstract Completable placeholder(@MayOptionallyUse T input);
@BeforeTemplate
java.util.function.Function<T, Publisher<? extends Void>> before() {
return Refaster.anyOf(
e ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, CompletableSource>toJdkFunction(
(Function<T, CompletableSource>) v -> placeholder(v))
.apply(e))),
e ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, CompletableSource>toJdkFunction(
v -> placeholder(v))
.apply(e))));
}
@AfterTemplate
java.util.function.Function<T, Mono<? extends Void>> after() {
return v -> RxJava2Adapter.completableToMono(Completable.wrap(placeholder(v)));
}
}
abstract static class FlowableFlatMapUnwrapLambda<T> {
@Placeholder
abstract CompletableSource placeholder(@MayOptionallyUse T input);
@BeforeTemplate
java.util.function.Function<T, ? extends Publisher<? extends Void>> before() {
return e ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, CompletableSource>toJdkFunction(
(Function<T, CompletableSource>) v -> placeholder(v))
.apply(e)));
}
@AfterTemplate
java.util.function.Function<T, Mono<? extends Void>> after() {
return v -> RxJava2Adapter.completableToMono(Completable.wrap(placeholder(v)));
}
}
abstract static class UnwrapCompletableExtendsMono<T, R> {
@Placeholder
abstract Mono<? extends R> placeholder(@MayOptionallyUse T input);
@BeforeTemplate
java.util.function.Function<? super T, ? extends Mono<? extends Void>> before() {
return e ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, CompletableSource>toJdkFunction(
(T ident) -> RxJava2Adapter.monoToCompletable(placeholder(ident)))
.apply(e)));
}
@AfterTemplate
java.util.function.Function<T, Mono<? extends R>> after() {
return v -> placeholder(v);
}
}
// XXX: Add test
abstract static class SingleFlatMapUnwrapLambda<T, R> {
@Placeholder
abstract Mono<? extends R> placeholder(@MayOptionallyUse T input);
@BeforeTemplate
java.util.function.Function<T, ? extends Mono<? extends R>> before() {
return v ->
RxJava2Adapter.singleToMono(
(Single<? extends R>)
RxJavaReactorMigrationUtil.toJdkFunction(
(T ident) -> RxJava2Adapter.monoToSingle(placeholder(ident)))
.apply(v));
}
@AfterTemplate
java.util.function.Function<T, ? extends Mono<? extends R>> after() {
return v -> placeholder(v);
}
}
// XXX: Add test
abstract static class SingleRemoveLambdaWithCast<T> {
@Placeholder
abstract Mono<?> placeholder(@MayOptionallyUse T input);
@BeforeTemplate
java.util.function.Function<? super T, ? extends Publisher<? extends Void>> before() {
return Refaster.anyOf(
e ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, Completable>toJdkFunction(
(Function<T, Completable>)
v -> placeholder(v).as(RxJava2Adapter::monoToCompletable))
.apply(e))),
e ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, Completable>toJdkFunction(
(Function<T, Completable>)
v -> RxJava2Adapter.monoToCompletable(placeholder(v)))
.apply(e))),
e ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, Completable>toJdkFunction(
v -> RxJava2Adapter.monoToCompletable(placeholder(v)))
.apply(e))));
}
@AfterTemplate
java.util.function.Function<? super T, ? extends Mono<?>> after() {
return v -> placeholder(v);
}
}
// XXX: Add test
abstract static class SingleRemoveLambdaWithCompletable<T> {
@BeforeTemplate
java.util.function.Function<? super T, ? extends Mono<? extends Void>> before(
Completable completable) {
return Refaster.anyOf(
e ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, CompletableSource>toJdkFunction(
(Function<T, CompletableSource>) v -> completable)
.apply(e))),
e ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, CompletableSource>toJdkFunction(
v -> completable)
.apply(e))));
}
@AfterTemplate
java.util.function.Function<? super T, ? extends Mono<? extends Void>> after(
Completable completable) {
return v -> RxJava2Adapter.completableToMono(completable);
}
}
// XXX: Verify if this template still flags other cases than the one above.
abstract static class SingleRemoveLambdaWithCompletableExtra<T> {
@Placeholder
abstract Completable placeholder(@MayOptionallyUse T input);
@BeforeTemplate
java.util.function.Function<? super T, ? extends Mono<? extends Void>> before() {
return e ->
RxJava2Adapter.completableToMono(
Completable.wrap(
RxJavaReactorMigrationUtil.<T, CompletableSource>toJdkFunction(
(Function<T, CompletableSource>) v -> placeholder(v))
.apply(e)));
}
@AfterTemplate
java.util.function.Function<? super T, ? extends Mono<? extends Void>> after() {
return v -> RxJava2Adapter.completableToMono(placeholder(v));
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,192 @@
// package tech.picnic.errorprone.bugpatterns;
//
// import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
// import static java.util.function.Function.identity;
// import static java.util.function.Predicate.not;
// import static org.assertj.core.api.Assertions.assertThat;
// import static org.assertj.core.api.Assertions.assertThatCode;
// import static org.junit.jupiter.params.provider.Arguments.arguments;
//
// import com.google.common.collect.ImmutableSet;
// import com.google.common.collect.ImmutableSetMultimap;
// import com.google.errorprone.BugCheckerRefactoringTestHelper;
// import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
// import java.util.regex.Pattern;
// import java.util.stream.Stream;
// import org.junit.jupiter.api.Disabled;
// import org.junit.jupiter.api.Test;
// import org.junit.jupiter.params.ParameterizedTest;
// import org.junit.jupiter.params.provider.Arguments;
// import org.junit.jupiter.params.provider.MethodSource;
//
// public final class RefasterCheckTest {
// /** The names of all Refaster template groups defined in this module. */
// private static final ImmutableSet<String> TEMPLATE_GROUPS =
// ImmutableSet.of(
// "AssertJ",
// "AssertJBigDecimal",
// "AssertJBigInteger",
// "AssertJBoolean",
// "AssertJByte",
// "AssertJCharSequence",
// "AssertJDouble",
// "AssertJEnumerable",
// "AssertJFloat",
// "AssertJInteger",
// "AssertJLong",
// "AssertJNumber",
// "AssertJObject",
// "AssertJOptional",
// "AssertJShort",
// "AssertJString",
// "Assorted",
// "BigDecimal",
// "Collection",
// "Comparator",
// "DoubleStream",
// "Equality",
// "ImmutableList",
// "ImmutableListMultimap",
// "ImmutableMap",
// "ImmutableMultiset",
// "ImmutableSet",
// "ImmutableSetMultimap",
// "ImmutableSortedMap",
// "ImmutableSortedMultiset",
// "ImmutableSortedSet",
// "IntStream",
// "JUnit",
// "LongStream",
// "MapEntry",
// "Mockito",
// "Multimap",
// "Null",
// "Optional",
// "Primitive",
// "Reactor",
// "RxJava2Adapter",
// "RxJavaCompletableToReactor",
// "RxJavaFlowableToReactor",
// "RxJavaMaybeToReactor",
// "RxJavaObservableToReactor",
// "RxJavaSingleToReactor",
// "RxJavaToReactor",
// "RxJavaToReactorUnwrap",
// "Stream",
// "String",
// "TestNGToAssertJ",
// "Time",
// "WebClient");
// /**
// * Matches the parts of the fully-qualified name of a template class that should be removed in
// * order to produce the associated {@link #TEMPLATE_GROUPS template group name}.
// */
// private static final Pattern TEMPLATE_FQCN_TRIM_FOR_GROUP_NAME =
// Pattern.compile(".*\\.|Templates\\$.*");
// /**
// * A mapping from template group names to associated template names.
// *
// * <p>In effect, the values correspond to nested classes that represent individual Refaster
// * templates, while the keys correspond to the associated top-level "aggregator" classes.
// */
// private static final ImmutableSetMultimap<String, String> TEMPLATES_BY_GROUP =
// indexTemplateNamesByGroup(RefasterCheck.ALL_CODE_TRANSFORMERS.get().keySet());
//
// /** Returns every known template group name as a parameterized test argument. */
// @SuppressWarnings("UnusedMethod" /* Used as a `@MethodSource`. */)
// private static Stream<Arguments> templateGroupsUnderTest() {
// // XXX: Drop the filter once we have added tests for AssertJ!
// return TEMPLATES_BY_GROUP.keySet().stream()
// .filter(not("AssertJ"::equals))
// .filter(not("Immutable"::equals))
// .filter(not("Reactor"::equals))
// .filter(not("RxJava2Adapter"::equals))
// .map(Arguments::of);
// }
//
// /**
// * Returns every known (template group name, template name) pair as a parameterized test
// argument.
// */
// @SuppressWarnings("UnusedMethod" /* Used as a `@MethodSource`. */)
// private static Stream<Arguments> templatesUnderTest() {
// // XXX: Drop the filter once we have added tests for AssertJ!
// return TEMPLATES_BY_GROUP.entries().stream()
// .filter(e -> !"AssertJ".equals(e.getKey()))
// .filter(e -> !"AssertJ".contains(e.getKey()))
// .filter(e -> !"Immutable".contains(e.getKey()))
// .filter(e -> !"Immutable".equals(e.getKey()))
// .map(e -> arguments(e.getKey(), e.getValue()));
// }
//
// /**
// * Verifies that {@link RefasterCheck#loadAllCodeTransformers} finds at least one code
// transformer
// * for all of the {@link #TEMPLATE_GROUPS}.
// *
// * <p>This test is just as much about ensuring that {@link #TEMPLATE_GROUPS} is exhaustive, so
// * that in turn {@link #replacement}'s coverage is exhaustive.
// */
// @Test
// void loadAllCodeTransformers() {
// assertThat(TEMPLATES_BY_GROUP.keySet()).hasSameElementsAs(TEMPLATE_GROUPS);
// }
//
// /**
// * Verifies for each of the {@link #TEMPLATE_GROUPS} that the associated code transformers have
// * the desired effect.
// */
// @MethodSource("templateGroupsUnderTest")
// @ParameterizedTest
// void replacement(String group) {
// verifyRefactoring(group, namePattern(group));
// }
//
// /**
// * Verifies that all loaded Refaster templates are covered by at least one test.
// *
// * <p>Note that this doesn't guarantee full coverage: this test cannot ascertain that all {@link
// * com.google.errorprone.refaster.Refaster#anyOf} branches are tested. Idem for {@link
// * com.google.errorprone.refaster.annotation.BeforeTemplate} methods in case there are multiple
// .
// */
// @Disabled
// @MethodSource("templatesUnderTest")
// @ParameterizedTest
// void coverage(String group, String template) {
// assertThatCode(() -> verifyRefactoring(group, namePattern(group, template)))
// .withFailMessage(
// "Template %s does not affect the tests for group %s; is it tested?", template, group)
// .isInstanceOf(AssertionError.class)
// .hasMessageFindingMatch("^(diff|expected):");
// }
//
// private static ImmutableSetMultimap<String, String> indexTemplateNamesByGroup(
// ImmutableSet<String> templateNames) {
// return templateNames.stream()
// .collect(
// toImmutableSetMultimap(
// n -> TEMPLATE_FQCN_TRIM_FOR_GROUP_NAME.matcher(n).replaceAll(""), identity()));
// }
//
// private static String namePattern(String groupName, String excludedTemplate) {
// return "(?!" + Pattern.quote(excludedTemplate) + ')' + namePattern(groupName);
// }
//
// private static String namePattern(String groupName) {
// return Pattern.compile(Pattern.quote(groupName)) + "Templates.*";
// }
//
// private void verifyRefactoring(String groupName, String templateNamePattern) {
// createRestrictedRefactoringTestHelper(templateNamePattern)
// .addInput(groupName + "TemplatesTestInput.java")
// .addOutput(groupName + "TemplatesTestOutput.java")
// .doTest(TestMode.TEXT_MATCH);
// }
//
// private BugCheckerRefactoringTestHelper createRestrictedRefactoringTestHelper(
// String namePattern) {
// return BugCheckerRefactoringTestHelper.newInstance(RefasterCheck.class, getClass())
// .setArgs("-XepOpt:Refaster:NamePattern=" + namePattern);
// }
// }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,7 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class AssertJEnumerableRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<?> elidedTypesAndStaticImports() {
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(Iterables.class);
}

View File

@@ -9,7 +9,7 @@ import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class AssertJEnumerableRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<?> elidedTypesAndStaticImports() {
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(Iterables.class);
}

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