Compare commits

...

42 Commits

Author SHA1 Message Date
Stephan Schroevers
b5f825b9ff Thoughts 2024-05-10 19:25:11 +02:00
Stephan Schroevers
d85f2dd6a8 X 2024-05-10 19:25:11 +02:00
Stephan Schroevers
2b7a555c05 WIP: Benchmark generation 2024-05-10 19:25:11 +02:00
Stephan Schroevers
ca85533b0d X 2024-05-10 19:25:11 +02:00
Picnic-DevPla-Bot
e192dacdfb Upgrade pitest-maven-plugin 1.16.0 -> 1.16.1 (#1172)
See:
- https://github.com/hcoles/pitest/releases/tag/1.16.1
- https://github.com/hcoles/pitest/compare/1.16.0...1.16.1
2024-05-10 19:24:00 +02:00
Picnic-DevPla-Bot
8418652de0 Upgrade OpenRewrite Templating 1.8.0 -> 1.8.1 (#1170)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.8.1
- https://github.com/openrewrite/rewrite-templating/compare/v1.8.0...v1.8.1
2024-05-10 19:13:24 +02:00
Picnic-DevPla-Bot
1d8ac35660 Upgrade OpenRewrite 2.10.0 -> 2.11.0 (#1171)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v2.11.0
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.10.0...v2.11.0
2024-05-10 19:00:31 +02:00
Picnic-DevPla-Bot
913cd2ee3a Upgrade Jackson 2.17.0 -> 2.17.1 (#1168)
See:
- https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.17.1
- https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.17.0...jackson-bom-2.17.1
2024-05-06 08:56:55 +02:00
Picnic-DevPla-Bot
6adaa6c4f6 Upgrade NullAway 0.10.25 -> 0.10.26 (#1167)
See:
- https://github.com/uber/NullAway/blob/master/CHANGELOG.md
- https://github.com/uber/NullAway/releases/tag/v0.10.26
- https://github.com/uber/NullAway/compare/v0.10.25...v0.10.26
2024-05-05 12:13:46 +02:00
Stephan Schroevers
08dbb8c298 Upgrade Error Prone 2.26.1 -> 2.27.1 (#1155)
See:
- https://github.com/google/error-prone/releases/tag/v2.27.0
- https://github.com/google/error-prone/releases/tag/v2.27.1
- https://github.com/google/error-prone/compare/v2.26.1...v2.27.1
- https://github.com/PicnicSupermarket/error-prone/compare/v2.26.1-picnic-2...v2.27.1-picnic-1
2024-05-04 16:08:58 +02:00
Picnic-DevPla-Bot
e7bc0e113c Upgrade Guava 33.1.0-jre -> 33.2.0-jre (#1166)
See:
- https://guava.dev/releases/33.2.0-jre/api/diffs/
- https://github.com/google/guava/releases/tag/v33.2.0
- https://github.com/google/guava/compare/v33.1.0...v33.2.0
2024-05-04 13:07:51 +02:00
Picnic-DevPla-Bot
6d2c926b0e Upgrade errorprone-slf4j 0.1.23 -> 0.1.24 (#1164)
See:
- https://github.com/KengoTODA/errorprone-slf4j/releases/tag/v0.1.24
- https://github.com/KengoTODA/errorprone-slf4j/compare/v0.1.23...v0.1.24
2024-05-04 12:58:25 +02:00
Picnic-DevPla-Bot
60e15cb569 Upgrade Checker Framework Annotations 3.42.0 -> 3.43.0 (#1165)
See:
- https://github.com/typetools/checker-framework/releases/tag/checker-framework-3.43.0
- https://github.com/typetools/checker-framework/compare/checker-framework-3.42.0...checker-framework-3.43.0
2024-05-04 12:44:17 +02:00
Picnic-Bot
22f61d3032 Upgrade maven-install-plugin 3.1.1 -> 3.1.2 (#1159)
See:
- https://github.com/apache/maven-install-plugin/releases/tag/maven-install-plugin-3.1.2
- https://github.com/apache/maven-install-plugin/compare/maven-install-plugin-3.1.1...maven-install-plugin-3.1.2
2024-05-02 09:30:44 +02:00
Picnic-DevPla-Bot
5d2a726aec Upgrade MongoDB driver 5.0.1 -> 5.1.0 (#1163)
See:
- https://jira.mongodb.org/issues/?jql=project%20%3D%20JAVA%20AND%20fixVersion%20%3E%205.0.1%20AND%20fixVersion%20%3C%3D%205.1.0
- https://github.com/mongodb/mongo-java-driver/releases/tag/r5.1.0
- https://github.com/mongodb/mongo-java-driver/compare/r5.0.1...r5.1.0
2024-05-02 08:57:49 +02:00
Picnic-Bot
192322a982 Upgrade OpenRewrite Templating 1.6.4 -> 1.8.0 (#1153)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.7.0
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.7.1
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.7.2
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.8.0
- https://github.com/openrewrite/rewrite-templating/compare/v1.6.4...v1.8.0
2024-05-02 08:20:24 +02:00
Picnic-Bot
ddf5d803bd Upgrade Checkstyle 10.15.0 -> 10.16.0 (#1161)
See:
- https://checkstyle.sourceforge.io/releasenotes.html
- https://github.com/checkstyle/checkstyle/releases/tag/checkstyle-10.16.0
- https://github.com/checkstyle/checkstyle/compare/checkstyle-10.15.0...checkstyle-10.16.0
2024-04-30 10:53:04 +02:00
Phil Werli
02fb6d468a Introduce OptionalEmpty Refaster rule (#1156) 2024-04-30 10:09:46 +02:00
Picnic-Bot
e7d50c247d Upgrade step-security/harden-runner v2.7.0 -> v2.7.1 (#1160)
See:
- https://github.com/step-security/harden-runner/releases/tag/v2.7.1
2024-04-30 10:02:09 +02:00
Picnic-Bot
85cfb4b4b7 Upgrade maven-deploy-plugin 3.1.1 -> 3.1.2 (#1158)
See:
- https://github.com/apache/maven-deploy-plugin/releases/tag/maven-deploy-plugin-3.1.2
- https://github.com/apache/maven-deploy-plugin/compare/maven-deploy-plugin-3.1.1...maven-deploy-plugin-3.1.2
2024-04-30 08:56:36 +02:00
Picnic-Bot
0684c577ac Upgrade TestNG 7.10.1 -> 7.10.2 (#1157)
See:
- https://github.com/testng-team/testng/releases/tag/7.10.2
- https://github.com/testng-team/testng/compare/7.10.1...7.10.2
2024-04-30 08:10:56 +02:00
Picnic-Bot
32778edc74 Upgrade actions/checkout v4.1.3 -> v4.1.4 (#1154)
See:
- https://github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v414
2024-04-26 10:10:52 +02:00
Picnic-Bot
1e6780afc1 Upgrade OpenRewrite 2.9.0 -> 2.10.0 (#1152)
See:
- https://github.com/openrewrite/rewrite-recipe-bom/releases/tag/v2.10.0
- https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.9.0...v2.10.0
2024-04-25 09:52:16 +02:00
Picnic-Bot
ef4e004141 Upgrade Byte Buddy 1.14.13 -> 1.14.14 (#1151)
See:
- https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.14.14
- https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.13...byte-buddy-1.14.14
2024-04-25 09:09:49 +02:00
Picnic-Bot
72c5a42feb Upgrade actions/upload-artifact v4.3.2 -> v4.3.3 (#1150)
See:
- https://github.com/actions/upload-artifact/releases/tag/v4.3.3
2024-04-23 11:24:02 +02:00
Picnic-Bot
271e01a02c Upgrade actions/checkout v4.1.1 -> v4.1.3 (#1149)
See:
- https://github.com/actions/checkout/releases/tag/v4.1.3
- https://github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v412
2024-04-23 10:48:35 +02:00
Picnic-Bot
d47549d68f Upgrade maven-jar-plugin 3.4.0 -> 3.4.1 (#1146)
See:
- https://github.com/apache/maven-jar-plugin/releases/tag/maven-jar-plugin-3.4.1
- https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.4.0...maven-jar-plugin-3.4.1
2024-04-22 13:05:52 +02:00
Picnic-Bot
01687c7f3e Upgrade maven-gpg-plugin 3.2.3 -> 3.2.4 (#1145)
See:
- https://github.com/apache/maven-gpg-plugin/releases/tag/maven-gpg-plugin-3.2.4
- https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.3...maven-gpg-plugin-3.2.4
2024-04-22 08:50:57 +02:00
Picnic-Bot
85cb7ffdb1 Upgrade CodeQL v3.24.9 -> v3.25.1 (#1147)
See:
- https://github.com/github/codeql-action/blob/main/CHANGELOG.md
- https://github.com/github/codeql-action/compare/v3.25.0...v3.25.1
- https://github.com/github/codeql-action/compare/v3.24.10...v3.25.0
- https://github.com/github/codeql-action/compare/v3.24.9...v3.24.10
2024-04-22 07:44:09 +02:00
Picnic-Bot
0367037f0a Upgrade ruby/setup-ruby v1.173.0 -> v1.174.0 (#1148)
See:
- https://github.com/ruby/setup-ruby/releases/tag/v1.174.0
2024-04-22 07:30:17 +02:00
Picnic-Bot
6669a2e1ec Upgrade Spring Boot 3.2.4 -> 3.2.5 (#1144)
See:
- https://github.com/spring-projects/spring-boot/releases/tag/v3.2.5
- https://github.com/spring-projects/spring-boot/compare/v3.2.4...v3.2.5
2024-04-20 13:37:38 +02:00
Picnic-Bot
eb36c1e493 Upgrade actions/upload-artifact v4.3.1 -> v4.3.2 (#1143)
See:
- https://github.com/actions/upload-artifact/releases/tag/v4.3.2
2024-04-19 10:14:36 +02:00
Picnic-Bot
8f5faf0f6a Upgrade OpenRewrite Templating 1.6.3 -> 1.6.4 (#1142)
See:
- https://github.com/openrewrite/rewrite-templating/releases/tag/v1.6.4
- https://github.com/openrewrite/rewrite-templating/compare/v1.6.3...v1.6.4
2024-04-19 09:55:53 +02:00
Picnic-Bot
5fad0ea04f Upgrade Spring Security 6.2.3 -> 6.2.4 (#1140)
See:
- https://github.com/spring-projects/spring-security/releases/tag/6.2.4
- https://github.com/spring-projects/spring-security/compare/6.2.3...6.2.4
2024-04-17 08:01:53 +02:00
Stephan Schroevers
4558f8affb Update FluxTake Refaster rule for Reactor 3.5.0+ (#1128) 2024-04-16 17:04:48 +02:00
Picnic-Bot
7be27614da Upgrade maven-jar-plugin 3.3.0 -> 3.4.0 (#1137)
See:
- https://github.com/apache/maven-jar-plugin/releases/tag/maven-jar-plugin-3.4.0
- https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.3.0...maven-jar-plugin-3.4.0
2024-04-15 08:45:16 +02:00
Picnic-Bot
7118d6bf03 Upgrade Spring 6.1.5 -> 6.1.6 (#1133)
See:
- https://github.com/spring-projects/spring-framework/releases/tag/v6.1.6
- https://github.com/spring-projects/spring-framework/compare/v6.1.5...v6.1.6
2024-04-15 08:32:41 +02:00
Picnic-Bot
eb84ddf500 Upgrade maven-gpg-plugin 3.2.2 -> 3.2.3 (#1134)
See:
- https://github.com/apache/maven-gpg-plugin/releases/tag/maven-gpg-plugin-3.2.3
- https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.2...maven-gpg-plugin-3.2.3
2024-04-15 07:44:29 +02:00
Picnic-Bot
032109756d Upgrade SLF4J 2.0.12 -> 2.0.13 (#1136)
See:
- https://www.slf4j.org/news.html
- https://github.com/qos-ch/slf4j/compare/v_2.0.12...v_2.0.13
2024-04-15 07:36:02 +02:00
Picnic-Bot
9e230302e9 Upgrade Project Reactor 2023.0.4 -> 2023.0.5 (#1130)
See:
- https://github.com/reactor/reactor/releases/tag/2023.0.5
- https://github.com/reactor/reactor/compare/2023.0.4...2023.0.5
2024-04-11 09:46:18 +02:00
Picnic-Bot
4708fec201 Upgrade TestNG 7.10.0 -> 7.10.1 (#1131)
See:
- https://github.com/testng-team/testng/releases/tag/7.10.1
- https://github.com/testng-team/testng/compare/7.10.0...7.10.1
2024-04-10 15:10:21 +02:00
Picnic-Bot
d102d6acbb Upgrade pitest-maven-plugin 1.15.8 -> 1.16.0 (#1127)
See:
- https://github.com/hcoles/pitest/releases/tag/1.16.0
- https://github.com/hcoles/pitest/compare/1.15.8...1.16.0
2024-04-10 08:05:25 +02:00
32 changed files with 930 additions and 67 deletions

View File

@@ -26,7 +26,7 @@ jobs:
continue-on-error: ${{ matrix.experimental }}
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
with:
disable-sudo: true
egress-policy: block

View File

@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
with:
disable-sudo: true
egress-policy: block
@@ -39,13 +39,13 @@ jobs:
java-distribution: temurin
maven-version: 3.9.6
- name: Initialize CodeQL
uses: github/codeql-action/init@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
uses: github/codeql-action/init@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
with:
languages: ${{ matrix.language }}
- name: Perform minimal build
if: matrix.language == 'java'
run: mvn -T1C clean package -DskipTests -Dverification.skip
- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
uses: github/codeql-action/analyze@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
with:
category: /language:${{ matrix.language }}

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
with:
disable-sudo: true
egress-policy: block
@@ -39,10 +39,10 @@ jobs:
www.youtube.com:443
youtrack.jetbrains.com:443
- name: Check out code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
persist-credentials: false
- uses: ruby/setup-ruby@5f19ec79cedfadb78ab837f95b87734d0003c899 # v1.173.0
- uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0
with:
working-directory: ./website
bundler-cache: true
@@ -74,7 +74,7 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
with:
disable-sudo: true
egress-policy: block

View File

@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
with:
disable-sudo: true
egress-policy: block
@@ -36,7 +36,7 @@ jobs:
tuf-repo-cdn.sigstore.dev:443
www.bestpractices.dev:443
- name: Check out code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
persist-credentials: false
- name: Run OpenSSF Scorecard analysis
@@ -46,6 +46,6 @@ jobs:
results_format: sarif
publish_results: ${{ github.ref == 'refs/heads/master' }}
- name: Update GitHub's code scanning dashboard
uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
with:
sarif_file: results.sarif

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
with:
disable-sudo: true
egress-policy: block
@@ -36,7 +36,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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: pitest-reports
path: ./target/pit-reports-ci

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
with:
disable-sudo: true
egress-policy: block

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
with:
disable-sudo: true
egress-policy: block
@@ -43,7 +43,7 @@ jobs:
run: xvfb-run ./integration-tests/checkstyle.sh "${{ runner.temp }}/artifacts"
- name: Upload artifacts on failure
if: ${{ failure() }}
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: integration-test-checkstyle
path: "${{ runner.temp }}/artifacts"

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Install Harden-Runner
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
with:
disable-sudo: true
egress-policy: block

View File

@@ -179,6 +179,11 @@
<artifactId>mongodb-driver-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-core</artifactId>
@@ -272,6 +277,11 @@
<artifactId>refaster-compiler</artifactId>
<version>${project.version}</version>
</path>
<path>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-rule-benchmark-generator</artifactId>
<version>${project.version}</version>
</path>
<path>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-support</artifactId>
@@ -280,6 +290,7 @@
</annotationProcessorPaths>
<compilerArgs combine.children="append">
<arg>-Xplugin:DocumentationGenerator -XoutputDirectory=${project.build.directory}/docs</arg>
<arg>-Xplugin:RefasterRuleBenchmarkGenerator -XoutputDirectory=${project.build.directory}/generated-test-sources/test-annotations</arg>
</compilerArgs>
</configuration>
</plugin>

View File

@@ -232,7 +232,7 @@ public final class JUnitValueSource extends BugChecker implements MethodTreeMatc
@Override
public @Nullable Void visitReturn(ReturnTree node, @Nullable Void unused) {
returnExpressions.add(node.getExpression());
return super.visitReturn(node, unused);
return super.visitReturn(node, null);
}
@Override

View File

@@ -192,7 +192,7 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
@Override
public @Nullable Void visitIdentifier(IdentifierTree node, @Nullable Void unused) {
nodes.add(ImmutableList.of(node.getName().toString()));
return super.visitIdentifier(node, unused);
return super.visitIdentifier(node, null);
}
@Override
@@ -203,13 +203,13 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
? STRING_ARGUMENT_SPLITTER.splitToStream(str).collect(toImmutableList())
: ImmutableList.of(String.valueOf(value)));
return super.visitLiteral(node, unused);
return super.visitLiteral(node, null);
}
@Override
public @Nullable Void visitPrimitiveType(PrimitiveTypeTree node, @Nullable Void unused) {
nodes.add(ImmutableList.of(node.getPrimitiveTypeKind().toString()));
return super.visitPrimitiveType(node, unused);
return super.visitPrimitiveType(node, null);
}
}.scan(array, null);

View File

@@ -210,7 +210,7 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
}
}
return super.visitIdentifier(node, unused);
return super.visitIdentifier(node, null);
}
}.scan(tree, null);
}

View File

@@ -22,9 +22,12 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
import reactor.core.publisher.Mono;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/**
@@ -211,6 +214,27 @@ final class AssortedRules {
}
}
/**
* Don't defer to subscription-time non-reactive operations that can efficiently be performed
* during assembly.
*/
// XXX: There are all kinds of variations on this theme. Generalize.
// XXX: Drop or move to `ReactorRules`.
static final class MonoFromSupplierOptionalMapOrElse<T, S> {
@BeforeTemplate
Mono<S> before(Optional<T> optional, Function<? super T, S> transformer, S value) {
return Refaster.anyOf(
Mono.justOrEmpty(optional).map(transformer),
Mono.justOrEmpty(optional).mapNotNull(transformer))
.defaultIfEmpty(value);
}
@AfterTemplate
Mono<? extends S> after(Optional<T> optional, Function<? super T, S> transformer, S value) {
return Mono.fromSupplier(() -> optional.map(transformer).orElse(value));
}
}
// /**
// * Don't unnecessarily pass a method reference to {@link Supplier#get()} or wrap this method
// * in a lambda expression.

View File

@@ -290,7 +290,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) {
T before(T value1, T value2, Comparator<? super T> cmp) {
return Refaster.anyOf(
cmp.compare(value1, value2) <= 0 ? value1 : value2,
cmp.compare(value1, value2) > 0 ? value2 : value1,
@@ -305,7 +305,7 @@ final class ComparatorRules {
}
@AfterTemplate
T after(T value1, T value2, Comparator<T> cmp) {
T after(T value1, T value2, Comparator<? super T> cmp) {
return Comparators.min(value1, value2, cmp);
}
}
@@ -357,7 +357,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) {
T before(T value1, T value2, Comparator<? super T> cmp) {
return Refaster.anyOf(
cmp.compare(value1, value2) >= 0 ? value1 : value2,
cmp.compare(value1, value2) < 0 ? value2 : value1,
@@ -372,7 +372,7 @@ final class ComparatorRules {
}
@AfterTemplate
T after(T value1, T value2, Comparator<T> cmp) {
T after(T value1, T value2, Comparator<? super T> cmp) {
return Comparators.max(value1, value2, cmp);
}
}

View File

@@ -27,6 +27,19 @@ import tech.picnic.errorprone.refaster.matchers.IsLikelyTrivialComputation;
final class OptionalRules {
private OptionalRules() {}
/** Prefer {@link Optional#empty()} over the more contrived alternative. */
static final class OptionalEmpty<T> {
@BeforeTemplate
Optional<T> before() {
return Optional.ofNullable(null);
}
@AfterTemplate
Optional<T> after() {
return Optional.empty();
}
}
static final class OptionalOfNullable<T> {
// XXX: Refaster should be smart enough to also rewrite occurrences in which there are
// parentheses around the null check, but that's currently not the case. Try to fix that.
@@ -114,6 +127,20 @@ final class OptionalRules {
}
}
/** Prefer {@link Optional#equals(Object)} over more contrived alternatives. */
static final class NotOptionalEqualsOptional<T, S> {
@BeforeTemplate
boolean before(Optional<T> optional, S value) {
return Refaster.anyOf(
optional.filter(value::equals).isEmpty(), optional.stream().noneMatch(value::equals));
}
@AfterTemplate
boolean after(Optional<T> optional, S value) {
return !optional.equals(Optional.of(value));
}
}
/**
* Don't use the ternary operator to extract the first element of a possibly-empty {@link
* Iterator} as an {@link Optional}.

View File

@@ -3,7 +3,6 @@ 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;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.reverseOrder;
@@ -52,9 +51,9 @@ import reactor.util.context.Context;
import reactor.util.function.Tuple2;
import tech.picnic.errorprone.refaster.annotation.Description;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
import tech.picnic.errorprone.refaster.annotation.Severity;
import tech.picnic.errorprone.refaster.matchers.IsEmpty;
import tech.picnic.errorprone.refaster.matchers.IsIdentityOperation;
import tech.picnic.errorprone.refaster.matchers.IsLikelyTrivialComputation;
import tech.picnic.errorprone.refaster.matchers.ThrowsCheckedException;
/** Refaster rules related to Reactor expressions and statements. */
@@ -174,6 +173,7 @@ final class ReactorRules {
*
* <p>In particular, avoid mixing of the {@link Optional} and {@link Mono} APIs.
*/
// XXX: Below we now have the opposite advice. Test.
static final class MonoFromOptionalSwitchIfEmpty<T> {
@BeforeTemplate
Mono<T> before(Optional<T> optional, Mono<T> mono) {
@@ -303,6 +303,38 @@ final class ReactorRules {
}
}
/**
* Don't eagerly instantiate {@link Throwable}s for {@link Mono#error(Throwable)}; let the
* framework do it on subscription.
*/
static final class MonoError<T> {
@BeforeTemplate
Mono<T> before(@NotMatches(IsLikelyTrivialComputation.class) Throwable throwable) {
return Mono.error(throwable);
}
@AfterTemplate
Mono<T> after(Throwable throwable) {
return Mono.error(() -> throwable);
}
}
/**
* Don't eagerly instantiate {@link Throwable}s for {@link Flux#error(Throwable)}; let the
* framework do it on subscription.
*/
static final class FluxError<T> {
@BeforeTemplate
Flux<T> before(@NotMatches(IsLikelyTrivialComputation.class) Throwable throwable) {
return Flux.error(throwable);
}
@AfterTemplate
Flux<T> after(Throwable throwable) {
return Flux.error(() -> throwable);
}
}
/** Don't unnecessarily defer {@link Mono#error(Throwable)}. */
static final class MonoDeferredError<T> {
@BeforeTemplate
@@ -380,30 +412,23 @@ final class ReactorRules {
}
/**
* Prefer {@link Flux#take(long, boolean)} over {@link Flux#take(long)}.
* Prefer {@link Flux#take(long)} over {@link Flux#take(long, boolean)} where relevant.
*
* <p>In Reactor versions prior to 3.5.0, {@code Flux#take(long)} makes an unbounded request
* upstream, and is equivalent to {@code Flux#take(long, false)}. In 3.5.0, the behavior of {@code
* Flux#take(long)} will change to that of {@code Flux#take(long, true)}.
*
* <p>The intent with this Refaster rule is to get the new behavior before upgrading to Reactor
* 3.5.0.
* upstream, and is equivalent to {@code Flux#take(long, false)}. From version 3.5.0 onwards, the
* behavior of {@code Flux#take(long)} instead matches {@code Flux#take(long, true)}.
*/
// XXX: Drop this rule some time after upgrading to Reactor 3.6.0, or introduce a way to apply
// this rule only when an older version of Reactor is on the classpath.
// XXX: Once Reactor 3.6.0 is out, introduce a rule that rewrites code in the opposite direction.
@Description(
"Prior to Reactor 3.5.0, `take(n)` requests and unbounded number of elements upstream.")
@Severity(WARNING)
"From Reactor 3.5.0 onwards, `take(n)` no longer requests an unbounded number of elements upstream.")
static final class FluxTake<T> {
@BeforeTemplate
Flux<T> before(Flux<T> flux, long n) {
return flux.take(n);
return flux.take(n, /* limitRequest= */ true);
}
@AfterTemplate
Flux<T> after(Flux<T> flux, long n) {
return flux.take(n, /* limitRequest= */ true);
return flux.take(n);
}
}
@@ -1707,6 +1732,22 @@ final class ReactorRules {
}
}
/**
* Don't defer to subscription-time non-reactive operations that can efficiently be performed
* during assembly.
*/
static final class OptionalMapOrElse<T, S, M extends Mono<? extends S>> {
@BeforeTemplate
Mono<S> before(Optional<T> optional, Function<? super T, M> transformer, M mono) {
return Mono.justOrEmpty(optional).flatMap(transformer).switchIfEmpty(mono);
}
@AfterTemplate
Mono<? extends S> after(Optional<T> optional, Function<? super T, M> transformer, M mono) {
return optional.map(transformer).orElse(mono);
}
}
/** Prefer {@link reactor.util.context.Context#empty()}} over more verbose alternatives. */
// XXX: Introduce Refaster rules or a `BugChecker` that maps `(Immutable)Map.of(k, v)` to
// `Context.of(k, v)` and likewise for multi-pair overloads.

View File

@@ -0,0 +1,187 @@
package tech.picnic.errorprone.refasterrules;
import static org.openjdk.jmh.results.format.ResultFormatType.JSON;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
import com.google.errorprone.refaster.annotation.Placeholder;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.profile.GCProfiler;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import tech.picnic.errorprone.refaster.annotation.Benchmarked;
// XXX: Fix warmup and measurements etc.
// XXX: Flag cases where the `before` code is faster, taking into account variation.
@SuppressWarnings("FieldCanBeFinal") // XXX: Triggers CompilesWithFix!!!
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(
jvmArgs = {"-Xms256M", "-Xmx256M"},
value = 1)
@Warmup(iterations = 1)
@Measurement(iterations = 10, time = 1)
public class ReactorRulesBenchmarks {
public static void main(String[] args) throws RunnerException {
String testRegex = Pattern.quote(ReactorRulesBenchmarks.class.getCanonicalName());
new Runner(
new OptionsBuilder()
.include(testRegex)
.addProfiler(GCProfiler.class)
.resultFormat(JSON) // XXX: Review.
.build())
.run();
}
// XXX: What a benchmarked rule could look like.
@Benchmarked
static final class MonoFromOptionalSwitchIfEmpty<T> {
// XXX: These parameters could perhaps be inferred in the common case.
@Benchmarked.Param private final Optional<String> emptyOptional = Optional.empty();
@Benchmarked.Param private final Optional<String> nonEmptyOptional = Optional.of("foo");
@Benchmarked.Param private final Mono<String> emptyMono = Mono.<String>empty().hide();
@Benchmarked.Param private final Mono<String> nonEmptyMono = Mono.just("bar").hide();
// XXX: Dropped to hide this class from `RefasterRuleCompilerTaskListener`: @BeforeTemplate
Mono<T> before(Optional<T> optional, Mono<T> mono) {
return optional.map(Mono::just).orElse(mono);
}
@AfterTemplate
Mono<T> after(Optional<T> optional, Mono<T> mono) {
return Mono.justOrEmpty(optional).switchIfEmpty(mono);
}
// XXX: Methods such as this one could perhaps be inferred in the common case.
@Benchmarked.OnResult
T subscribe(Mono<T> mono) {
return mono.block();
}
}
// XXX: Variations like this would be generated.
public static class MonoFromOptionalSwitchIfEmptyBenchmark extends ReactorRulesBenchmarks {
private final MonoFromOptionalSwitchIfEmpty<String> rule = new MonoFromOptionalSwitchIfEmpty();
// private final Optional<String> optional = Optional.of("foo");
// private final Mono<String> mono = Mono.just("bar").hide();
private final Optional<String> optional = Optional.empty();
private final Mono<String> mono = Mono.<String>empty().hide();
private final Mono<String> before = rule.before(optional, mono);
private final Mono<String> after = rule.after(optional, mono);
@Benchmark
public Mono<String> before() {
return rule.before(optional, mono);
}
@Benchmark
public String beforeSubscribe1() {
return before.block();
}
@Benchmark
public Mono<String> after() {
return rule.after(optional, mono);
}
@Benchmark
public String afterSubscribe() {
return after.block();
}
}
/////////////////////////////
abstract static class MonoFlatMapToFlux<T, S> {
@Placeholder(allowsIdentity = true)
abstract Mono<S> transformation(@MayOptionallyUse T value);
// XXX: Dropped to hide this class from `RefasterRuleCompilerTaskListener`: @BeforeTemplate
Flux<S> before(Mono<T> mono) {
return mono.flatMapMany(v -> transformation(v));
}
@AfterTemplate
Flux<S> after(Mono<T> mono) {
return mono.flatMap(v -> transformation(v)).flux();
}
@Benchmarked.OnResult
Long subscribe(Flux<T> flux) {
return flux.count().block();
}
@Benchmarked.MinimalPlaceholder
<X> Mono<X> identity(X value) {
return Mono.just(value);
}
}
public static class MonoFlatMapToFluxBenchmarks extends ReactorRulesBenchmarks {
abstract static class Rule<T, S> {
abstract Mono<S> transformation(T value);
Flux<S> before(Mono<T> mono) {
return mono.flatMapMany(v -> transformation(v));
}
Flux<S> after(Mono<T> mono) {
return mono.flatMap(v -> transformation(v)).flux();
}
}
// XXX: For variantions, we could use `@Param(strings)` indirection, like in
// https://github.com/openjdk/jmh/blob/master/jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_35_Profilers.java
// XXX: Or we can make the benchmark abstract and have subclasses for each variant:
// https://github.com/openjdk/jmh/blob/master/jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_24_Inheritance.java
public static class MonoFlatMapToFluxStringStringBenchmark extends MonoFlatMapToFluxBenchmarks {
private final Rule<String, String> rule =
new Rule<>() {
@Override
Mono<String> transformation(String value) {
return Mono.just(value);
}
};
private final Mono<String> mono = Mono.just("foo");
private final Flux<String> before = rule.before(mono);
private final Flux<String> after = rule.after(mono);
@Benchmark
public Flux<String> before() {
return rule.before(mono);
}
@Benchmark
public Flux<String> after() {
return rule.after(mono);
}
@Benchmark
public Long onResultOfBefore() {
return before.count().block();
}
@Benchmark
public Long onResultOfAfter() {
return after.count().block();
}
}
}
}

View File

@@ -13,6 +13,10 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableSet.of(Streams.class);
}
Optional<String> testOptionalEmpty() {
return Optional.ofNullable(null);
}
ImmutableSet<Optional<String>> testOptionalOfNullable() {
return ImmutableSet.of(
toString() == null ? Optional.empty() : Optional.of(toString()),
@@ -41,6 +45,12 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
Optional.of("baz").stream().anyMatch("qux"::equals));
}
ImmutableSet<Boolean> testNotOptionalEqualsOptional() {
return ImmutableSet.of(
Optional.of("foo").filter("bar"::equals).isEmpty(),
Optional.of("baz").stream().noneMatch("qux"::equals));
}
ImmutableSet<Optional<String>> testOptionalFirstIteratorElement() {
return ImmutableSet.of(
ImmutableSet.of("foo").iterator().hasNext()

View File

@@ -15,6 +15,10 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableSet.of(Streams.class);
}
Optional<String> testOptionalEmpty() {
return Optional.empty();
}
ImmutableSet<Optional<String>> testOptionalOfNullable() {
return ImmutableSet.of(Optional.ofNullable(toString()), Optional.ofNullable(toString()));
}
@@ -41,6 +45,12 @@ final class OptionalRulesTest implements RefasterRuleCollectionTestCase {
Optional.of("baz").equals(Optional.of("qux")));
}
ImmutableSet<Boolean> testNotOptionalEqualsOptional() {
return ImmutableSet.of(
!Optional.of("foo").equals(Optional.of("bar")),
!Optional.of("baz").equals(Optional.of("qux")));
}
ImmutableSet<Optional<String>> testOptionalFirstIteratorElement() {
return ImmutableSet.of(
stream(ImmutableSet.of("foo").iterator()).findFirst(),

View File

@@ -118,6 +118,16 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return Flux.just("foo", "bar").zipWithIterable(ImmutableSet.of(1, 2), String::repeat);
}
ImmutableSet<?> testMonoError() {
IllegalStateException exception = new IllegalStateException();
return ImmutableSet.of(Mono.error(exception), Mono.error(new IllegalArgumentException()));
}
ImmutableSet<?> testFluxError() {
IllegalStateException exception = new IllegalStateException();
return ImmutableSet.of(Flux.error(exception), Flux.error(new IllegalArgumentException()));
}
Mono<Void> testMonoDeferredError() {
return Mono.defer(() -> Mono.error(new IllegalStateException()));
}
@@ -142,7 +152,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
}
Flux<Integer> testFluxTake() {
return Flux.just(1, 2, 3).take(1);
return Flux.just(1, 2, 3).take(1, true);
}
Mono<String> testMonoDefaultIfEmpty() {
@@ -575,6 +585,10 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
MathFlux.min(Flux.just(1), reverseOrder()), MathFlux.max(Flux.just(2), naturalOrder()));
}
Mono<String> testOptionalMapOrElse() {
return Mono.justOrEmpty(Optional.of("foo")).flatMap(Mono::just).switchIfEmpty(Mono.just("bar"));
}
ImmutableSet<Context> testContextEmpty() {
return ImmutableSet.of(Context.of(ImmutableMap.of()), Context.of(ImmutableMap.of(1, 2)));
}

View File

@@ -123,6 +123,16 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
.map(function(String::repeat));
}
ImmutableSet<?> testMonoError() {
IllegalStateException exception = new IllegalStateException();
return ImmutableSet.of(Mono.error(exception), Mono.error(() -> new IllegalArgumentException()));
}
ImmutableSet<?> testFluxError() {
IllegalStateException exception = new IllegalStateException();
return ImmutableSet.of(Flux.error(exception), Flux.error(() -> new IllegalArgumentException()));
}
Mono<Void> testMonoDeferredError() {
return Mono.error(() -> new IllegalStateException());
}
@@ -147,7 +157,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
}
Flux<Integer> testFluxTake() {
return Flux.just(1, 2, 3).take(1, true);
return Flux.just(1, 2, 3).take(1);
}
Mono<String> testMonoDefaultIfEmpty() {
@@ -564,6 +574,10 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
return ImmutableSet.of(MathFlux.max(Flux.just(1)), MathFlux.max(Flux.just(2)));
}
Mono<String> testOptionalMapOrElse() {
return Optional.of("foo").map(Mono::just).orElse(Mono.just("bar"));
}
ImmutableSet<Context> testContextEmpty() {
return ImmutableSet.of(Context.empty(), Context.of(ImmutableMap.of(1, 2)));
}

View File

@@ -120,6 +120,7 @@
</annotationProcessorPaths>
<compilerArgs combine.children="append">
<arg>-Xplugin:DocumentationGenerator -XoutputDirectory=${project.build.directory}/docs</arg>
<!-- XXX: Enable `refaster-rule-benchmark-generator` here. -->
</compilerArgs>
</configuration>
</plugin>

119
pom.xml
View File

@@ -45,6 +45,7 @@
<module>error-prone-guidelines</module>
<module>error-prone-utils</module>
<module>refaster-compiler</module>
<module>refaster-rule-benchmark-generator</module>
<module>refaster-runner</module>
<module>refaster-support</module>
<module>refaster-test-support</module>
@@ -207,17 +208,18 @@
<version.auto-service>1.1.1</version.auto-service>
<version.auto-value>1.10.4</version.auto-value>
<version.error-prone>${version.error-prone-orig}</version.error-prone>
<version.error-prone-fork>v${version.error-prone-orig}-picnic-2</version.error-prone-fork>
<version.error-prone-orig>2.26.1</version.error-prone-orig>
<version.error-prone-slf4j>0.1.23</version.error-prone-slf4j>
<version.error-prone-fork>v${version.error-prone-orig}-picnic-1</version.error-prone-fork>
<version.error-prone-orig>2.27.1</version.error-prone-orig>
<version.error-prone-slf4j>0.1.24</version.error-prone-slf4j>
<version.guava-beta-checker>1.0</version.guava-beta-checker>
<version.jdk>17</version.jdk>
<version.jmh>1.37</version.jmh>
<version.maven>3.9.5</version.maven>
<version.mockito>5.11.0</version.mockito>
<version.nopen-checker>1.0.1</version.nopen-checker>
<version.nullaway>0.10.25</version.nullaway>
<version.nullaway>0.10.26</version.nullaway>
<version.pitest-git>1.1.4</version.pitest-git>
<version.rewrite-templating>1.6.3</version.rewrite-templating>
<version.rewrite-templating>1.8.1</version.rewrite-templating>
<version.surefire>3.2.3</version.surefire>
</properties>
@@ -296,7 +298,7 @@
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.17.0</version>
<version>2.17.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -338,7 +340,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-bom</artifactId>
<version>33.1.0-jre</version>
<version>33.2.0-jre</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -360,7 +362,7 @@
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-bom</artifactId>
<version>2023.0.4</version>
<version>2023.0.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -407,7 +409,7 @@
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.14.13</version>
<version>1.14.14</version>
</dependency>
<!-- Specified so that Renovate will file Maven upgrade PRs, which
subsequently will cause `maven-enforcer-plugin` to require that
@@ -432,7 +434,7 @@
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>3.42.0</version>
<version>3.43.0</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
@@ -466,7 +468,12 @@
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-core</artifactId>
<version>5.0.1</version>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${version.jmh}</version>
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
@@ -476,40 +483,40 @@
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-recipe-bom</artifactId>
<version>2.9.0</version>
<version>2.11.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-bom</artifactId>
<version>2.0.12</version>
<version>2.0.13</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>6.1.5</version>
<version>6.1.6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>3.2.4</version>
<version>3.2.5</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-bom</artifactId>
<version>6.2.3</version>
<version>6.2.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.10.0</version>
<version>7.10.2</version>
</dependency>
</dependencies>
</dependencyManagement>
@@ -891,7 +898,7 @@
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>10.15.0</version>
<version>10.16.0</version>
</dependency>
<dependency>
<groupId>io.spring.nohttp</groupId>
@@ -932,6 +939,11 @@
<artifactId>auto-service</artifactId>
<version>${version.auto-service}</version>
</path>
<path>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${version.jmh}</version>
</path>
<path>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-templating</artifactId>
@@ -978,7 +990,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.1</version>
<version>3.1.2</version>
<configuration>
<retryFailedDeploymentCount>3</retryFailedDeploymentCount>
</configuration>
@@ -1068,7 +1080,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.2.2</version>
<version>3.2.4</version>
<executions>
<execution>
<id>sign-artifacts</id>
@@ -1081,12 +1093,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>3.1.1</version>
<version>3.1.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<version>3.4.1</version>
<configuration>
<skipIfEmpty>true</skipIfEmpty>
<archive>
@@ -1412,7 +1424,7 @@
<plugin>
<groupId>org.pitest</groupId>
<artifactId>pitest-maven</artifactId>
<version>1.15.8</version>
<version>1.16.1</version>
<configuration>
<excludedClasses>
<!-- AutoValue generated classes. -->
@@ -2054,6 +2066,67 @@
</pluginManagement>
</build>
</profile>
<profile>
<!-- Enables execution of a JMH benchmark. Given a benchmark class
`tech.picnic.MyBenchmark`, the following command (executed against
the (sub)module in which the benchmark resides) will compile and
execute said benchmark:
mvn process-test-classes -Dverification.skip \
-Djmh.run-benchmark=tech.picnic.MyBenchmark
-->
<id>run-jmh-benchmark</id>
<activation>
<property>
<name>jmh.run-benchmark</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>build-jmh-runtime-classpath</id>
<goals>
<goal>build-classpath</goal>
</goals>
<configuration>
<outputProperty>testClasspath</outputProperty>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>run-jmh-benchmark</id>
<goals>
<goal>java</goal>
</goals>
<phase>process-test-classes</phase>
<configuration>
<classpathScope>test</classpathScope>
<mainClass>${jmh.run-benchmark}</mainClass>
<systemProperties>
<!-- The runtime classpath is defined
in this way so that any JVMs forked by
JMH will have the desired classpath. -->
<systemProperty>
<key>java.class.path</key>
<value>${project.build.testOutputDirectory}${path.separator}${project.build.outputDirectory}${path.separator}${testClasspath}</value>
</systemProperty>
</systemProperties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<!-- This profile is auto-activated when performing a release. -->
<id>release</id>

View File

@@ -112,7 +112,7 @@ final class RefasterRuleCompilerTaskListener implements TaskListener {
return (sym != null
&& sym.getQualifiedName()
.contentEquals(BeforeTemplate.class.getCanonicalName()))
|| super.visitAnnotation(node, unused);
|| super.visitAnnotation(node, null);
}
@Override

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.16.2-SNAPSHOT</version>
</parent>
<artifactId>refaster-rule-benchmark-generator</artifactId>
<name>Picnic :: Error Prone Support :: Refaster Rule Benchmark Generator</name>
<!-- XXX: Review description: can this be an annotation processor? -->
<description>Compiler plugin that derives JMH benchmarks from Refaster Rules.</description>
<url>https://error-prone.picnic.tech</url>
<dependencies>
<!-- XXX: Prune this list. -->
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotation</artifactId>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_check_api</artifactId>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_core</artifactId>
<!-- XXX: Review scope. -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_test_helpers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-support</artifactId>
<!-- XXX: Review scope. -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-guava</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
<groupId>com.google.auto</groupId>
<artifactId>auto-common</artifactId>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<!-- XXX: Explicitly declared as a workaround for
https://github.com/pitest/pitest-junit5-plugin/issues/105. -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,55 @@
package tech.picnic.errorprone.refaster.benchmark;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.auto.service.AutoService;
import com.google.common.annotations.VisibleForTesting;
import com.sun.source.util.JavacTask;
import com.sun.source.util.Plugin;
import com.sun.tools.javac.api.BasicJavacTask;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** A compiler {@link Plugin} that generates JMH benchmarks for Refaster rules. */
// XXX: Review whether this can be an annotation processor instead.
@AutoService(Plugin.class)
public final class RefasterRuleBenchmarkGenerator implements Plugin {
@VisibleForTesting static final String OUTPUT_DIRECTORY_FLAG = "-XoutputDirectory";
private static final Pattern OUTPUT_DIRECTORY_FLAG_PATTERN =
Pattern.compile(Pattern.quote(OUTPUT_DIRECTORY_FLAG) + "=(.*)");
/** Instantiates a new {@link RefasterRuleBenchmarkGenerator} instance. */
public RefasterRuleBenchmarkGenerator() {}
@Override
public String getName() {
return getClass().getSimpleName();
}
@Override
public void init(JavacTask javacTask, String... args) {
checkArgument(args.length == 1, "Precisely one path must be provided");
// XXX: Drop all path logic: instead generate in the same package as the Refaster rule
// collection. (But how do we then determine the base directory?)
javacTask.addTaskListener(
new RefasterRuleBenchmarkGeneratorTaskListener(
((BasicJavacTask) javacTask).getContext(), getOutputPath(args[0])));
}
@VisibleForTesting
static Path getOutputPath(String pathArg) {
Matcher matcher = OUTPUT_DIRECTORY_FLAG_PATTERN.matcher(pathArg);
checkArgument(
matcher.matches(), "'%s' must be of the form '%s=<value>'", pathArg, OUTPUT_DIRECTORY_FLAG);
String path = matcher.group(1);
try {
return Path.of(path);
} catch (InvalidPathException e) {
throw new IllegalArgumentException(String.format("Invalid path '%s'", path), e);
}
}
}

View File

@@ -0,0 +1,118 @@
package tech.picnic.errorprone.refaster.benchmark;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.VisitorState;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskEvent.Kind;
import com.sun.source.util.TaskListener;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.util.Context;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.tools.JavaFileObject;
import org.jspecify.annotations.Nullable;
// XXX: Document.
final class RefasterRuleBenchmarkGeneratorTaskListener implements TaskListener {
private static final Matcher<Tree> IS_TEMPLATE =
anyOf(hasAnnotation(BeforeTemplate.class), hasAnnotation(AfterTemplate.class));
private static final Matcher<Tree> IS_BENCHMARKED =
Matchers.hasAnnotation("tech.picnic.errorprone.refaster.annotation.Benchmarked");
private final Context context;
private final Path outputPath;
RefasterRuleBenchmarkGeneratorTaskListener(Context context, Path outputPath) {
this.context = context;
this.outputPath = outputPath;
}
@Override
public void started(TaskEvent taskEvent) {
if (taskEvent.getKind() == Kind.ANALYZE) {
createOutputDirectory();
}
}
@Override
public void finished(TaskEvent taskEvent) {
if (taskEvent.getKind() != Kind.ANALYZE) {
return;
}
JavaFileObject sourceFile = taskEvent.getSourceFile();
CompilationUnitTree compilationUnit = taskEvent.getCompilationUnit();
ClassTree classTree = JavacTrees.instance(context).getTree(taskEvent.getTypeElement());
if (sourceFile == null || compilationUnit == null || classTree == null) {
return;
}
VisitorState state =
VisitorState.createForUtilityPurposes(context)
.withPath(new TreePath(new TreePath(compilationUnit), classTree));
new TreePathScanner<@Nullable Void, Boolean>() {
@Override
public @Nullable Void visitClass(ClassTree classTree, Boolean doBenchmark) {
// XXX: Validate that `@Benchmarked` is only placed in contexts with at least one Refaster
// rule.
boolean inspectClass = doBenchmark || IS_BENCHMARKED.matches(classTree, state);
if (inspectClass) {
System.out.println(handle(classTree, state));
// XXX: If this class has a `@BeforeTemplate` method, generate a benchmark for it.
}
return super.visitClass(classTree, inspectClass);
}
}.scan(compilationUnit, false);
}
// XXX: Name? Scope?
private static Rule handle(ClassTree classTree, VisitorState state) {
ImmutableList<Rule.Method> methods =
classTree.getMembers().stream()
.filter(m -> IS_TEMPLATE.matches(m, state))
.map(m -> process((MethodTree) m, state))
.collect(toImmutableList());
Rule rule = new Rule(classTree, methods);
return rule;
}
private static Rule.Method process(MethodTree methodTree, VisitorState state) {
// XXX: Initially, disallow `Refaster.x` usages.
// XXX: Initially, disallow references to `@Placeholder` methods.
// XXX: Disallow `void` methods. (Can't be black-holed.)
return new Rule.Method(methodTree);
}
// XXX: Move types down.
record Rule(ClassTree tree, ImmutableList<Rule.Method> methods) {
record Method(MethodTree tree) {}
}
private void createOutputDirectory() {
try {
Files.createDirectories(outputPath);
} catch (IOException e) {
throw new IllegalStateException(
String.format("Error while creating directory with path '%s'", outputPath), e);
}
}
}

View File

@@ -0,0 +1,72 @@
package tech.picnic.errorprone.refaster.benchmark;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.FileManagers;
import com.google.errorprone.FileObjects;
import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.api.JavacTool;
import com.sun.tools.javac.file.JavacFileManager;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
// XXX: This code is a near-duplicate of the identically named class in `documentation-support`.
public final class Compilation {
private Compilation() {}
public static void compileWithRefasterRuleBenchmarkGenerator(
Path outputDirectory, String path, String... lines) {
compileWithRefasterRuleBenchmarkGenerator(
outputDirectory.toAbsolutePath().toString(), path, lines);
}
public static void compileWithRefasterRuleBenchmarkGenerator(
String outputDirectory, String path, String... lines) {
/*
* The compiler options specified here largely match those used by Error Prone's
* `CompilationTestHelper`. A key difference is the stricter linting configuration. When
* compiling using JDK 21+, these lint options also require that certain JDK modules are
* explicitly exported.
*/
compile(
ImmutableList.of(
"--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
"-encoding",
"UTF-8",
"-parameters",
"-proc:none",
"-Werror",
"-Xlint:all,-serial",
"-Xplugin:RefasterRuleBenchmarkGenerator -XoutputDirectory=" + outputDirectory,
"-XDdev",
"-XDcompilePolicy=simple"),
FileObjects.forSourceLines(path, lines));
}
private static void compile(ImmutableList<String> options, JavaFileObject javaFileObject) {
JavacFileManager javacFileManager = FileManagers.testFileManager();
JavaCompiler compiler = JavacTool.create();
List<Diagnostic<?>> diagnostics = new ArrayList<>();
JavacTaskImpl task =
(JavacTaskImpl)
compiler.getTask(
null,
javacFileManager,
diagnostics::add,
options,
ImmutableList.of(),
ImmutableList.of(javaFileObject));
Boolean result = task.call();
assertThat(diagnostics).isEmpty();
assertThat(result).isTrue();
}
}

View File

@@ -0,0 +1,41 @@
package tech.picnic.errorprone.refaster.benchmark;
import static org.assertj.core.api.Assertions.assertThat;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
// XXX: Add relevant tests also present in `DocumentationGeneratorTaskListenerTest`.
// XXX: Test package placement.
final class RefasterRuleBenchmarkGeneratorTaskListenerTest {
// XXX: Rename!
@Test
void xxx(@TempDir Path outputDirectory) {
Compilation.compileWithRefasterRuleBenchmarkGenerator(
outputDirectory,
"TestCheckerWithoutAnnotation.java",
"""
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import java.util.Optional;
import reactor.core.publisher.Mono;
import tech.picnic.errorprone.refaster.annotation.Benchmarked;
@Benchmarked
final class MonoFromOptionalSwitchIfEmpty<T> {
@BeforeTemplate
Mono<T> before(Optional<T> optional, Mono<T> mono) {
return optional.map(Mono::just).orElse(mono);
}
@AfterTemplate
Mono<T> after(Optional<T> optional, Mono<T> mono) {
return Mono.justOrEmpty(optional).switchIfEmpty(mono);
}
}
""");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
}

View File

@@ -0,0 +1,5 @@
package tech.picnic.errorprone.refaster.benchmark;
final class RefasterRuleBenchmarkGeneratorTest {
// XXX: TBD. See `DocumentationGeneratorTest`.
}

View File

@@ -0,0 +1,35 @@
package tech.picnic.errorprone.refaster.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that benchmark(s) should be generated for the annotated Refaster rule or group of
* Refaster rules.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Benchmarked {
// XXX: Make configurable. E.g.
// - While not supported, don't complain about `Refaster.anyOf` or other `Refaster` utility method
// usages.
// - Specify warmup and measurement iterations.
// - Specify output time unit.
// - Value generation hints.f
// Once configuration is supported, annotations on nested classes should override the
// configuration specified by outer classes.
// XXX: Explain use. Allow restriction by name?
@Target(ElementType.FIELD)
@interface Param {}
// XXX: Explain use
@Target(ElementType.METHOD)
@interface OnResult {}
// XXX: Explain use
@Target(ElementType.METHOD)
@interface MinimalPlaceholder {}
}

View File

@@ -3,6 +3,8 @@
releases:
- version: 0.16.1
compatible:
- "2.27.1"
- "2.27.0"
- "2.26.1"
- "2.26.0"
- "2.25.0"
@@ -11,6 +13,8 @@ releases:
- "2.23.0"
- version: 0.16.0
compatible:
- "2.27.1"
- "2.27.0"
- "2.26.1"
- "2.26.0"
- "2.25.0"
@@ -19,6 +23,8 @@ releases:
- "2.23.0"
- version: 0.15.0
compatible:
- "2.27.1"
- "2.27.0"
- "2.26.1"
- "2.26.0"
- "2.25.0"
@@ -27,6 +33,8 @@ releases:
- "2.23.0"
- version: 0.14.0
compatible:
- "2.27.1"
- "2.27.0"
- "2.26.1"
- "2.26.0"
- "2.25.0"