Compare commits

...

51 Commits

Author SHA1 Message Date
Stephan Schroevers
817bd685b0 Migrate away from deprecated edit API 2023-12-17 17:54:32 +01:00
Stephan Schroevers
f27dd279e5 Upgrades 2023-12-17 16:59:29 +01:00
Stephan Schroevers
ce4bcd00f2 Use new model 2023-12-17 16:33:32 +01:00
Stephan Schroevers
3d45b4f1c6 One more 2023-12-17 16:15:05 +01:00
Stephan Schroevers
49d06f47f0 Readme update 2023-12-17 16:15:05 +01:00
Stephan Schroevers
b0549244fd Apply some auto-cleanup 2023-12-17 16:15:05 +01:00
Stephan Schroevers
a6f72ece0d Save logic 2023-12-17 16:15:05 +01:00
Stephan Schroevers
288ef7af94 Restart support 2023-12-17 16:15:05 +01:00
Stephan Schroevers
b6e17b5d54 Preliminary command support 2023-12-17 16:15:05 +01:00
Stephan Schroevers
ed4679ab09 Merge 2023-12-17 16:15:05 +01:00
Stephan Schroevers
87eec476ef Link code 2023-12-17 16:15:05 +01:00
Stephan Schroevers
95594c00a7 Top-level CLI 2023-12-17 16:15:05 +01:00
Stephan Schroevers
7adc5ab557 Better colors, add unused jgit dep 2023-12-17 16:15:05 +01:00
Stephan Schroevers
c71407af01 Submission logic 2023-12-17 16:15:05 +01:00
Stephan Schroevers
4453f66005 Move stuff around 2023-12-17 16:15:05 +01:00
Stephan Schroevers
ceeccd3f55 Highlight things 2023-12-17 16:15:05 +01:00
Stephan Schroevers
89e49b118e Cleanup 2023-12-17 16:15:05 +01:00
Stephan Schroevers
943a409ec1 More doodling 2023-12-17 16:15:05 +01:00
Stephan Schroevers
15622acfc2 Playing around 2023-12-17 16:15:05 +01:00
Stephan Schroevers
9f39ac7942 More tests etc. 2023-12-17 16:15:05 +01:00
Stephan Schroevers
9221f7f212 Checkpoint 2023-12-17 16:15:05 +01:00
Stephan Schroevers
34024296a6 Flush 2023-12-17 16:15:05 +01:00
Stephan Schroevers
c3f952f12d Collect thoughts 2023-12-17 16:15:05 +01:00
Stephan Schroevers
19e4e17c88 Checport 2023-12-17 16:15:05 +01:00
Stephan Schroevers
73bea4aa90 Checkpoint 2023-12-17 16:15:05 +01:00
Stephan Schroevers
2e2ce473b5 Some musings 2023-12-17 16:15:05 +01:00
Stephan Schroevers
23450cb942 Maybe 2023-12-17 16:15:05 +01:00
Stephan Schroevers
dbe72b4561 X 2023-12-17 16:15:05 +01:00
Stephan Schroevers
e220f0e65c Not sure 2023-12-17 16:15:05 +01:00
Stephan Schroevers
68f6c328b3 Ideation 2023-12-17 16:15:05 +01:00
Stephan Schroevers
ac88295c96 Z 2023-12-17 16:15:05 +01:00
Stephan Schroevers
0ff590ef96 X 2023-12-17 16:15:05 +01:00
Stephan Schroevers
098e07dc3b X 2023-12-17 16:15:05 +01:00
Stephan Schroevers
327ab1e629 Checkpoint 2023-12-17 16:15:05 +01:00
Stephan Schroevers
ccd6ae67a6 Checkpoint 2023-12-17 16:15:05 +01:00
Stephan Schroevers
15e61b85a2 Move to separate module 2023-12-17 16:15:05 +01:00
Stephan Schroevers
61dd2e72f2 Checkpoint 2023-12-17 16:15:05 +01:00
Stephan Schroevers
41e59944c3 WIP 2023-12-17 16:05:33 +01:00
Stephan Schroevers
7ccf3e6b42 Use text blocks 2023-12-17 15:55:24 +01:00
Stephan Schroevers
13a1cd2f4b Update all tests to use text blocks
All `CompilationTestHelper` and `BugCheckerRefactoringTestHelper` test
code is now specified using a single text block. These changes were
automated thanks to the new text block support added to the
`ErrorProneTestHelperSourceFormat` check.
2023-12-17 15:50:46 +01:00
Stephan Schroevers
24636372c8 Rebase cleanup 2023-12-17 13:27:25 +01:00
Stephan Schroevers
6d3180df85 Rebase cleanup 2023-12-17 13:20:30 +01:00
Stephan Schroevers
1d625f9f76 Rebase cleanup 2023-12-17 13:20:30 +01:00
Rick Ossendrijver
b9f2f8dd0d Suggestions 2023-12-17 13:20:30 +01:00
Stephan Schroevers
3f659cac50 Some more text blocks 2023-12-17 13:20:30 +01:00
Stephan Schroevers
890c8b23f3 Use instanceof pattern matching 2023-12-17 13:20:28 +01:00
Stephan Schroevers
c24e6cc25d Update some comments 2023-12-17 13:16:56 +01:00
Stephan Schroevers
1292f7121f Use switch expressions 2023-12-17 13:16:56 +01:00
Stephan Schroevers
cb4b973f7c Auto-start RefasterRuleCompiler 2023-12-17 13:16:56 +01:00
Stephan Schroevers
f8251a7b7e Use text blocks 2023-12-17 13:16:54 +01:00
Stephan Schroevers
cebb66479f Use Jabel to support JDK >11 constructs while still targeting JDK 11 2023-12-17 13:06:48 +01:00
134 changed files with 12173 additions and 9189 deletions

35
.github/workflows/build-jdk11.yaml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Build with JDK 17 and test against JDK 11
on:
pull_request:
push:
branches: [ master ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-22.04
steps:
# We run the build twice: once against the original Error Prone
# release, and once against the Picnic Error Prone fork. In both cases
# the code is compiled using JDK 17, while the tests are executed
# using JDK 11.
- name: Check out code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
java-version: |
11.0.20
17.0.8
distribution: temurin
cache: maven
- name: Display build environment details
run: mvn --version
- name: Build project against vanilla Error Prone
run: mvn -T1C install -Dsurefire.jdk-toolchain-version=11.0.20
- name: Build project with self-check against Error Prone fork
run: mvn -T1C clean verify -Perror-prone-fork -Dsurefire.jdk-toolchain-version=11.0.20 -s settings.xml
- name: Remove installed project artifacts
run: mvn build-helper:remove-project-artifact

View File

@@ -10,7 +10,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-22.04 ]
jdk: [ 11.0.20, 17.0.8, 21.0.0 ]
jdk: [ 17.0.8, 21.0.0 ]
distribution: [ temurin ]
experimental: [ false ]
include:

View File

@@ -233,17 +233,11 @@ Other highly relevant commands:
against _all_ code in the current working directory. For more information
check the [PIT Maven plugin][pitest-maven].
When running the project's tests in IntelliJ IDEA, you might see the following
error:
```
java: exporting a package from system module jdk.compiler is not allowed with --release
```
If this happens, go to _Settings -> Build, Execution, Deployment -> Compiler ->
Java Compiler_ and deselect the option _Use '--release' option for
cross-compilation (Java 9 and later)_. See [IDEA-288052][idea-288052] for
details.
The `BugChecker` implementations provided by this project are tested using
Error Prone's `CompilationTestHelper` and `BugCheckerRefactoringTestHelper`
classes. These utilities accept text blocks containing inline Java source code.
To ease modification of this inline source code, consider using IntelliJ IDEA's
[language injection][idea-language-injection] feature.
## 💡 How it works
@@ -280,7 +274,7 @@ channel; please see our [security policy][security] for details.
[github-actions-build-badge]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/build.yml/badge.svg
[github-actions-build-master]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/build.yml?query=branch:master&event=push
[google-java-format]: https://github.com/google/google-java-format
[idea-288052]: https://youtrack.jetbrains.com/issue/IDEA-288052
[idea-language-injection]: https://www.jetbrains.com/help/idea/using-language-injections.html
[license-badge]: https://img.shields.io/github/license/PicnicSupermarket/error-prone-support
[license]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/LICENSE.md
[maven-central-badge]: https://img.shields.io/maven-central/v/tech.picnic.error-prone-support/error-prone-support?color=blue

View File

@@ -125,8 +125,8 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
}
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
return receiver instanceof MethodInvocationTree
? getClassUnderTest((MethodInvocationTree) receiver, state)
return receiver instanceof MethodInvocationTree methodInvocation
? getClassUnderTest(methodInvocation, state)
: Optional.empty();
}
@@ -144,8 +144,8 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
}
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
if (receiver instanceof MethodInvocationTree) {
extractIdentificationTestCases((MethodInvocationTree) receiver, sink, state);
if (receiver instanceof MethodInvocationTree methodInvocation) {
extractIdentificationTestCases(methodInvocation, sink, state);
}
}
@@ -174,8 +174,8 @@ public final class BugPatternTestExtractor implements Extractor<TestCases> {
}
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
if (receiver instanceof MethodInvocationTree) {
extractReplacementTestCases((MethodInvocationTree) receiver, sink, state);
if (receiver instanceof MethodInvocationTree methodInvocation) {
extractReplacementTestCases(methodInvocation, sink, state);
}
}

View File

@@ -3,8 +3,6 @@ package tech.picnic.errorprone.documentation;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.io.Resources;
import java.io.IOException;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -15,92 +13,148 @@ final class BugPatternExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerWithoutAnnotation.java",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"public final class TestCheckerWithoutAnnotation extends BugChecker {}");
"""
import com.google.errorprone.bugpatterns.BugChecker;
public final class TestCheckerWithoutAnnotation extends BugChecker {}
""");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void minimalBugPattern(@TempDir Path outputDirectory) throws IOException {
void minimalBugPattern(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"MinimalBugChecker.java",
"package pkg;",
"",
"import com.google.errorprone.BugPattern;",
"import com.google.errorprone.BugPattern.SeverityLevel;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"@BugPattern(summary = \"MinimalBugChecker summary\", severity = SeverityLevel.ERROR)",
"public final class MinimalBugChecker extends BugChecker {}");
"""
package pkg;
verifyGeneratedFileContent(outputDirectory, "MinimalBugChecker");
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.bugpatterns.BugChecker;
@BugPattern(summary = "MinimalBugChecker summary", severity = SeverityLevel.ERROR)
public final class MinimalBugChecker extends BugChecker {}
""");
verifyGeneratedFileContent(
outputDirectory,
"MinimalBugChecker",
"""
{
"fullyQualifiedName": "pkg.MinimalBugChecker",
"name": "MinimalBugChecker",
"altNames": [],
"link": "",
"tags": [],
"summary": "MinimalBugChecker summary",
"explanation": "",
"severityLevel": "ERROR",
"canDisable": true,
"suppressionAnnotations": [
"java.lang.SuppressWarnings"
]
}
""");
}
@Test
void completeBugPattern(@TempDir Path outputDirectory) throws IOException {
void completeBugPattern(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"CompleteBugChecker.java",
"package pkg;",
"",
"import com.google.errorprone.BugPattern;",
"import com.google.errorprone.BugPattern.SeverityLevel;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import org.junit.jupiter.api.Test;",
"",
"@BugPattern(",
" name = \"OtherName\",",
" summary = \"CompleteBugChecker summary\",",
" linkType = BugPattern.LinkType.CUSTOM,",
" link = \"https://error-prone.picnic.tech\",",
" explanation = \"Example explanation\",",
" severity = SeverityLevel.SUGGESTION,",
" altNames = \"Check\",",
" tags = BugPattern.StandardTags.SIMPLIFICATION,",
" disableable = false,",
" suppressionAnnotations = {BugPattern.class, Test.class})",
"public final class CompleteBugChecker extends BugChecker {}");
"""
package pkg;
verifyGeneratedFileContent(outputDirectory, "CompleteBugChecker");
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.bugpatterns.BugChecker;
import org.junit.jupiter.api.Test;
@BugPattern(
name = "OtherName",
summary = "CompleteBugChecker summary",
linkType = BugPattern.LinkType.CUSTOM,
link = "https://error-prone.picnic.tech",
explanation = "Example explanation",
severity = SeverityLevel.SUGGESTION,
altNames = "Check",
tags = BugPattern.StandardTags.SIMPLIFICATION,
disableable = false,
suppressionAnnotations = {BugPattern.class, Test.class})
public final class CompleteBugChecker extends BugChecker {}
""");
verifyGeneratedFileContent(
outputDirectory,
"CompleteBugChecker",
"""
{
"fullyQualifiedName": "pkg.CompleteBugChecker",
"name": "OtherName",
"altNames": [
"Check"
],
"link": "https://error-prone.picnic.tech",
"tags": [
"Simplification"
],
"summary": "CompleteBugChecker summary",
"explanation": "Example explanation",
"severityLevel": "SUGGESTION",
"canDisable": false,
"suppressionAnnotations": [
"com.google.errorprone.BugPattern",
"org.junit.jupiter.api.Test"
]
}
""");
}
@Test
void undocumentedSuppressionBugPattern(@TempDir Path outputDirectory) throws IOException {
void undocumentedSuppressionBugPattern(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"UndocumentedSuppressionBugPattern.java",
"package pkg;",
"",
"import com.google.errorprone.BugPattern;",
"import com.google.errorprone.BugPattern.SeverityLevel;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"@BugPattern(",
" summary = \"UndocumentedSuppressionBugPattern summary\",",
" severity = SeverityLevel.WARNING,",
" documentSuppression = false)",
"public final class UndocumentedSuppressionBugPattern extends BugChecker {}");
"""
package pkg;
verifyGeneratedFileContent(outputDirectory, "UndocumentedSuppressionBugPattern");
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.bugpatterns.BugChecker;
@BugPattern(
summary = "UndocumentedSuppressionBugPattern summary",
severity = SeverityLevel.WARNING,
documentSuppression = false)
public final class UndocumentedSuppressionBugPattern extends BugChecker {}
""");
verifyGeneratedFileContent(
outputDirectory,
"UndocumentedSuppressionBugPattern",
"""
{
"fullyQualifiedName": "pkg.UndocumentedSuppressionBugPattern",
"name": "UndocumentedSuppressionBugPattern",
"altNames": [],
"link": "",
"tags": [],
"summary": "UndocumentedSuppressionBugPattern summary",
"explanation": "",
"severityLevel": "WARNING",
"canDisable": true,
"suppressionAnnotations": []
}
""");
}
private static void verifyGeneratedFileContent(Path outputDirectory, String testClass)
throws IOException {
private static void verifyGeneratedFileContent(
Path outputDirectory, String testClass, String expectedContent) {
String resourceName = String.format("bugpattern-%s.json", testClass);
assertThat(outputDirectory.resolve(resourceName))
.content(UTF_8)
.isEqualToIgnoringWhitespace(
getResource(
String.join("-", BugPatternExtractorTest.class.getSimpleName(), resourceName)));
}
// XXX: Once we support only JDK 15+, drop this method in favour of including the resources as
// text blocks in this class.
private static String getResource(String resourceName) throws IOException {
return Resources.toString(
Resources.getResource(BugPatternExtractorTest.class, resourceName), UTF_8);
.isEqualToIgnoringWhitespace(expectedContent);
}
}

View File

@@ -15,9 +15,11 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerWithoutAnnotation.java",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"public final class TestCheckerWithoutAnnotation extends BugChecker {}");
"""
import com.google.errorprone.bugpatterns.BugChecker;
public final class TestCheckerWithoutAnnotation extends BugChecker {}
""");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@@ -27,22 +29,24 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\");",
"",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\");",
" }",
"}");
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class TestCheckerTest {
private static class TestChecker extends BugChecker {}
void m() {
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.addSourceLines("A.java", "// BUG: Diagnostic contains:", "class A {}");
BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())
.addInputLines("A.java", "class A {}")
.addOutputLines("A.java", "class A { /* This is a change. */ }");
}
}
""");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@@ -52,22 +56,24 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" void m() {",
" CompilationTestHelper.newInstance((Class<BugChecker>) null, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance((Class<BugChecker>) null, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class TestCheckerTest {
void m() {
CompilationTestHelper.newInstance((Class<BugChecker>) null, getClass())
.addSourceLines("A.java", "// BUG: Diagnostic contains:", "class A {}")
.doTest();
BugCheckerRefactoringTestHelper.newInstance((Class<BugChecker>) null, getClass())
.addInputLines("A.java", "class A {}")
.addOutputLines("A.java", "class A { /* This is a change. */ }")
.doTest();
}
}
""");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@@ -77,28 +83,30 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" @SuppressWarnings(\"unchecked\")",
" void m() {",
" @SuppressWarnings(\"rawtypes\")",
" Class bugChecker = TestChecker.class;",
"",
" CompilationTestHelper.newInstance(bugChecker, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(bugChecker, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class TestCheckerTest {
private static class TestChecker extends BugChecker {}
@SuppressWarnings("unchecked")
void m() {
@SuppressWarnings("rawtypes")
Class bugChecker = TestChecker.class;
CompilationTestHelper.newInstance(bugChecker, getClass())
.addSourceLines("A.java", "// BUG: Diagnostic contains:", "class A {}")
.doTest();
BugCheckerRefactoringTestHelper.newInstance(bugChecker, getClass())
.addInputLines("A.java", "class A {}")
.addOutputLines("A.java", "class A { /* This is a change. */ }")
.doTest();
}
}
""");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@@ -108,27 +116,29 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import com.google.errorprone.scanner.ScannerSupplier;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(",
" ScannerSupplier.fromBugCheckerClasses(TestChecker.class), getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(",
" ScannerSupplier.fromBugCheckerClasses(TestChecker.class), getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.scanner.ScannerSupplier;
final class TestCheckerTest {
private static class TestChecker extends BugChecker {}
void m() {
CompilationTestHelper.newInstance(
ScannerSupplier.fromBugCheckerClasses(TestChecker.class), getClass())
.addSourceLines("A.java", "// BUG: Diagnostic contains:", "class A {}")
.doTest();
BugCheckerRefactoringTestHelper.newInstance(
ScannerSupplier.fromBugCheckerClasses(TestChecker.class), getClass())
.addInputLines("A.java", "class A {}")
.addOutputLines("A.java", "class A { /* This is a change. */ }")
.doTest();
}
}
""");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@@ -138,29 +148,31 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(toString() + \"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .addSourceLines(\"B.java\", \"// BUG: Diagnostic contains:\", \"class B {}\", toString())",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(toString() + \"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .addInputLines(\"B.java\", \"class B {}\", toString())",
" .addOutputLines(\"B.java\", \"class B { /* This is a change. */ }\")",
" .addInputLines(\"C.java\", \"class C {}\")",
" .addOutputLines(\"C.java\", \"class C { /* This is a change. */ }\", toString())",
" .doTest();",
" }",
"}");
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class TestCheckerTest {
private static class TestChecker extends BugChecker {}
void m() {
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.addSourceLines(toString() + "A.java", "// BUG: Diagnostic contains:", "class A {}")
.addSourceLines("B.java", "// BUG: Diagnostic contains:", "class B {}", toString())
.doTest();
BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())
.addInputLines(toString() + "A.java", "class A {}")
.addOutputLines("A.java", "class A { /* This is a change. */ }")
.addInputLines("B.java", "class B {}", toString())
.addOutputLines("B.java", "class B { /* This is a change. */ }")
.addInputLines("C.java", "class C {}")
.addOutputLines("C.java", "class C { /* This is a change. */ }", toString())
.doTest();
}
}
""");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@@ -170,26 +182,28 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper testHelper =",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"class A {}\");",
" testHelper.doTest();",
"",
" BugCheckerRefactoringTestHelper.ExpectOutput expectedOutput =",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\");",
" expectedOutput.addOutputLines(\"A.java\", \"class A {}\").doTest();",
" expectedOutput.expectUnchanged().doTest();",
" }",
"}");
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class TestCheckerTest {
private static class TestChecker extends BugChecker {}
void m() {
CompilationTestHelper testHelper =
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.addSourceLines("A.java", "class A {}");
testHelper.doTest();
BugCheckerRefactoringTestHelper.ExpectOutput expectedOutput =
BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())
.addInputLines("A.java", "class A {}");
expectedOutput.addOutputLines("A.java", "class A {}").doTest();
expectedOutput.expectUnchanged().doTest();
}
}
""");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@@ -199,19 +213,21 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass()).doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass()).doTest();",
" }",
"}");
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class TestCheckerTest {
private static class TestChecker extends BugChecker {}
void m() {
CompilationTestHelper.newInstance(TestChecker.class, getClass()).doTest();
BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass()).doTest();
}
}
""");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@@ -221,26 +237,28 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"class A {}\")",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A {}\")",
" .addInputLines(\"B.java\", \"class B {}\")",
" .expectUnchanged()",
" .doTest();",
" }",
"}");
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class TestCheckerTest {
private static class TestChecker extends BugChecker {}
void m() {
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.addSourceLines("A.java", "class A {}")
.doTest();
BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())
.addInputLines("A.java", "class A {}")
.addOutputLines("A.java", "class A {}")
.addInputLines("B.java", "class B {}")
.expectUnchanged()
.doTest();
}
}
""");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@@ -250,18 +268,20 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"SingleFileCompilationTestHelperTest.java",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class SingleFileCompilationTestHelperTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
" }",
"}");
"""
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class SingleFileCompilationTestHelperTest {
private static class TestChecker extends BugChecker {}
void m() {
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.addSourceLines("A.java", "// BUG: Diagnostic contains:", "class A {}")
.doTest();
}
}
""");
verifyGeneratedFileContent(outputDirectory, "SingleFileCompilationTestHelperTest");
}
@@ -272,19 +292,21 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"SingleFileCompilationTestHelperWithSetArgsTest.java",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class SingleFileCompilationTestHelperWithSetArgsTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .setArgs(\"-XepAllSuggestionsAsWarnings\")",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
" }",
"}");
"""
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class SingleFileCompilationTestHelperWithSetArgsTest {
private static class TestChecker extends BugChecker {}
void m() {
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.setArgs("-XepAllSuggestionsAsWarnings")
.addSourceLines("A.java", "// BUG: Diagnostic contains:", "class A {}")
.doTest();
}
}
""");
verifyGeneratedFileContent(outputDirectory, "SingleFileCompilationTestHelperWithSetArgsTest");
}
@@ -294,19 +316,21 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"MultiFileCompilationTestHelperTest.java",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class MultiFileCompilationTestHelperTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .addSourceLines(\"B.java\", \"// BUG: Diagnostic contains:\", \"class B {}\")",
" .doTest();",
" }",
"}");
"""
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class MultiFileCompilationTestHelperTest {
private static class TestChecker extends BugChecker {}
void m() {
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.addSourceLines("A.java", "// BUG: Diagnostic contains:", "class A {}")
.addSourceLines("B.java", "// BUG: Diagnostic contains:", "class B {}")
.doTest();
}
}
""");
verifyGeneratedFileContent(outputDirectory, "MultiFileCompilationTestHelperTest");
}
@@ -316,19 +340,21 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"SingleFileBugCheckerRefactoringTestHelperTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class SingleFileBugCheckerRefactoringTestHelperTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class SingleFileBugCheckerRefactoringTestHelperTest {
private static class TestChecker extends BugChecker {}
void m() {
BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())
.addInputLines("A.java", "class A {}")
.addOutputLines("A.java", "class A { /* This is a change. */ }")
.doTest();
}
}
""");
verifyGeneratedFileContent(outputDirectory, "SingleFileBugCheckerRefactoringTestHelperTest");
}
@@ -339,23 +365,25 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .setArgs(\"-XepAllSuggestionsAsWarnings\")",
" .setFixChooser(FixChoosers.SECOND)",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest(TestMode.TEXT_MATCH);",
" }",
"}");
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.bugpatterns.BugChecker;
final class SingleFileBugCheckerRefactoringTestHelperWithSetArgsFixChooserAndCustomTestModeTest {
private static class TestChecker extends BugChecker {}
void m() {
BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())
.setArgs("-XepAllSuggestionsAsWarnings")
.setFixChooser(FixChoosers.SECOND)
.addInputLines("A.java", "class A {}")
.addOutputLines("A.java", "class A { /* This is a change. */ }")
.doTest(TestMode.TEXT_MATCH);
}
}
""");
verifyGeneratedFileContent(
outputDirectory,
@@ -367,21 +395,23 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"MultiFileBugCheckerRefactoringTestHelperTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class MultiFileBugCheckerRefactoringTestHelperTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .addInputLines(\"B.java\", \"class B {}\")",
" .addOutputLines(\"B.java\", \"class B { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class MultiFileBugCheckerRefactoringTestHelperTest {
private static class TestChecker extends BugChecker {}
void m() {
BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())
.addInputLines("A.java", "class A {}")
.addOutputLines("A.java", "class A { /* This is a change. */ }")
.addInputLines("B.java", "class B {}")
.addOutputLines("B.java", "class B { /* This is a change. */ }")
.doTest();
}
}
""");
verifyGeneratedFileContent(outputDirectory, "MultiFileBugCheckerRefactoringTestHelperTest");
}
@@ -392,24 +422,26 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"CompilationAndBugCheckerRefactoringTestHelpersTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class CompilationAndBugCheckerRefactoringTestHelpersTest {",
" private static class TestChecker extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class CompilationAndBugCheckerRefactoringTestHelpersTest {
private static class TestChecker extends BugChecker {}
void m() {
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.addSourceLines("A.java", "// BUG: Diagnostic contains:", "class A {}")
.doTest();
BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())
.addInputLines("A.java", "class A {}")
.addOutputLines("A.java", "class A { /* This is a change. */ }")
.doTest();
}
}
""");
verifyGeneratedFileContent(
outputDirectory, "CompilationAndBugCheckerRefactoringTestHelpersTest");
@@ -421,28 +453,30 @@ final class BugPatternTestExtractorTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest.java",
"package pkg;",
"",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.CompilationTestHelper;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"final class CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest {",
" private static class CustomTestChecker extends BugChecker {}",
"",
" private static class CustomTestChecker2 extends BugChecker {}",
"",
" void m() {",
" CompilationTestHelper.newInstance(CustomTestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"// BUG: Diagnostic contains:\", \"class A {}\")",
" .doTest();",
"",
" BugCheckerRefactoringTestHelper.newInstance(CustomTestChecker2.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A { /* This is a change. */ }\")",
" .doTest();",
" }",
"}");
"""
package pkg;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
final class CompilationAndBugCheckerRefactoringTestHelpersWithCustomCheckerPackageAndNamesTest {
private static class CustomTestChecker extends BugChecker {}
private static class CustomTestChecker2 extends BugChecker {}
void m() {
CompilationTestHelper.newInstance(CustomTestChecker.class, getClass())
.addSourceLines("A.java", "// BUG: Diagnostic contains:", "class A {}")
.doTest();
BugCheckerRefactoringTestHelper.newInstance(CustomTestChecker2.class, getClass())
.addInputLines("A.java", "class A {}")
.addOutputLines("A.java", "class A { /* This is a change. */ }")
.doTest();
}
}
""");
verifyGeneratedFileContent(
outputDirectory,

View File

@@ -62,7 +62,11 @@ final class DocumentationGeneratorTaskListenerTest {
assertThatThrownBy(
() ->
Compilation.compileWithDocumentationGenerator(
outputDirectory, "A.java", "class A {}"))
outputDirectory,
"A.java",
"""
class A {}
"""))
.hasRootCauseInstanceOf(FileSystemException.class)
.hasCauseInstanceOf(IllegalStateException.class)
.hasMessageEndingWith("Error while creating directory with path '%s'", outputDirectory);
@@ -70,7 +74,10 @@ final class DocumentationGeneratorTaskListenerTest {
@Test
void noClassNoOutput(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(outputDirectory, "A.java", "package pkg;");
Compilation.compileWithDocumentationGenerator(
outputDirectory, "A.java", """
package pkg;
""");
assertThat(outputDirectory).isEmptyDirectory();
}
@@ -81,7 +88,11 @@ final class DocumentationGeneratorTaskListenerTest {
assertThatThrownBy(
() ->
Compilation.compileWithDocumentationGenerator(
actualOutputDirectory, "A.java", "package pkg;"))
actualOutputDirectory,
"A.java",
"""
package pkg;
"""))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Precisely one path must be provided");
}
@@ -91,7 +102,9 @@ final class DocumentationGeneratorTaskListenerTest {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"DocumentationGeneratorTaskListenerTestClass.java",
"class DocumentationGeneratorTaskListenerTestClass {}");
"""
class DocumentationGeneratorTaskListenerTestClass {}
""");
// XXX: Once we support only JDK 15+, use a text block for the `expected` string.
assertThat(
@@ -125,8 +138,8 @@ final class DocumentationGeneratorTaskListenerTest {
}
private static String describeTree(Tree tree) {
return (tree instanceof ClassTree)
? String.join(": ", String.valueOf(tree.getKind()), ((ClassTree) tree).getSimpleName())
return (tree instanceof ClassTree clazz)
? String.join(": ", String.valueOf(tree.getKind()), clazz.getSimpleName())
: tree.getKind().toString();
}
}

View File

@@ -1,19 +0,0 @@
{
"fullyQualifiedName": "pkg.CompleteBugChecker",
"name": "OtherName",
"altNames": [
"Check"
],
"link": "https://error-prone.picnic.tech",
"tags": [
"Simplification"
],
"summary": "CompleteBugChecker summary",
"explanation": "Example explanation",
"severityLevel": "SUGGESTION",
"canDisable": false,
"suppressionAnnotations": [
"com.google.errorprone.BugPattern",
"org.junit.jupiter.api.Test"
]
}

View File

@@ -1,14 +0,0 @@
{
"fullyQualifiedName": "pkg.MinimalBugChecker",
"name": "MinimalBugChecker",
"altNames": [],
"link": "",
"tags": [],
"summary": "MinimalBugChecker summary",
"explanation": "",
"severityLevel": "ERROR",
"canDisable": true,
"suppressionAnnotations": [
"java.lang.SuppressWarnings"
]
}

View File

@@ -1,12 +0,0 @@
{
"fullyQualifiedName": "pkg.UndocumentedSuppressionBugPattern",
"name": "UndocumentedSuppressionBugPattern",
"altNames": [],
"link": "",
"tags": [],
"summary": "UndocumentedSuppressionBugPattern summary",
"explanation": "",
"severityLevel": "WARNING",
"canDisable": true,
"suppressionAnnotations": []
}

View File

@@ -248,7 +248,6 @@
</path>
</annotationProcessorPaths>
<compilerArgs combine.children="append">
<arg>-Xplugin:RefasterRuleCompiler</arg>
<arg>-Xplugin:DocumentationGenerator -XoutputDirectory=${project.build.directory}/docs</arg>
</compilerArgs>
</configuration>

View File

@@ -18,7 +18,7 @@ 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;
import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.code.Symbol;
import java.util.Map;
import javax.lang.model.element.AnnotationValue;
@@ -46,7 +46,7 @@ public final class AmbiguousJsonCreator extends BugChecker implements Annotation
}
ClassTree clazz = state.findEnclosing(ClassTree.class);
if (clazz == null || clazz.getKind() != Tree.Kind.ENUM) {
if (clazz == null || clazz.getKind() != Kind.ENUM) {
return Description.NO_MATCH;
}

View File

@@ -19,7 +19,6 @@ import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.Tree.Kind;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -119,7 +118,7 @@ public final class CanonicalAnnotationSyntax extends BugChecker implements Annot
* the expression as a whole.
*/
ExpressionTree value =
(arg.getKind() == Kind.ASSIGNMENT) ? ((AssignmentTree) arg).getExpression() : arg;
(arg instanceof AssignmentTree assignment) ? assignment.getExpression() : arg;
/* Store a fix for each expression that was successfully simplified. */
simplifyAttributeValue(value, state)
@@ -130,13 +129,10 @@ public final class CanonicalAnnotationSyntax extends BugChecker implements Annot
}
private static Optional<String> simplifyAttributeValue(ExpressionTree expr, VisitorState state) {
if (expr.getKind() != Kind.NEW_ARRAY) {
/* There are no curly braces or commas to be dropped here. */
return Optional.empty();
}
NewArrayTree array = (NewArrayTree) expr;
return simplifySingletonArray(array, state).or(() -> dropTrailingComma(array, state));
/* Drop curly braces or commas if possible. */
return expr instanceof NewArrayTree newArray
? simplifySingletonArray(newArray, state).or(() -> dropTrailingComma(newArray, state))
: Optional.empty();
}
/** Returns the expression describing the array's sole element, if any. */

View File

@@ -98,19 +98,17 @@ public final class DirectReturn extends BugChecker implements BlockTreeMatcher {
}
private static Optional<ExpressionTree> tryMatchAssignment(Symbol targetSymbol, Tree tree) {
if (tree instanceof ExpressionStatementTree) {
return tryMatchAssignment(targetSymbol, ((ExpressionStatementTree) tree).getExpression());
if (tree instanceof ExpressionStatementTree expressionStatement) {
return tryMatchAssignment(targetSymbol, expressionStatement.getExpression());
}
if (tree instanceof AssignmentTree) {
AssignmentTree assignment = (AssignmentTree) tree;
if (tree instanceof AssignmentTree assignment) {
return targetSymbol.equals(ASTHelpers.getSymbol(assignment.getVariable()))
? Optional.of(assignment.getExpression())
: Optional.empty();
}
if (tree instanceof VariableTree) {
VariableTree declaration = (VariableTree) tree;
if (tree instanceof VariableTree declaration) {
return declaration.getModifiers().getAnnotations().isEmpty()
&& targetSymbol.equals(ASTHelpers.getSymbol(declaration))
? Optional.ofNullable(declaration.getInitializer())
@@ -148,11 +146,11 @@ public final class DirectReturn extends BugChecker implements BlockTreeMatcher {
Streams.stream(state.getPath()).skip(1),
Streams.stream(state.getPath()),
(tree, child) -> {
if (!(tree instanceof TryTree)) {
if (!(tree instanceof TryTree tryTree)) {
return null;
}
BlockTree finallyBlock = ((TryTree) tree).getFinallyBlock();
BlockTree finallyBlock = tryTree.getFinallyBlock();
return !child.equals(finallyBlock) ? finallyBlock : null;
})
.anyMatch(finallyBlock -> referencesIdentifierSymbol(symbol, finallyBlock));

View File

@@ -6,19 +6,24 @@ import static com.google.errorprone.BugPattern.StandardTags.STYLE;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.instanceMethod;
import static com.google.errorprone.matchers.Matchers.staticMethod;
import static com.sun.tools.javac.util.Position.NOPOS;
import static java.util.stream.Collectors.joining;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.errorprone.BugPattern;
import com.google.errorprone.ErrorProneFlags;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.suppliers.Supplier;
import com.google.errorprone.util.ASTHelpers;
import com.google.errorprone.util.SourceVersion;
import com.google.googlejavaformat.java.Formatter;
import com.google.googlejavaformat.java.FormatterException;
import com.google.googlejavaformat.java.ImportOrderer;
@@ -27,9 +32,12 @@ import com.google.googlejavaformat.java.RemoveUnusedImports;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.util.Position;
import com.sun.tools.javac.api.MultiTaskListener;
import com.sun.tools.javac.util.Context;
import java.util.List;
import java.util.Optional;
import javax.inject.Inject;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
/**
* A {@link BugChecker} that flags improperly formatted Error Prone test code.
@@ -45,20 +53,33 @@ import java.util.Optional;
* are not able to) remove imports that become obsolete as a result of applying their suggested
* fix(es).
*/
// XXX: Once we target JDK 17 (optionally?) suggest text block fixes.
// XXX: The check does not flag well-formatted text blocks with insufficient indentation. Cover this
// using an generic check or wait for Google Java Format support (see
// https://github.com/google/google-java-format/issues/883#issuecomment-1404336418).
// XXX: The check does not flag well-formatted text blocks with excess indentation.
// XXX: ^ Validate this claim.
// XXX: GJF guesses the line separator to be used by inspecting the source. When using text blocks
// this may cause the current unconditional use of `\n` not to be sufficient when building on
// Windows; TBD.
// XXX: Forward compatibility: ignore "malformed" code in tests that, based on an
// `@DisabledForJreRange` or `@EnableForJreRange` annotation, target a Java runtime greater than the
// current runtime.
@AutoService(BugChecker.class)
@BugPattern(
summary = "Test code should follow the Google Java style",
summary =
"Test code should follow the Google Java style (and when targeting JDK 15+ be "
+ "specified using a single text block)",
link = BUG_PATTERNS_BASE_URL + "ErrorProneTestHelperSourceFormat",
linkType = CUSTOM,
severity = SUGGESTION,
tags = STYLE)
// XXX: Drop suppression if/when the `avoidTextBlocks` field is dropped.
@SuppressWarnings("java:S2160" /* Super class equality definition suffices. */)
public final class ErrorProneTestHelperSourceFormat extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final String FLAG_AVOID_TEXT_BLOCKS =
"ErrorProneTestHelperSourceFormat:AvoidTextBlocks";
private static final Formatter FORMATTER = new Formatter();
private static final Matcher<ExpressionTree> INPUT_SOURCE_ACCEPTING_METHOD =
anyOf(
@@ -77,9 +98,31 @@ public final class ErrorProneTestHelperSourceFormat extends BugChecker
instanceMethod()
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper.ExpectOutput")
.named("addOutputLines");
private static final Supplier<Boolean> IS_JABEL_ENABLED =
VisitorState.memoize(ErrorProneTestHelperSourceFormat::isJabelEnabled);
// XXX: Proper name for this?
// XXX: Something about tabs.
private static final String TEXT_BLOCK_MARKER = "\"\"\"";
private static final String TEXT_BLOCK_LINE_SEPARATOR = "\n";
private static final String DEFAULT_TEXT_BLOCK_INDENTATION = " ".repeat(12);
private static final String METHOD_SELECT_ARGUMENT_RELATIVE_INDENTATION = " ".repeat(8);
/** Instantiates a new {@link ErrorProneTestHelperSourceFormat} instance. */
public ErrorProneTestHelperSourceFormat() {}
private final boolean avoidTextBlocks;
/** Instantiates a default {@link ErrorProneTestHelperSourceFormat} instance. */
public ErrorProneTestHelperSourceFormat() {
this(ErrorProneFlags.empty());
}
/**
* Instantiates a customized {@link ErrorProneTestHelperSourceFormat}.
*
* @param flags Any provided command line flags.
*/
@Inject
ErrorProneTestHelperSourceFormat(ErrorProneFlags flags) {
avoidTextBlocks = flags.getBoolean(FLAG_AVOID_TEXT_BLOCKS).orElse(Boolean.FALSE);
}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
@@ -95,22 +138,23 @@ public final class ErrorProneTestHelperSourceFormat extends BugChecker
return buildDescription(tree).setMessage("No source code provided").build();
}
int startPos = ASTHelpers.getStartPosition(sourceLines.get(0));
int endPos = state.getEndPosition(sourceLines.get(sourceLines.size() - 1));
/* Attempt to format the source code only if it fully consists of constant expressions. */
return getConstantSourceCode(sourceLines)
.map(source -> flagFormattingIssues(startPos, endPos, source, isOutputSource, state))
.map(source -> flagFormattingIssues(sourceLines, source, isOutputSource, state))
.orElse(Description.NO_MATCH);
}
private Description flagFormattingIssues(
int startPos, int endPos, String source, boolean retainUnusedImports, VisitorState state) {
Tree methodInvocation = state.getPath().getLeaf();
List<? extends ExpressionTree> sourceLines,
String source,
boolean retainUnusedImports,
VisitorState state) {
MethodInvocationTree methodInvocation = (MethodInvocationTree) state.getPath().getLeaf();
String formatted;
try {
formatted = formatSourceCode(source, retainUnusedImports).trim();
String gjfResult = formatSourceCode(source, retainUnusedImports);
formatted = canUseTextBlocks(sourceLines, state) ? gjfResult : gjfResult.stripTrailing();
} catch (
@SuppressWarnings("java:S1166" /* Stack trace not relevant. */)
FormatterException e) {
@@ -119,31 +163,113 @@ public final class ErrorProneTestHelperSourceFormat extends BugChecker
.build();
}
if (source.trim().equals(formatted)) {
boolean isFormatted = source.equals(formatted);
boolean hasStringLiteralMismatch = shouldUpdateStringLiteralFormat(sourceLines, state);
if (isFormatted && !hasStringLiteralMismatch) {
return Description.NO_MATCH;
}
if (startPos == Position.NOPOS || endPos == Position.NOPOS) {
/*
* We have insufficient source information to emit a fix, so we only flag the fact that the
* code isn't properly formatted.
*/
return describeMatch(methodInvocation);
}
int startPos = ASTHelpers.getStartPosition(sourceLines.get(0));
int endPos = state.getEndPosition(sourceLines.get(sourceLines.size() - 1));
boolean hasNewlineMismatch =
!isFormatted && source.stripTrailing().equals(formatted.stripTrailing());
/*
* The code isn't properly formatted; replace all lines with the properly formatted
* alternatives.
* The source code is not properly formatted and/or not specified using a single text block.
* Report the more salient of the violations, and suggest a fix if sufficient source information
* is available.
*/
return describeMatch(
methodInvocation,
SuggestedFix.replace(
startPos,
endPos,
Splitter.on(System.lineSeparator())
.splitToStream(formatted)
.map(state::getConstantExpression)
.collect(joining(", "))));
boolean isTextBlockUsageIssue = isFormatted || (hasNewlineMismatch && hasStringLiteralMismatch);
boolean canSuggestFix = startPos != NOPOS && endPos != NOPOS;
return buildDescription(methodInvocation)
.setMessage(
isTextBlockUsageIssue
? String.format(
"Test code should %sbe specified using a single text block",
avoidTextBlocks ? "not " : "")
: String.format(
"Test code should follow the Google Java style%s",
hasNewlineMismatch ? " (pay attention to trailing newlines)" : ""))
.addFix(
canSuggestFix
? SuggestedFix.replace(
startPos,
endPos,
canUseTextBlocks(sourceLines, state)
? toTextBlockExpression(methodInvocation, formatted, state)
: toLineEnumeration(formatted, state))
: SuggestedFix.emptyFix())
.build();
}
private boolean shouldUpdateStringLiteralFormat(
List<? extends ExpressionTree> sourceLines, VisitorState state) {
return canUseTextBlocks(sourceLines, state)
? (sourceLines.size() > 1 || !SourceCode.isTextBlock(sourceLines.get(0), state))
: sourceLines.stream().anyMatch(tree -> SourceCode.isTextBlock(tree, state));
}
private boolean canUseTextBlocks(List<? extends ExpressionTree> sourceLines, VisitorState state) {
return !avoidTextBlocks
&& (SourceVersion.supportsTextBlocks(state.context)
|| IS_JABEL_ENABLED.get(state)
|| sourceLines.stream().anyMatch(line -> SourceCode.isTextBlock(line, state)));
}
private static String toTextBlockExpression(
MethodInvocationTree tree, String source, VisitorState state) {
String indentation = suggestTextBlockIndentation(tree, state);
// XXX: Verify trailing """ on new line.
return TEXT_BLOCK_MARKER
+ System.lineSeparator()
+ indentation
+ source
.replace(TEXT_BLOCK_LINE_SEPARATOR, System.lineSeparator() + indentation)
.replace("\\", "\\\\")
.replace(TEXT_BLOCK_MARKER, "\"\"\\\"")
+ TEXT_BLOCK_MARKER;
}
private static String toLineEnumeration(String source, VisitorState state) {
return Splitter.on(TEXT_BLOCK_LINE_SEPARATOR)
.splitToStream(source)
.map(state::getConstantExpression)
.collect(joining(", "));
}
// XXX: This makes certain assumptions; document these.
private static String suggestTextBlockIndentation(
MethodInvocationTree target, VisitorState state) {
CharSequence sourceCode = state.getSourceCode();
if (sourceCode == null) {
return DEFAULT_TEXT_BLOCK_INDENTATION;
}
String source = sourceCode.toString();
return getIndentation(target.getArguments().get(1), source)
.or(() -> getIndentation(target.getArguments().get(0), source))
.or(
() ->
getIndentation(target.getMethodSelect(), source)
.map(METHOD_SELECT_ARGUMENT_RELATIVE_INDENTATION::concat))
.orElse(DEFAULT_TEXT_BLOCK_INDENTATION);
}
private static Optional<String> getIndentation(Tree tree, String source) {
int startPos = ASTHelpers.getStartPosition(tree);
if (startPos == NOPOS) {
return Optional.empty();
}
int finalNewLine = source.lastIndexOf(System.lineSeparator(), startPos);
if (finalNewLine < 0) {
return Optional.empty();
}
return Optional.of(source.substring(finalNewLine + 1, startPos))
.filter(CharMatcher.whitespace()::matchesAllOf);
}
private static String formatSourceCode(String source, boolean retainUnusedImports)
@@ -162,14 +288,29 @@ public final class ErrorProneTestHelperSourceFormat extends BugChecker
StringBuilder source = new StringBuilder();
for (ExpressionTree sourceLine : sourceLines) {
if (source.length() > 0) {
source.append(TEXT_BLOCK_LINE_SEPARATOR);
}
String value = ASTHelpers.constValue(sourceLine, String.class);
if (value == null) {
return Optional.empty();
}
source.append(value).append(System.lineSeparator());
source.append(value);
}
return Optional.of(source.toString());
}
/**
* Tells whether Jabel appears to be enabled, indicating that text blocks are supported, even if
* may <em>appear</em> that they {@link SourceVersion#supportsTextBlocks(Context) aren't}.
*
* @see <a href="https://github.com/bsideup/jabel">Jabel</a>
*/
private static boolean isJabelEnabled(VisitorState state) {
return MultiTaskListener.instance(state.context).getTaskListeners().stream()
.anyMatch(listener -> listener.toString().contains("com.github.bsideup.jabel"));
}
}

View File

@@ -50,8 +50,9 @@ import reactor.core.publisher.Flux;
@AutoService(BugChecker.class)
@BugPattern(
summary =
"`Flux#flatMap` and `Flux#flatMapSequential` have subtle semantics; "
+ "please use `Flux#concatMap` or explicitly specify the desired amount of concurrency",
"""
`Flux#flatMap` and `Flux#flatMapSequential` have subtle semantics; please use \
`Flux#concatMap` or explicitly specify the desired amount of concurrency""",
link = BUG_PATTERNS_BASE_URL + "FluxFlatMapUsage",
linkType = CUSTOM,
severity = ERROR,

View File

@@ -242,8 +242,8 @@ public final class FormatStringConcatenation extends BugChecker
}
private void appendExpression(Tree tree) {
if (tree instanceof LiteralTree) {
formatString.append(((LiteralTree) tree).getValue());
if (tree instanceof LiteralTree literal) {
formatString.append(literal.getValue());
} else {
formatString.append(formatSpecifier);
formatArguments.add(tree);

View File

@@ -114,8 +114,9 @@ public final class IdentityConversion extends BugChecker implements MethodInvoca
return buildDescription(tree)
.setMessage(
"This method invocation appears redundant; remove it or suppress this warning and "
+ "add a comment explaining its purpose")
"""
This method invocation appears redundant; remove it or suppress this warning and add a \
comment explaining its purpose""")
.addFix(SuggestedFix.replace(tree, SourceCode.treeToString(sourceTree, state)))
.addFix(SuggestedFixes.addSuppressWarnings(state, canonicalName()))
.build();

View File

@@ -43,8 +43,9 @@ import javax.lang.model.element.Modifier;
@AutoService(BugChecker.class)
@BugPattern(
summary =
"`SortedSet` properties of a `@Value.Immutable` or `@Value.Modifiable` type must be "
+ "annotated with `@Value.NaturalOrder` or `@Value.ReverseOrder`",
"""
`SortedSet` properties of a `@Value.Immutable` or `@Value.Modifiable` type must be \
annotated with `@Value.NaturalOrder` or `@Value.ReverseOrder`""",
link = BUG_PATTERNS_BASE_URL + "ImmutablesSortedSetComparator",
linkType = CUSTOM,
severity = ERROR,

View File

@@ -16,7 +16,6 @@ import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.InstanceOfTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.VariableTree;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
@@ -42,12 +41,12 @@ public final class IsInstanceLambdaUsage extends BugChecker implements LambdaExp
@Override
public Description matchLambdaExpression(LambdaExpressionTree tree, VisitorState state) {
if (tree.getParameters().size() != 1 || tree.getBody().getKind() != Kind.INSTANCE_OF) {
if (tree.getParameters().size() != 1
|| !(tree.getBody() instanceof InstanceOfTree instanceOf)) {
return Description.NO_MATCH;
}
VariableTree param = Iterables.getOnlyElement(tree.getParameters());
InstanceOfTree instanceOf = (InstanceOfTree) tree.getBody();
if (!ASTHelpers.getSymbol(param).equals(ASTHelpers.getSymbol(instanceOf.getExpression()))) {
return Description.NO_MATCH;
}

View File

@@ -257,8 +257,8 @@ public final class JUnitValueSource extends BugChecker implements MethodTreeMatc
arguments.stream()
.map(
arg ->
arg instanceof MethodInvocationTree
? Iterables.getOnlyElement(((MethodInvocationTree) arg).getArguments())
arg instanceof MethodInvocationTree methodInvocation
? Iterables.getOnlyElement(methodInvocation.getArguments())
: arg)
.map(argument -> SourceCode.treeToString(argument, state))
.collect(joining(", ")))
@@ -268,16 +268,12 @@ public final class JUnitValueSource extends BugChecker implements MethodTreeMatc
private static String toValueSourceAttributeName(Type type) {
String typeString = type.tsym.name.toString();
switch (typeString) {
case "Class":
return "classes";
case "Character":
return "chars";
case "Integer":
return "ints";
default:
return typeString.toLowerCase(Locale.ROOT) + 's';
}
return switch (typeString) {
case "Class" -> "classes";
case "Character" -> "chars";
case "Integer" -> "ints";
default -> typeString.toLowerCase(Locale.ROOT) + 's';
};
}
private static <T> Optional<T> getElementIfSingleton(Collection<T> collection) {
@@ -289,11 +285,10 @@ public final class JUnitValueSource extends BugChecker implements MethodTreeMatc
private static Matcher<ExpressionTree> isSingleDimensionArrayCreationWithAllElementsMatching(
Matcher<? super ExpressionTree> elementMatcher) {
return (tree, state) -> {
if (!(tree instanceof NewArrayTree)) {
if (!(tree instanceof NewArrayTree newArray)) {
return false;
}
NewArrayTree newArray = (NewArrayTree) tree;
return newArray.getDimensions().isEmpty()
&& !newArray.getInitializers().isEmpty()
&& newArray.getInitializers().stream()

View File

@@ -31,7 +31,6 @@ import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.PrimitiveTypeTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.util.TreeScanner;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
@@ -122,13 +121,9 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
}
private static Optional<NewArrayTree> extractArray(ExpressionTree expr) {
if (expr.getKind() == Kind.ASSIGNMENT) {
return extractArray(((AssignmentTree) expr).getExpression());
}
return Optional.of(expr)
.filter(e -> e.getKind() == Kind.NEW_ARRAY)
.map(NewArrayTree.class::cast);
return expr instanceof AssignmentTree assignment
? extractArray(assignment.getExpression())
: Optional.of(expr).filter(NewArrayTree.class::isInstance).map(NewArrayTree.class::cast);
}
private static Optional<SuggestedFix.Builder> suggestSorting(
@@ -200,8 +195,8 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
public @Nullable Void visitLiteral(LiteralTree node, @Nullable Void unused) {
Object value = ASTHelpers.constValue(node);
nodes.add(
value instanceof String
? STRING_ARGUMENT_SPLITTER.splitToStream((String) value).collect(toImmutableList())
value instanceof String str
? STRING_ARGUMENT_SPLITTER.splitToStream(str).collect(toImmutableList())
: ImmutableList.of(String.valueOf(value)));
return super.visitLiteral(node, unused);

View File

@@ -27,7 +27,6 @@ import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.ReturnTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Type;
@@ -85,22 +84,19 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
.orElse(Description.NO_MATCH);
}
// XXX: Use switch pattern matching once the targeted JDK supports this.
private static Optional<SuggestedFix.Builder> constructMethodRef(
LambdaExpressionTree lambdaExpr, Tree subTree) {
switch (subTree.getKind()) {
case BLOCK:
return constructMethodRef(lambdaExpr, (BlockTree) subTree);
case EXPRESSION_STATEMENT:
return constructMethodRef(lambdaExpr, ((ExpressionStatementTree) subTree).getExpression());
case METHOD_INVOCATION:
return constructMethodRef(lambdaExpr, (MethodInvocationTree) subTree);
case PARENTHESIZED:
return constructMethodRef(lambdaExpr, ((ParenthesizedTree) subTree).getExpression());
case RETURN:
return constructMethodRef(lambdaExpr, ((ReturnTree) subTree).getExpression());
default:
return Optional.empty();
}
return switch (subTree.getKind()) {
case BLOCK -> constructMethodRef(lambdaExpr, (BlockTree) subTree);
case EXPRESSION_STATEMENT -> constructMethodRef(
lambdaExpr, ((ExpressionStatementTree) subTree).getExpression());
case METHOD_INVOCATION -> constructMethodRef(lambdaExpr, (MethodInvocationTree) subTree);
case PARENTHESIZED -> constructMethodRef(
lambdaExpr, ((ParenthesizedTree) subTree).getExpression());
case RETURN -> constructMethodRef(lambdaExpr, ((ReturnTree) subTree).getExpression());
default -> Optional.empty();
};
}
private static Optional<SuggestedFix.Builder> constructMethodRef(
@@ -118,33 +114,35 @@ 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. */)
// XXX: Review whether to use switch pattern matching once the targeted JDK supports this.
private static Optional<SuggestedFix.Builder> constructMethodRef(
LambdaExpressionTree lambdaExpr,
MethodInvocationTree subTree,
Optional<Name> expectedInstance) {
ExpressionTree methodSelect = subTree.getMethodSelect();
switch (methodSelect.getKind()) {
case IDENTIFIER:
if (expectedInstance.isPresent()) {
/* Direct method call; there is no matching "implicit parameter". */
return Optional.empty();
}
Symbol sym = ASTHelpers.getSymbol(methodSelect);
return ASTHelpers.isStatic(sym)
? constructFix(lambdaExpr, sym.owner, methodSelect)
: constructFix(lambdaExpr, "this", methodSelect);
case MEMBER_SELECT:
return constructMethodRef(lambdaExpr, (MemberSelectTree) methodSelect, expectedInstance);
default:
throw new VerifyException("Unexpected type of expression: " + methodSelect.getKind());
if (methodSelect instanceof IdentifierTree) {
if (expectedInstance.isPresent()) {
/* Direct method call; there is no matching "implicit parameter". */
return Optional.empty();
}
Symbol sym = ASTHelpers.getSymbol(methodSelect);
return ASTHelpers.isStatic(sym)
? constructFix(lambdaExpr, sym.owner, methodSelect)
: constructFix(lambdaExpr, "this", methodSelect);
}
if (methodSelect instanceof MemberSelectTree memberSelect) {
return constructMethodRef(lambdaExpr, memberSelect, expectedInstance);
}
throw new VerifyException("Unexpected type of expression: " + methodSelect.getKind());
}
private static Optional<SuggestedFix.Builder> constructMethodRef(
LambdaExpressionTree lambdaExpr, MemberSelectTree subTree, Optional<Name> expectedInstance) {
if (subTree.getExpression().getKind() != Kind.IDENTIFIER) {
if (!(subTree.getExpression() instanceof IdentifierTree identifier)) {
// XXX: Could be parenthesized. Handle. Also in other classes.
/*
* Only suggest a replacement if the method select's expression provably doesn't have
@@ -153,12 +151,12 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
return Optional.empty();
}
Name lhs = ((IdentifierTree) subTree.getExpression()).getName();
Name lhs = identifier.getName();
if (expectedInstance.isEmpty()) {
return constructFix(lambdaExpr, lhs, subTree.getIdentifier());
}
Type lhsType = ASTHelpers.getType(subTree.getExpression());
Type lhsType = ASTHelpers.getType(identifier);
if (lhsType == null || !expectedInstance.orElseThrow().equals(lhs)) {
return Optional.empty();
}
@@ -183,8 +181,8 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
for (int i = 0; i < args.size(); i++) {
ExpressionTree arg = args.get(i);
if (arg.getKind() != Kind.IDENTIFIER
|| !((IdentifierTree) arg).getName().equals(expectedArguments.get(i + diff))) {
if (!(arg instanceof IdentifierTree identifier)
|| !identifier.getName().equals(expectedArguments.get(i + diff))) {
return Optional.empty();
}
}

View File

@@ -67,20 +67,17 @@ public final class MockitoMockClassReference extends BugChecker
return describeMatch(tree, SuggestedFixes.removeElement(arguments.get(0), arguments, state));
}
// XXX: Use switch pattern matching once the targeted JDK supports this.
private static boolean isTypeDerivableFromContext(MethodInvocationTree tree, VisitorState state) {
Tree parent = state.getPath().getParentPath().getLeaf();
switch (parent.getKind()) {
case VARIABLE:
return !ASTHelpers.hasImplicitType((VariableTree) parent, state)
&& MoreASTHelpers.areSameType(tree, parent, state);
case ASSIGNMENT:
return MoreASTHelpers.areSameType(tree, parent, state);
case RETURN:
return MoreASTHelpers.findMethodExitedOnReturn(state)
.filter(m -> MoreASTHelpers.areSameType(tree, m.getReturnType(), state))
.isPresent();
default:
return false;
}
return switch (parent.getKind()) {
case VARIABLE -> !ASTHelpers.hasImplicitType((VariableTree) parent, state)
&& MoreASTHelpers.areSameType(tree, parent, state);
case ASSIGNMENT -> MoreASTHelpers.areSameType(tree, parent, state);
case RETURN -> MoreASTHelpers.findMethodExitedOnReturn(state)
.filter(m -> MoreASTHelpers.areSameType(tree, m.getReturnType(), state))
.isPresent();
default -> false;
};
}
}

View File

@@ -34,8 +34,9 @@ import com.sun.tools.javac.code.Type;
@AutoService(BugChecker.class)
@BugPattern(
summary =
"Avoid `Publisher`s that emit other `Publishers`s; "
+ "the resultant code is hard to reason about",
"""
Avoid `Publisher`s that emit other `Publishers`s; the resultant code is hard to reason \
about""",
link = BUG_PATTERNS_BASE_URL + "NestedPublishers",
linkType = CUSTOM,
severity = WARNING,

View File

@@ -159,10 +159,9 @@ public final class NonStaticImport extends BugChecker implements CompilationUnit
ImmutableTable.builder();
for (ImportTree importTree : tree.getImports()) {
Tree qualifiedIdentifier = importTree.getQualifiedIdentifier();
if (importTree.isStatic() && qualifiedIdentifier instanceof MemberSelectTree) {
MemberSelectTree memberSelectTree = (MemberSelectTree) qualifiedIdentifier;
String type = SourceCode.treeToString(memberSelectTree.getExpression(), state);
String member = memberSelectTree.getIdentifier().toString();
if (importTree.isStatic() && qualifiedIdentifier instanceof MemberSelectTree memberSelect) {
String type = SourceCode.treeToString(memberSelect.getExpression(), state);
String member = memberSelect.getIdentifier().toString();
if (shouldNotBeStaticallyImported(type, member)) {
imports.put(
type,

View File

@@ -21,6 +21,7 @@ import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
@@ -43,8 +44,9 @@ import tech.picnic.errorprone.bugpatterns.util.SourceCode;
@AutoService(BugChecker.class)
@BugPattern(
summary =
"Ensure invocations of `Comparator#comparing{,Double,Int,Long}` match the return type"
+ " of the provided function",
"""
Ensure invocations of `Comparator#comparing{,Double,Int,Long}` match the return type of \
the provided function""",
link = BUG_PATTERNS_BASE_URL + "PrimitiveComparison",
linkType = CUSTOM,
severity = WARNING,
@@ -146,37 +148,42 @@ public final class PrimitiveComparison extends BugChecker implements MethodInvoc
return isStatic ? "comparing" : "thenComparing";
}
// XXX: Use switch pattern matching once the targeted JDK supports this.
private static Optional<Type> getPotentiallyBoxedReturnType(ExpressionTree tree) {
switch (tree.getKind()) {
case LAMBDA_EXPRESSION:
/* Return the lambda expression's actual return type. */
return Optional.ofNullable(ASTHelpers.getType(((LambdaExpressionTree) tree).getBody()));
case MEMBER_REFERENCE:
/* Return the method's declared return type. */
// XXX: Very fragile. Do better.
Type subType2 = ((JCMemberReference) tree).referentType;
return Optional.of(subType2.getReturnType());
default:
/* This appears to be a genuine `{,ToInt,ToLong,ToDouble}Function`. */
return Optional.empty();
if (tree instanceof LambdaExpressionTree lambdaExpression) {
/* Return the lambda expression's actual return type. */
return Optional.ofNullable(ASTHelpers.getType(lambdaExpression.getBody()));
}
// XXX: The match against a concrete type and reference to one of its fields is fragile. Do
// better.
if (tree instanceof JCMemberReference memberReference) {
/* Return the method's declared return type. */
Type subType = memberReference.referentType;
return Optional.of(subType.getReturnType());
}
/* This appears to be a genuine `{,ToInt,ToLong,ToDouble}Function`. */
return Optional.empty();
}
private static Fix suggestFix(
MethodInvocationTree tree, String preferredMethodName, VisitorState state) {
ExpressionTree expr = tree.getMethodSelect();
switch (expr.getKind()) {
case IDENTIFIER:
return SuggestedFix.builder()
.addStaticImport(Comparator.class.getName() + '.' + preferredMethodName)
.replace(expr, preferredMethodName)
.build();
case MEMBER_SELECT:
MemberSelectTree ms = (MemberSelectTree) tree.getMethodSelect();
return SuggestedFix.replace(
ms, SourceCode.treeToString(ms.getExpression(), state) + '.' + preferredMethodName);
default:
throw new VerifyException("Unexpected type of expression: " + expr.getKind());
if (expr instanceof IdentifierTree) {
return SuggestedFix.builder()
.addStaticImport(Comparator.class.getName() + '.' + preferredMethodName)
.replace(expr, preferredMethodName)
.build();
}
if (expr instanceof MemberSelectTree memberSelect) {
return SuggestedFix.replace(
memberSelect,
SourceCode.treeToString(memberSelect.getExpression(), state) + '.' + preferredMethodName);
}
throw new VerifyException("Unexpected type of expression: " + expr.getKind());
}
}

View File

@@ -327,36 +327,31 @@ public final class RedundantStringConversion extends BugChecker
}
private Optional<ExpressionTree> trySimplify(ExpressionTree tree, VisitorState state) {
if (tree.getKind() != Kind.METHOD_INVOCATION) {
if (!(tree instanceof MethodInvocationTree methodInvocation)) {
return Optional.empty();
}
MethodInvocationTree methodInvocation = (MethodInvocationTree) tree;
if (!conversionMethodMatcher.matches(methodInvocation, state)) {
return Optional.empty();
}
switch (methodInvocation.getArguments().size()) {
case 0:
return trySimplifyNullaryMethod(methodInvocation, state);
case 1:
return trySimplifyUnaryMethod(methodInvocation, state);
default:
throw new IllegalStateException(
"Cannot simplify method call with two or more arguments: "
+ SourceCode.treeToString(tree, state));
}
return switch (methodInvocation.getArguments().size()) {
case 0 -> trySimplifyNullaryMethod(methodInvocation, state);
case 1 -> trySimplifyUnaryMethod(methodInvocation, state);
default -> throw new IllegalStateException(
"Cannot simplify method call with two or more arguments: "
+ SourceCode.treeToString(tree, state));
};
}
private static Optional<ExpressionTree> trySimplifyNullaryMethod(
MethodInvocationTree methodInvocation, VisitorState state) {
if (!instanceMethod().matches(methodInvocation, state)) {
if (!instanceMethod().matches(methodInvocation, state)
|| !(methodInvocation.getMethodSelect() instanceof MemberSelectTree memberSelect)) {
return Optional.empty();
}
return Optional.of(methodInvocation.getMethodSelect())
.filter(methodSelect -> methodSelect.getKind() == Kind.MEMBER_SELECT)
.map(methodSelect -> ((MemberSelectTree) methodSelect).getExpression())
return Optional.of(memberSelect.getExpression())
.filter(expr -> !"super".equals(SourceCode.treeToString(expr, state)));
}

View File

@@ -42,21 +42,18 @@ public final class RefasterAnyOfUsage extends BugChecker implements MethodInvoca
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (REFASTER_ANY_OF.matches(tree, state)) {
switch (tree.getArguments().size()) {
case 0:
// We can't safely fix this case; dropping the expression may produce non-compilable code.
return describeMatch(tree);
case 1:
return describeMatch(
tree,
SuggestedFix.replace(
tree, SourceCode.treeToString(tree.getArguments().get(0), state)));
default:
/* Handled below. */
}
int argumentCount = tree.getArguments().size();
if (argumentCount > 1 || !REFASTER_ANY_OF.matches(tree, state)) {
return Description.NO_MATCH;
}
return Description.NO_MATCH;
if (argumentCount == 0) {
/* We can't safely fix this case; dropping the expression may produce non-compilable code. */
return describeMatch(tree);
}
return describeMatch(
tree,
SuggestedFix.replace(tree, SourceCode.treeToString(tree.getArguments().get(0), state)));
}
}

View File

@@ -99,9 +99,10 @@ 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

@@ -1,6 +1,5 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.common.base.Verify.verify;
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
@@ -24,7 +23,6 @@ import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.Tree.Kind;
import java.util.Optional;
import tech.picnic.errorprone.bugpatterns.util.AnnotationAttributeMatcher;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
@@ -79,31 +77,25 @@ public final class SpringMvcAnnotation extends BugChecker implements AnnotationT
}
private static Optional<String> extractUniqueMethod(ExpressionTree arg, VisitorState state) {
verify(
arg.getKind() == Kind.ASSIGNMENT,
"Annotation attribute is not an assignment: %s",
arg.getKind());
ExpressionTree expr = ((AssignmentTree) arg).getExpression();
if (expr.getKind() != Kind.NEW_ARRAY) {
return Optional.of(extractMethod(expr, state));
if (!(arg instanceof AssignmentTree assignment)) {
throw new VerifyException("Annotation attribute is not an assignment:" + arg.getKind());
}
NewArrayTree newArray = (NewArrayTree) expr;
return Optional.of(newArray.getInitializers())
.filter(args -> args.size() == 1)
.map(args -> extractMethod(args.get(0), state));
ExpressionTree expr = assignment.getExpression();
return expr instanceof NewArrayTree newArray
? Optional.of(newArray.getInitializers())
.filter(args -> args.size() == 1)
.map(args -> extractMethod(args.get(0), state))
: Optional.of(extractMethod(expr, state));
}
// XXX: Use switch pattern matching once the targeted JDK supports this.
private static String extractMethod(ExpressionTree expr, VisitorState state) {
switch (expr.getKind()) {
case IDENTIFIER:
return SourceCode.treeToString(expr, state);
case MEMBER_SELECT:
return ((MemberSelectTree) expr).getIdentifier().toString();
default:
throw new VerifyException("Unexpected type of expression: " + expr.getKind());
}
return switch (expr.getKind()) {
case IDENTIFIER -> SourceCode.treeToString(expr, state);
case MEMBER_SELECT -> ((MemberSelectTree) expr).getIdentifier().toString();
default -> throw new VerifyException("Unexpected type of expression: " + expr.getKind());
};
}
private static Fix replaceAnnotation(

View File

@@ -26,6 +26,7 @@ import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.code.Type;
import java.util.Optional;
@@ -176,15 +177,10 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa
Tree parentTree =
requireNonNull(state.getPath().getParentPath(), "MemberSelectTree lacks enclosing node")
.getLeaf();
switch (parentTree.getKind()) {
case IMPORT:
case MEMBER_SELECT:
return false;
case METHOD_INVOCATION:
return ((MethodInvocationTree) parentTree).getTypeArguments().isEmpty();
default:
return true;
}
return parentTree instanceof MethodInvocationTree methodInvocation
? methodInvocation.getTypeArguments().isEmpty()
: (parentTree.getKind() != Kind.IMPORT && parentTree.getKind() != Kind.MEMBER_SELECT);
}
private static boolean isCandidate(MemberSelectTree tree) {

View File

@@ -9,7 +9,6 @@ import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.code.Type;
import java.io.Serializable;
import java.util.HashSet;
@@ -116,8 +115,8 @@ public final class AnnotationAttributeMatcher implements Serializable {
}
private static String extractAttributeName(ExpressionTree expr) {
return (expr.getKind() == Kind.ASSIGNMENT)
? ASTHelpers.getSymbol(((AssignmentTree) expr).getVariable()).getSimpleName().toString()
return (expr instanceof AssignmentTree assignment)
? ASTHelpers.getSymbol(assignment.getVariable()).getSimpleName().toString()
: "value";
}

View File

@@ -99,14 +99,13 @@ public final class MoreJUnitMatchers {
String methodName = method.getName().toString();
ExpressionTree value = AnnotationMatcherUtils.getArgument(methodSourceAnnotation, "value");
if (!(value instanceof NewArrayTree)) {
if (!(value instanceof NewArrayTree newArray)) {
return ImmutableList.of(toMethodSourceFactoryDescriptor(value, methodName));
}
return ((NewArrayTree) value)
.getInitializers().stream()
.map(name -> toMethodSourceFactoryDescriptor(name, methodName))
.collect(toImmutableList());
return newArray.getInitializers().stream()
.map(name -> toMethodSourceFactoryDescriptor(name, methodName))
.collect(toImmutableList());
}
private static String toMethodSourceFactoryDescriptor(

View File

@@ -12,6 +12,7 @@ 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.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
@@ -26,8 +27,30 @@ public final class SourceCode {
/** The complement of {@link CharMatcher#whitespace()}. */
private static final CharMatcher NON_WHITESPACE_MATCHER = CharMatcher.whitespace().negate();
// XXX: Proper name for this?
private static final String TEXT_BLOCK_MARKER = "\"\"\"";
private SourceCode() {}
/**
* Tells whether the given expression is a text block.
*
* @param tree The AST node of interest.
* @param state A {@link VisitorState} describing the context in which the given {@link
* ExpressionTree} is found.
* @return {@code true} iff the given expression is a text block.
*/
// XXX: Add tests!
public static boolean isTextBlock(ExpressionTree tree, VisitorState state) {
if (tree.getKind() != Tree.Kind.STRING_LITERAL) {
return false;
}
/* If the source code is unavailable then we assume that this literal is _not_ a text block. */
String src = state.getSourceForNode(tree);
return src != null && src.startsWith(TEXT_BLOCK_MARKER);
}
/**
* Returns a string representation of the given {@link Tree}, preferring the original source code
* (if available) over its prettified representation.

View File

@@ -75,8 +75,12 @@ public enum ThirdPartyLibrary {
*
* <p>The {@link VisitorState}'s symbol table is consulted first. If the type has not yet been
* loaded, then an attempt is made to do so.
*
* @param className The type of interest.
* @param state The context under consideration.
* @return {@code true} iff the indicated type is on the classpath.
*/
private static boolean isKnownClass(String className, VisitorState state) {
public static boolean isKnownClass(String className, VisitorState state) {
return state.getTypeFromString(className) != null || canLoadClass(className, state);
}

View File

@@ -29,7 +29,9 @@ 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.
// XXX: Now that we build with JDK 15+, this rule can be generalized to cover all `CharSequence`
// subtypes. This does require a mechanism (perhaps an annotation, or a separate Maven module) to
// make sure that non-String expressions are rewritten only if client code also targets JDK 15+.
static final class StringIsEmpty {
@BeforeTemplate
boolean before(String str) {
@@ -44,7 +46,9 @@ 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: Now that we build with JDK 15+, this rule can be generalized to cover all `CharSequence`
// subtypes. However, `CharSequence::isEmpty` isn't as nice as `String::isEmpty`, so we might want
// to introduce a rule that suggests `String::isEmpty` where possible.
// 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 {
@@ -60,7 +64,9 @@ 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: Now that we build with JDK 15+, this rule can be generalized to cover all `CharSequence`
// subtypes. However, `CharSequence::isEmpty` isn't as nice as `String::isEmpty`, so we might want
// to introduce a rule that suggests `String::isEmpty` where possible.
static final class StringIsNotEmptyPredicate {
@BeforeTemplate
Predicate<String> before() {

View File

@@ -13,99 +13,101 @@ final class AmbiguousJsonCreatorTest {
"X", m -> m.contains("`JsonCreator.Mode` should be set for single-argument creators"))
.addSourceLines(
"Container.java",
"import com.fasterxml.jackson.annotation.JsonCreator;",
"import com.fasterxml.jackson.annotation.JsonValue;",
"",
"interface Container {",
" enum A {",
" FOO(1);",
"",
" private final int i;",
"",
" A(int i) {",
" this.i = i;",
" }",
"",
" // BUG: Diagnostic matches: X",
" @JsonCreator",
" public static A of(int i) {",
" return FOO;",
" }",
" }",
"",
" enum B {",
" FOO(1);",
"",
" private final int i;",
"",
" B(int i) {",
" this.i = i;",
" }",
"",
" @JsonCreator(mode = JsonCreator.Mode.DELEGATING)",
" public static B of(int i) {",
" return FOO;",
" }",
" }",
"",
" enum C {",
" FOO(1, \"s\");",
"",
" @JsonValue private final int i;",
" private final String s;",
"",
" C(int i, String s) {",
" this.i = i;",
" this.s = s;",
" }",
"",
" // BUG: Diagnostic matches: X",
" @JsonCreator",
" public static C of(int i) {",
" return FOO;",
" }",
" }",
"",
" enum D {",
" FOO(1, \"s\");",
"",
" private final int i;",
" private final String s;",
"",
" D(int i, String s) {",
" this.i = i;",
" this.s = s;",
" }",
"",
" @JsonCreator",
" public static D of(int i, String s) {",
" return FOO;",
" }",
" }",
"",
" enum E {",
" FOO;",
"",
" // BUG: Diagnostic matches: X",
" @JsonCreator",
" public static E of(String s) {",
" return FOO;",
" }",
" }",
"",
" class F {",
" private final String s;",
"",
" F(String s) {",
" this.s = s;",
" }",
"",
" @JsonCreator",
" public static F of(String s) {",
" return new F(s);",
" }",
" }",
"}")
"""
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
interface Container {
enum A {
FOO(1);
private final int i;
A(int i) {
this.i = i;
}
// BUG: Diagnostic matches: X
@JsonCreator
public static A of(int i) {
return FOO;
}
}
enum B {
FOO(1);
private final int i;
B(int i) {
this.i = i;
}
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
public static B of(int i) {
return FOO;
}
}
enum C {
FOO(1, "s");
@JsonValue private final int i;
private final String s;
C(int i, String s) {
this.i = i;
this.s = s;
}
// BUG: Diagnostic matches: X
@JsonCreator
public static C of(int i) {
return FOO;
}
}
enum D {
FOO(1, "s");
private final int i;
private final String s;
D(int i, String s) {
this.i = i;
this.s = s;
}
@JsonCreator
public static D of(int i, String s) {
return FOO;
}
}
enum E {
FOO;
// BUG: Diagnostic matches: X
@JsonCreator
public static E of(String s) {
return FOO;
}
}
class F {
private final String s;
F(String s) {
this.s = s;
}
@JsonCreator
public static F of(String s) {
return new F(s);
}
}
}
""")
.doTest();
}
@@ -114,28 +116,32 @@ final class AmbiguousJsonCreatorTest {
BugCheckerRefactoringTestHelper.newInstance(AmbiguousJsonCreator.class, getClass())
.addInputLines(
"A.java",
"import com.fasterxml.jackson.annotation.JsonCreator;",
"",
"enum A {",
" FOO;",
"",
" @JsonCreator",
" public static A of(String s) {",
" return FOO;",
" }",
"}")
"""
import com.fasterxml.jackson.annotation.JsonCreator;
enum A {
FOO;
@JsonCreator
public static A of(String s) {
return FOO;
}
}
""")
.addOutputLines(
"A.java",
"import com.fasterxml.jackson.annotation.JsonCreator;",
"",
"enum A {",
" FOO;",
"",
" @JsonCreator(mode = JsonCreator.Mode.DELEGATING)",
" public static A of(String s) {",
" return FOO;",
" }",
"}")
"""
import com.fasterxml.jackson.annotation.JsonCreator;
enum A {
FOO;
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
public static A of(String s) {
return FOO;
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,22 +11,24 @@ final class AssertJIsNullTest {
CompilationTestHelper.newInstance(AssertJIsNull.class, getClass())
.addSourceLines(
"A.java",
"import static org.assertj.core.api.Assertions.assertThat;",
"",
"class A {",
" void m() {",
" assertThat(1).isEqualTo(1);",
" // BUG: Diagnostic contains:",
" assertThat(1).isEqualTo(null);",
" // BUG: Diagnostic contains:",
" assertThat(\"foo\").isEqualTo(null);",
" isEqualTo(null);",
" }",
"",
" private boolean isEqualTo(Object value) {",
" return value.equals(\"bar\");",
" }",
"}")
"""
import static org.assertj.core.api.Assertions.assertThat;
class A {
void m() {
assertThat(1).isEqualTo(1);
// BUG: Diagnostic contains:
assertThat(1).isEqualTo(null);
// BUG: Diagnostic contains:
assertThat("foo").isEqualTo(null);
isEqualTo(null);
}
private boolean isEqualTo(Object value) {
return value.equals("bar");
}
}
""")
.doTest();
}
@@ -35,24 +37,28 @@ final class AssertJIsNullTest {
BugCheckerRefactoringTestHelper.newInstance(AssertJIsNull.class, getClass())
.addInputLines(
"A.java",
"import static org.assertj.core.api.Assertions.assertThat;",
"",
"class A {",
" void m() {",
" assertThat(1).isEqualTo(null);",
" assertThat(\"foo\").isEqualTo(null);",
" }",
"}")
"""
import static org.assertj.core.api.Assertions.assertThat;
class A {
void m() {
assertThat(1).isEqualTo(null);
assertThat("foo").isEqualTo(null);
}
}
""")
.addOutputLines(
"A.java",
"import static org.assertj.core.api.Assertions.assertThat;",
"",
"class A {",
" void m() {",
" assertThat(1).isNull();",
" assertThat(\"foo\").isNull();",
" }",
"}")
"""
import static org.assertj.core.api.Assertions.assertThat;
class A {
void m() {
assertThat(1).isNull();
assertThat("foo").isNull();
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,44 +11,46 @@ final class AssociativeMethodInvocationTest {
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);",
" }",
"}")
"""
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();
}
@@ -57,54 +59,58 @@ final class AssociativeMethodInvocationTest {
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());",
" }",
"}")
"""
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());",
" }",
"}")
"""
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

@@ -11,56 +11,58 @@ final class AutowiredConstructorTest {
CompilationTestHelper.newInstance(AutowiredConstructor.class, getClass())
.addSourceLines(
"Container.java",
"import com.google.errorprone.annotations.Immutable;",
"import java.util.List;",
"import org.springframework.beans.factory.annotation.Autowired;",
"",
"interface Container {",
" @Immutable",
" class A {",
" A() {}",
" }",
"",
" class B {",
" @Autowired",
" void setProperty(Object o) {}",
" }",
"",
" class C {",
" // BUG: Diagnostic contains:",
" @Autowired",
" C() {}",
" }",
"",
" class D {",
" // BUG: Diagnostic contains:",
" @Autowired",
" D(String x) {}",
" }",
"",
" class E {",
" @Autowired",
" E() {}",
"",
" E(String x) {}",
" }",
"",
" class F {",
" F() {}",
"",
" @Autowired",
" F(String x) {}",
" }",
"",
" class G {",
" @Autowired private Object o;",
" }",
"",
" class H {",
" @SafeVarargs",
" H(List<String>... lists) {}",
" }",
"}")
"""
import com.google.errorprone.annotations.Immutable;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
interface Container {
@Immutable
class A {
A() {}
}
class B {
@Autowired
void setProperty(Object o) {}
}
class C {
// BUG: Diagnostic contains:
@Autowired
C() {}
}
class D {
// BUG: Diagnostic contains:
@Autowired
D(String x) {}
}
class E {
@Autowired
E() {}
E(String x) {}
}
class F {
F() {}
@Autowired
F(String x) {}
}
class G {
@Autowired private Object o;
}
class H {
@SafeVarargs
H(List<String>... lists) {}
}
}
""")
.doTest();
}
@@ -69,34 +71,38 @@ final class AutowiredConstructorTest {
BugCheckerRefactoringTestHelper.newInstance(AutowiredConstructor.class, getClass())
.addInputLines(
"Container.java",
"import org.springframework.beans.factory.annotation.Autowired;",
"",
"interface Container {",
" class A {",
" @Autowired",
" @Deprecated",
" A() {}",
" }",
"",
" class B {",
" @Autowired",
" B(String x) {}",
" }",
"}")
"""
import org.springframework.beans.factory.annotation.Autowired;
interface Container {
class A {
@Autowired
@Deprecated
A() {}
}
class B {
@Autowired
B(String x) {}
}
}
""")
.addOutputLines(
"Container.java",
"import org.springframework.beans.factory.annotation.Autowired;",
"",
"interface Container {",
" class A {",
" @Deprecated",
" A() {}",
" }",
"",
" class B {",
" B(String x) {}",
" }",
"}")
"""
import org.springframework.beans.factory.annotation.Autowired;
interface Container {
class A {
@Deprecated
A() {}
}
class B {
B(String x) {}
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,128 +11,130 @@ final class CanonicalAnnotationSyntaxTest {
CompilationTestHelper.newInstance(CanonicalAnnotationSyntax.class, getClass())
.addSourceLines(
"pkg/A.java",
"package pkg;",
"",
"import pkg.A.Foo;",
"",
"interface A {",
" @interface Foo {",
" int[] value() default {};",
"",
" int[] value2() default {};",
" }",
"",
" @pkg.A.Foo",
" A minimal1();",
"",
" @A.Foo",
" A minimal2();",
"",
" @Foo",
" A minimal3();",
"",
" // BUG: Diagnostic contains:",
" @pkg.A.Foo()",
" A functional1();",
"",
" // BUG: Diagnostic contains:",
" @A.Foo()",
" A functional2();",
"",
" // BUG: Diagnostic contains:",
" @Foo()",
" A functional3();",
"",
" @pkg.A.Foo(1)",
" A simple1();",
"",
" @A.Foo(1)",
" A simple2();",
"",
" @Foo(1)",
" A simple3();",
"",
" // BUG: Diagnostic contains:",
" @pkg.A.Foo({1})",
" A singleton1();",
"",
" // BUG: Diagnostic contains:",
" @A.Foo({1})",
" A singleton2();",
"",
" // BUG: Diagnostic contains:",
" @Foo({1})",
" A singleton3();",
"",
" // 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();",
"",
" @pkg.A.Foo(value2 = 2)",
" A custom1();",
"",
" @A.Foo(value2 = 2)",
" A custom2();",
"",
" @Foo(value2 = 2)",
" A custom3();",
"",
" // 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();",
"",
" @pkg.A.Foo(value2 = {2, 2})",
" A customPair1();",
"",
" @A.Foo(value2 = {2, 2})",
" A customPair2();",
"",
" @Foo(value2 = {2, 2})",
" A customPair3();",
"",
" @pkg.A.Foo(value = 1, value2 = 2)",
" A extended1();",
"",
" @A.Foo(value = 1, value2 = 2)",
" A extended2();",
"",
" @Foo(value = 1, value2 = 2)",
" A extended3();",
"",
" // BUG: Diagnostic contains:",
" @pkg.A.Foo({",
" 1, 1,",
" })",
" A trailingComma1();",
"",
" // BUG: Diagnostic contains:",
" @A.Foo({",
" 1, 1,",
" })",
" A trailingComma2();",
"",
" // BUG: Diagnostic contains:",
" @Foo({",
" 1, 1,",
" })",
" A trailingComma3();",
"}")
"""
package pkg;
import pkg.A.Foo;
interface A {
@interface Foo {
int[] value() default {};
int[] value2() default {};
}
@pkg.A.Foo
A minimal1();
@A.Foo
A minimal2();
@Foo
A minimal3();
// BUG: Diagnostic contains:
@pkg.A.Foo()
A functional1();
// BUG: Diagnostic contains:
@A.Foo()
A functional2();
// BUG: Diagnostic contains:
@Foo()
A functional3();
@pkg.A.Foo(1)
A simple1();
@A.Foo(1)
A simple2();
@Foo(1)
A simple3();
// BUG: Diagnostic contains:
@pkg.A.Foo({1})
A singleton1();
// BUG: Diagnostic contains:
@A.Foo({1})
A singleton2();
// BUG: Diagnostic contains:
@Foo({1})
A singleton3();
// 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();
@pkg.A.Foo(value2 = 2)
A custom1();
@A.Foo(value2 = 2)
A custom2();
@Foo(value2 = 2)
A custom3();
// 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();
@pkg.A.Foo(value2 = {2, 2})
A customPair1();
@A.Foo(value2 = {2, 2})
A customPair2();
@Foo(value2 = {2, 2})
A customPair3();
@pkg.A.Foo(value = 1, value2 = 2)
A extended1();
@A.Foo(value = 1, value2 = 2)
A extended2();
@Foo(value = 1, value2 = 2)
A extended3();
// BUG: Diagnostic contains:
@pkg.A.Foo({
1, 1,
})
A trailingComma1();
// BUG: Diagnostic contains:
@A.Foo({
1, 1,
})
A trailingComma2();
// BUG: Diagnostic contains:
@Foo({
1, 1,
})
A trailingComma3();
}
""")
.doTest();
}
@@ -141,139 +143,143 @@ final class CanonicalAnnotationSyntaxTest {
BugCheckerRefactoringTestHelper.newInstance(CanonicalAnnotationSyntax.class, getClass())
.addInputLines(
"pkg/A.java",
"package pkg;",
"",
"import pkg.A.Foo;",
"",
"interface A {",
" @interface Foo {",
" String[] value() default {};",
"",
" int[] value2() default {};",
" }",
"",
" @pkg.A.Foo()",
" A functional1();",
"",
" @A.Foo()",
" A functional2();",
"",
" @Foo()",
" A functional3();",
"",
" @pkg.A.Foo(value = \"foo\")",
" A verbose1();",
"",
" @A.Foo(value = \"a'b\")",
" A verbose2();",
"",
" @Foo(value = \"a\" + \"\\nb\")",
" A verbose3();",
"",
" @pkg.A.Foo(value = {\"foo\"})",
" A moreVerbose1();",
"",
" @A.Foo(value = {\"a'b\"})",
" A moreVerbose2();",
"",
" @Foo(value = {\"a\" + \"\\nb\"})",
" A moreVerbose3();",
"",
" @pkg.A.Foo(",
" value = {\"foo\", \"bar\"},",
" value2 = {2})",
" A extended1();",
"",
" @A.Foo(",
" value = {\"a'b\", \"c'd\"},",
" value2 = {2})",
" A extended2();",
"",
" @Foo(",
" value = {\"a\" + \"\\nb\", \"c\" + \"\\nd\"},",
" value2 = {2})",
" A extended3();",
"",
" @pkg.A.Foo({",
" \"foo\", \"bar\",",
" })",
" A trailingComma1();",
"",
" @A.Foo({",
" \"a'b\", \"c'd\",",
" })",
" A trailingComma2();",
"",
" @Foo({",
" \"a\" + \"\\nb\",",
" \"c\" + \"\\nd\",",
" })",
" A trailingComma3();",
"}")
"""
package pkg;
import pkg.A.Foo;
interface A {
@interface Foo {
String[] value() default {};
int[] value2() default {};
}
@pkg.A.Foo()
A functional1();
@A.Foo()
A functional2();
@Foo()
A functional3();
@pkg.A.Foo(value = "foo")
A verbose1();
@A.Foo(value = "a'b")
A verbose2();
@Foo(value = "a" + "\\nb")
A verbose3();
@pkg.A.Foo(value = {"foo"})
A moreVerbose1();
@A.Foo(value = {"a'b"})
A moreVerbose2();
@Foo(value = {"a" + "\\nb"})
A moreVerbose3();
@pkg.A.Foo(
value = {"foo", "bar"},
value2 = {2})
A extended1();
@A.Foo(
value = {"a'b", "c'd"},
value2 = {2})
A extended2();
@Foo(
value = {"a" + "\\nb", "c" + "\\nd"},
value2 = {2})
A extended3();
@pkg.A.Foo({
"foo", "bar",
})
A trailingComma1();
@A.Foo({
"a'b", "c'd",
})
A trailingComma2();
@Foo({
"a" + "\\nb",
"c" + "\\nd",
})
A trailingComma3();
}
""")
.addOutputLines(
"pkg/A.java",
"package pkg;",
"",
"import pkg.A.Foo;",
"",
"interface A {",
" @interface Foo {",
" String[] value() default {};",
"",
" int[] value2() default {};",
" }",
"",
" @pkg.A.Foo",
" A functional1();",
"",
" @A.Foo",
" A functional2();",
"",
" @Foo",
" A functional3();",
"",
" @pkg.A.Foo(\"foo\")",
" A verbose1();",
"",
" @A.Foo(\"a'b\")",
" A verbose2();",
"",
" @Foo(\"a\" + \"\\nb\")",
" A verbose3();",
"",
" @pkg.A.Foo(\"foo\")",
" A moreVerbose1();",
"",
" @A.Foo(\"a'b\")",
" A moreVerbose2();",
"",
" @Foo(\"a\" + \"\\nb\")",
" A moreVerbose3();",
"",
" @pkg.A.Foo(",
" value = {\"foo\", \"bar\"},",
" value2 = 2)",
" A extended1();",
"",
" @A.Foo(",
" value = {\"a'b\", \"c'd\"},",
" value2 = 2)",
" A extended2();",
"",
" @Foo(",
" value = {\"a\" + \"\\nb\", \"c\" + \"\\nd\"},",
" value2 = 2)",
" A extended3();",
"",
" @pkg.A.Foo({\"foo\", \"bar\"})",
" A trailingComma1();",
"",
" @A.Foo({\"a'b\", \"c'd\"})",
" A trailingComma2();",
"",
" @Foo({\"a\" + \"\\nb\", \"c\" + \"\\nd\"})",
" A trailingComma3();",
"}")
"""
package pkg;
import pkg.A.Foo;
interface A {
@interface Foo {
String[] value() default {};
int[] value2() default {};
}
@pkg.A.Foo
A functional1();
@A.Foo
A functional2();
@Foo
A functional3();
@pkg.A.Foo("foo")
A verbose1();
@A.Foo("a'b")
A verbose2();
@Foo("a" + "\\nb")
A verbose3();
@pkg.A.Foo("foo")
A moreVerbose1();
@A.Foo("a'b")
A moreVerbose2();
@Foo("a" + "\\nb")
A moreVerbose3();
@pkg.A.Foo(
value = {"foo", "bar"},
value2 = 2)
A extended1();
@A.Foo(
value = {"a'b", "c'd"},
value2 = 2)
A extended2();
@Foo(
value = {"a" + "\\nb", "c" + "\\nd"},
value2 = 2)
A extended3();
@pkg.A.Foo({"foo", "bar"})
A trailingComma1();
@A.Foo({"a'b", "c'd"})
A trailingComma2();
@Foo({"a" + "\\nb", "c" + "\\nd"})
A trailingComma3();
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -13,50 +13,52 @@ final class CollectorMutabilityTest {
CompilationTestHelper.newInstance(CollectorMutability.class, getClass())
.addSourceLines(
"A.java",
"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.stream.Collectors.toCollection;",
"import static java.util.stream.Collectors.toList;",
"import static java.util.stream.Collectors.toMap;",
"import static java.util.stream.Collectors.toSet;",
"",
"import java.util.ArrayList;",
"import java.util.HashMap;",
"import java.util.HashSet;",
"import java.util.stream.Collectors;",
"import java.util.stream.Stream;",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" // BUG: Diagnostic contains:",
" Flux.just(1).collect(Collectors.toList());",
" // BUG: Diagnostic contains:",
" Flux.just(2).collect(toList());",
" Flux.just(3).collect(toImmutableList());",
" Flux.just(4).collect(toCollection(ArrayList::new));",
"",
" // BUG: Diagnostic contains:",
" Flux.just(\"foo\").collect(Collectors.toMap(String::getBytes, String::length));",
" // BUG: Diagnostic contains:",
" Flux.just(\"bar\").collect(toMap(String::getBytes, String::length));",
" Flux.just(\"baz\").collect(toImmutableMap(String::getBytes, String::length));",
" // BUG: Diagnostic contains:",
" Flux.just(\"qux\").collect(toMap(String::getBytes, String::length, (a, b) -> a));",
" Flux.just(\"quux\").collect(toImmutableMap(String::getBytes, String::length, (a, b) -> a));",
" Flux.just(\"quuz\").collect(toMap(String::getBytes, String::length, (a, b) -> a, HashMap::new));",
"",
" // BUG: Diagnostic contains:",
" Stream.of(1).collect(Collectors.toSet());",
" // BUG: Diagnostic contains:",
" Stream.of(2).collect(toSet());",
" Stream.of(3).collect(toImmutableSet());",
" Stream.of(4).collect(toCollection(HashSet::new));",
"",
" Flux.just(\"foo\").collect(Collectors.joining());",
" }",
"}")
"""
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.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import reactor.core.publisher.Flux;
class A {
void m() {
// BUG: Diagnostic contains:
Flux.just(1).collect(Collectors.toList());
// BUG: Diagnostic contains:
Flux.just(2).collect(toList());
Flux.just(3).collect(toImmutableList());
Flux.just(4).collect(toCollection(ArrayList::new));
// BUG: Diagnostic contains:
Flux.just("foo").collect(Collectors.toMap(String::getBytes, String::length));
// BUG: Diagnostic contains:
Flux.just("bar").collect(toMap(String::getBytes, String::length));
Flux.just("baz").collect(toImmutableMap(String::getBytes, String::length));
// BUG: Diagnostic contains:
Flux.just("qux").collect(toMap(String::getBytes, String::length, (a, b) -> a));
Flux.just("quux").collect(toImmutableMap(String::getBytes, String::length, (a, b) -> a));
Flux.just("quuz").collect(toMap(String::getBytes, String::length, (a, b) -> a, HashMap::new));
// BUG: Diagnostic contains:
Stream.of(1).collect(Collectors.toSet());
// BUG: Diagnostic contains:
Stream.of(2).collect(toSet());
Stream.of(3).collect(toImmutableSet());
Stream.of(4).collect(toCollection(HashSet::new));
Flux.just("foo").collect(Collectors.joining());
}
}
""")
.doTest();
}
@@ -66,14 +68,16 @@ final class CollectorMutabilityTest {
.withClasspath()
.addSourceLines(
"A.java",
"import java.util.stream.Collectors;",
"import java.util.stream.Stream;",
"",
"class A {",
" void m() {",
" Stream.empty().collect(Collectors.toList());",
" }",
"}")
"""
import java.util.stream.Collectors;
import java.util.stream.Stream;
class A {
void m() {
Stream.empty().collect(Collectors.toList());
}
}
""")
.doTest();
}
@@ -82,55 +86,59 @@ final class CollectorMutabilityTest {
BugCheckerRefactoringTestHelper.newInstance(CollectorMutability.class, getClass())
.addInputLines(
"A.java",
"import static java.util.stream.Collectors.toList;",
"import static java.util.stream.Collectors.toMap;",
"import static java.util.stream.Collectors.toSet;",
"",
"import java.util.stream.Collectors;",
"import java.util.stream.Stream;",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" Flux.just(1).collect(Collectors.toList());",
" Flux.just(2).collect(toList());",
"",
" Stream.of(\"foo\").collect(Collectors.toMap(String::getBytes, String::length));",
" Stream.of(\"bar\").collect(toMap(String::getBytes, String::length));",
" Flux.just(\"baz\").collect(Collectors.toMap(String::getBytes, String::length, (a, b) -> b));",
" Flux.just(\"qux\").collect(toMap(String::getBytes, String::length, (a, b) -> b));",
"",
" Stream.of(1).collect(Collectors.toSet());",
" Stream.of(2).collect(toSet());",
" }",
"}")
"""
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import reactor.core.publisher.Flux;
class A {
void m() {
Flux.just(1).collect(Collectors.toList());
Flux.just(2).collect(toList());
Stream.of("foo").collect(Collectors.toMap(String::getBytes, String::length));
Stream.of("bar").collect(toMap(String::getBytes, String::length));
Flux.just("baz").collect(Collectors.toMap(String::getBytes, String::length, (a, b) -> b));
Flux.just("qux").collect(toMap(String::getBytes, String::length, (a, b) -> b));
Stream.of(1).collect(Collectors.toSet());
Stream.of(2).collect(toSet());
}
}
""")
.addOutputLines(
"A.java",
"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.stream.Collectors.toList;",
"import static java.util.stream.Collectors.toMap;",
"import static java.util.stream.Collectors.toSet;",
"",
"import java.util.stream.Collectors;",
"import java.util.stream.Stream;",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" Flux.just(1).collect(toImmutableList());",
" Flux.just(2).collect(toImmutableList());",
"",
" Stream.of(\"foo\").collect(toImmutableMap(String::getBytes, String::length));",
" Stream.of(\"bar\").collect(toImmutableMap(String::getBytes, String::length));",
" Flux.just(\"baz\").collect(toImmutableMap(String::getBytes, String::length, (a, b) -> b));",
" Flux.just(\"qux\").collect(toImmutableMap(String::getBytes, String::length, (a, b) -> b));",
"",
" Stream.of(1).collect(toImmutableSet());",
" Stream.of(2).collect(toImmutableSet());",
" }",
"}")
"""
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.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import reactor.core.publisher.Flux;
class A {
void m() {
Flux.just(1).collect(toImmutableList());
Flux.just(2).collect(toImmutableList());
Stream.of("foo").collect(toImmutableMap(String::getBytes, String::length));
Stream.of("bar").collect(toImmutableMap(String::getBytes, String::length));
Flux.just("baz").collect(toImmutableMap(String::getBytes, String::length, (a, b) -> b));
Flux.just("qux").collect(toImmutableMap(String::getBytes, String::length, (a, b) -> b));
Stream.of(1).collect(toImmutableSet());
Stream.of(2).collect(toImmutableSet());
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
@@ -140,73 +148,77 @@ final class CollectorMutabilityTest {
.setFixChooser(SECOND)
.addInputLines(
"A.java",
"import static java.util.stream.Collectors.toList;",
"import static java.util.stream.Collectors.toMap;",
"import static java.util.stream.Collectors.toSet;",
"",
"import java.util.stream.Collectors;",
"import java.util.stream.Stream;",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" Flux.just(1).collect(Collectors.toList());",
" Flux.just(2).collect(toList());",
"",
" Stream.of(\"foo\").collect(Collectors.toMap(String::getBytes, String::length));",
" Stream.of(\"bar\").collect(toMap(String::getBytes, String::length));",
" Flux.just(\"baz\").collect(Collectors.toMap(String::getBytes, String::length, (a, b) -> b));",
" Flux.just(\"qux\").collect(toMap(String::getBytes, String::length, (a, b) -> b));",
"",
" Stream.of(1).collect(Collectors.toSet());",
" Stream.of(2).collect(toSet());",
" }",
"}")
"""
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import reactor.core.publisher.Flux;
class A {
void m() {
Flux.just(1).collect(Collectors.toList());
Flux.just(2).collect(toList());
Stream.of("foo").collect(Collectors.toMap(String::getBytes, String::length));
Stream.of("bar").collect(toMap(String::getBytes, String::length));
Flux.just("baz").collect(Collectors.toMap(String::getBytes, String::length, (a, b) -> b));
Flux.just("qux").collect(toMap(String::getBytes, String::length, (a, b) -> b));
Stream.of(1).collect(Collectors.toSet());
Stream.of(2).collect(toSet());
}
}
""")
.addOutputLines(
"A.java",
"import static java.util.stream.Collectors.toCollection;",
"import static java.util.stream.Collectors.toList;",
"import static java.util.stream.Collectors.toMap;",
"import static java.util.stream.Collectors.toSet;",
"",
"import java.util.ArrayList;",
"import java.util.HashMap;",
"import java.util.HashSet;",
"import java.util.stream.Collectors;",
"import java.util.stream.Stream;",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" Flux.just(1).collect(toCollection(ArrayList::new));",
" Flux.just(2).collect(toCollection(ArrayList::new));",
"",
" Stream.of(\"foo\")",
" .collect(",
" Collectors.toMap(",
" String::getBytes,",
" String::length,",
" (a, b) -> {",
" throw new IllegalStateException();",
" },",
" HashMap::new));",
" Stream.of(\"bar\")",
" .collect(",
" toMap(",
" String::getBytes,",
" String::length,",
" (a, b) -> {",
" throw new IllegalStateException();",
" },",
" HashMap::new));",
" Flux.just(\"baz\")",
" .collect(Collectors.toMap(String::getBytes, String::length, (a, b) -> b, HashMap::new));",
" Flux.just(\"qux\").collect(toMap(String::getBytes, String::length, (a, b) -> b, HashMap::new));",
"",
" Stream.of(1).collect(toCollection(HashSet::new));",
" Stream.of(2).collect(toCollection(HashSet::new));",
" }",
"}")
"""
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import reactor.core.publisher.Flux;
class A {
void m() {
Flux.just(1).collect(toCollection(ArrayList::new));
Flux.just(2).collect(toCollection(ArrayList::new));
Stream.of("foo")
.collect(
Collectors.toMap(
String::getBytes,
String::length,
(a, b) -> {
throw new IllegalStateException();
},
HashMap::new));
Stream.of("bar")
.collect(
toMap(
String::getBytes,
String::length,
(a, b) -> {
throw new IllegalStateException();
},
HashMap::new));
Flux.just("baz")
.collect(Collectors.toMap(String::getBytes, String::length, (a, b) -> b, HashMap::new));
Flux.just("qux").collect(toMap(String::getBytes, String::length, (a, b) -> b, HashMap::new));
Stream.of(1).collect(toCollection(HashSet::new));
Stream.of(2).collect(toCollection(HashSet::new));
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,184 +11,186 @@ final class DirectReturnTest {
CompilationTestHelper.newInstance(DirectReturn.class, getClass())
.addSourceLines(
"A.java",
"import static org.mockito.Mockito.mock;",
"import static org.mockito.Mockito.spy;",
"",
"import java.util.function.Supplier;",
"",
"class A {",
" private String field;",
"",
" void emptyMethod() {}",
"",
" void voidMethod() {",
" toString();",
" return;",
" }",
"",
" String directReturnOfParam(String param) {",
" return param;",
" }",
"",
" String assignmentToField() {",
" field = toString();",
" return field;",
" }",
"",
" Object redundantAssignmentToParam(String param) {",
" // BUG: Diagnostic contains:",
" param = toString();",
" return param;",
" }",
"",
" String redundantMockAssignmentToParam(String param) {",
" // BUG: Diagnostic contains:",
" param = mock();",
" return param;",
" }",
"",
" Object redundantMockWithExplicitTypeAssignmentToParam(String param) {",
" // BUG: Diagnostic contains:",
" param = mock(String.class);",
" return param;",
" }",
"",
" Object salientMockAssignmentToParam(String param) {",
" param = mock();",
" return param;",
" }",
"",
" String redundantAssignmentToLocalVariable() {",
" String variable = null;",
" // BUG: Diagnostic contains:",
" variable = toString();",
" return variable;",
" }",
"",
" String unusedAssignmentToLocalVariable(String param) {",
" String variable = null;",
" variable = toString();",
" return param;",
" }",
"",
" String redundantVariableDeclaration() {",
" // BUG: Diagnostic contains:",
" String variable = toString();",
" return variable;",
" }",
"",
" String redundantSpyVariableDeclaration() {",
" // BUG: Diagnostic contains:",
" String variable = spy();",
" return variable;",
" }",
"",
" Object redundantSpyWithExplicitTypeVariableDeclaration() {",
" // BUG: Diagnostic contains:",
" String variable = spy(String.class);",
" return variable;",
" }",
"",
" Object salientSpyTypeVariableDeclaration() {",
" String variable = spy(\"name\");",
" return variable;",
" }",
"",
" String unusedVariableDeclaration(String param) {",
" String variable = toString();",
" return param;",
" }",
"",
" String assignmentToAnnotatedVariable() {",
" @SuppressWarnings(\"HereBeDragons\")",
" String variable = toString();",
" return variable;",
" }",
"",
" String complexReturnStatement() {",
" String variable = toString();",
" return variable + toString();",
" }",
"",
" String assignmentInsideIfClause() {",
" String variable = null;",
" if (true) {",
" variable = toString();",
" }",
" return variable;",
" }",
"",
" String redundantAssignmentInsideElseClause() {",
" String variable = toString();",
" if (true) {",
" return variable;",
" } else {",
" // BUG: Diagnostic contains:",
" variable = \"foo\";",
" return variable;",
" }",
" }",
"",
" Supplier<String> redundantAssignmentInsideLambda() {",
" return () -> {",
" // BUG: Diagnostic contains:",
" String variable = toString();",
" 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;",
" }",
" }",
"}")
"""
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import java.util.function.Supplier;
class A {
private String field;
void emptyMethod() {}
void voidMethod() {
toString();
return;
}
String directReturnOfParam(String param) {
return param;
}
String assignmentToField() {
field = toString();
return field;
}
Object redundantAssignmentToParam(String param) {
// BUG: Diagnostic contains:
param = toString();
return param;
}
String redundantMockAssignmentToParam(String param) {
// BUG: Diagnostic contains:
param = mock();
return param;
}
Object redundantMockWithExplicitTypeAssignmentToParam(String param) {
// BUG: Diagnostic contains:
param = mock(String.class);
return param;
}
Object salientMockAssignmentToParam(String param) {
param = mock();
return param;
}
String redundantAssignmentToLocalVariable() {
String variable = null;
// BUG: Diagnostic contains:
variable = toString();
return variable;
}
String unusedAssignmentToLocalVariable(String param) {
String variable = null;
variable = toString();
return param;
}
String redundantVariableDeclaration() {
// BUG: Diagnostic contains:
String variable = toString();
return variable;
}
String redundantSpyVariableDeclaration() {
// BUG: Diagnostic contains:
String variable = spy();
return variable;
}
Object redundantSpyWithExplicitTypeVariableDeclaration() {
// BUG: Diagnostic contains:
String variable = spy(String.class);
return variable;
}
Object salientSpyTypeVariableDeclaration() {
String variable = spy("name");
return variable;
}
String unusedVariableDeclaration(String param) {
String variable = toString();
return param;
}
String assignmentToAnnotatedVariable() {
@SuppressWarnings("HereBeDragons")
String variable = toString();
return variable;
}
String complexReturnStatement() {
String variable = toString();
return variable + toString();
}
String assignmentInsideIfClause() {
String variable = null;
if (true) {
variable = toString();
}
return variable;
}
String redundantAssignmentInsideElseClause() {
String variable = toString();
if (true) {
return variable;
} else {
// BUG: Diagnostic contains:
variable = "foo";
return variable;
}
}
Supplier<String> redundantAssignmentInsideLambda() {
return () -> {
// BUG: Diagnostic contains:
String variable = toString();
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();
}
@@ -197,30 +199,34 @@ final class DirectReturnTest {
BugCheckerRefactoringTestHelper.newInstance(DirectReturn.class, getClass())
.addInputLines(
"A.java",
"class A {",
" String m1() {",
" String variable = null;",
" variable = toString();",
" return variable;",
" }",
"",
" String m2() {",
" String variable = toString();",
" return variable;",
" }",
"}")
"""
class A {
String m1() {
String variable = null;
variable = toString();
return variable;
}
String m2() {
String variable = toString();
return variable;
}
}
""")
.addOutputLines(
"A.java",
"class A {",
" String m1() {",
" String variable = null;",
" return toString();",
" }",
"",
" String m2() {",
" return toString();",
" }",
"}")
"""
class A {
String m1() {
String variable = null;
return toString();
}
String m2() {
return toString();
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,54 +11,58 @@ final class EmptyMethodTest {
CompilationTestHelper.newInstance(EmptyMethod.class, getClass())
.addSourceLines(
"A.java",
"class A {",
" Object m1() {",
" return null;",
" }",
"",
" void m2() {",
" System.out.println(42);",
" }",
"",
" void m3() {}",
"",
" // BUG: Diagnostic contains:",
" static void m4() {}",
"",
" interface F {",
" void fun();",
" }",
"",
" final class MyTestClass {",
" void helperMethod() {}",
" }",
"}")
"""
class A {
Object m1() {
return null;
}
void m2() {
System.out.println(42);
}
void m3() {}
// BUG: Diagnostic contains:
static void m4() {}
interface F {
void fun();
}
final class MyTestClass {
void helperMethod() {}
}
}
""")
.addSourceLines(
"B.java",
"import org.aspectj.lang.annotation.Pointcut;",
"",
"final class B implements A.F {",
" @Override",
" public void fun() {}",
"",
" // BUG: Diagnostic contains:",
" void m3() {}",
"",
" /** Javadoc. */",
" // BUG: Diagnostic contains:",
" void m4() {}",
"",
" void m5() {",
" // Single-line comment.",
" }",
"",
" void m6() {",
" /* Multi-line comment. */",
" }",
"",
" @Pointcut",
" void m7() {}",
"}")
"""
import org.aspectj.lang.annotation.Pointcut;
final class B implements A.F {
@Override
public void fun() {}
// BUG: Diagnostic contains:
void m3() {}
/** Javadoc. */
// BUG: Diagnostic contains:
void m4() {}
void m5() {
// Single-line comment.
}
void m6() {
/* Multi-line comment. */
}
@Pointcut
void m7() {}
}
""")
.doTest();
}
@@ -67,22 +71,26 @@ final class EmptyMethodTest {
BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass())
.addInputLines(
"A.java",
"final class A {",
" void instanceMethod() {}",
"",
" static void staticMethod() {}",
"",
" static void staticMethodWithComment() {",
" /* Foo. */",
" }",
"}")
"""
final class A {
void instanceMethod() {}
static void staticMethod() {}
static void staticMethodWithComment() {
/* Foo. */
}
}
""")
.addOutputLines(
"A.java",
"final class A {",
" static void staticMethodWithComment() {",
" /* Foo. */",
" }",
"}")
"""
final class A {
static void staticMethodWithComment() {
/* Foo. */
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -4,142 +4,314 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
final class ErrorProneTestHelperSourceFormatTest {
// XXX: Add tests cases for `ErrorProneTestHelperSourceFormat:IgnoreMalformedCode`.
// XXX: Consider reducing the `@DisabledForJreRange(max = JRE.JAVA_14)` test scope by moving the
// text blocks to smaller test methods.
@DisabledForJreRange(max = JRE.JAVA_14)
@Test
void identification() {
CompilationTestHelper.newInstance(ErrorProneTestHelperSourceFormat.class, getClass())
.addSourceLines(
"A.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
"import com.google.errorprone.CompilationTestHelper;",
"import tech.picnic.errorprone.bugpatterns.EmptyMethod;",
"",
"class A {",
" private final CompilationTestHelper compilationTestHelper =",
" CompilationTestHelper.newInstance(EmptyMethod.class, getClass());",
" private final BugCheckerRefactoringTestHelper refactoringTestHelper =",
" BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass());",
"",
" void m() {",
" compilationTestHelper",
" // BUG: Diagnostic contains: No source code provided",
" .addSourceLines(\"A.java\")",
" // BUG: Diagnostic contains: Source code is malformed:",
" .addSourceLines(\"B.java\", \"class B {\")",
" // Well-formed code, so not flagged.",
" .addSourceLines(\"C.java\", \"class C {}\")",
" // Malformed code, but not compile-time constant, so not flagged.",
" .addSourceLines(\"D.java\", \"class D {\" + getClass())",
" // BUG: Diagnostic contains: Test code should follow the Google Java style",
" .addSourceLines(\"E.java\", \"class E { }\")",
" .doTest();",
"",
" refactoringTestHelper",
" // BUG: Diagnostic contains: Test code should follow the Google Java style",
" .addInputLines(\"A.java\", \"class A { }\")",
" // BUG: Diagnostic contains: Test code should follow the Google Java style",
" .addOutputLines(\"A.java\", \"class A { }\")",
" // BUG: Diagnostic contains: Test code should follow the Google Java style",
" .addInputLines(\"B.java\", \"import java.util.Map;\", \"\", \"class B {}\")",
" // Unused import, but in an output file, so not flagged.",
" .addOutputLines(\"B.java\", \"import java.util.Map;\", \"\", \"class B {}\")",
" .doTest(TestMode.TEXT_MATCH);",
" }",
"}")
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import tech.picnic.errorprone.bugpatterns.EmptyMethod;
class A {
void m() {
CompilationTestHelper.newInstance(EmptyMethod.class, getClass())
// BUG: Diagnostic contains: No source code provided
.addSourceLines("A.java")
// BUG: Diagnostic contains: Source code is malformed:
.addSourceLines("B.java", "class B {")
// BUG: Diagnostic contains: Test code should be specified using a single text block
.addSourceLines("C.java", "class C {}")
// Malformed code, but not compile-time constant, so not flagged.
.addSourceLines("D.java", "class D {" + getClass())
// BUG: Diagnostic contains: Test code should follow the Google Java style
.addSourceLines("E.java", "class E { }")
// Well-formed code, so not flagged.
.addSourceLines("F.java", ""\"
class F {}
""\")
// BUG: Diagnostic contains: Test code should follow the Google Java style (pay attention to
// trailing newlines)
.addSourceLines("G.java", ""\"
class G {}""\")
.doTest();
BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass())
// BUG: Diagnostic contains: Test code should follow the Google Java style
.addInputLines("in/A.java", "class A { }")
// BUG: Diagnostic contains: Test code should follow the Google Java style
.addOutputLines("out/A.java", "class A { }")
// BUG: Diagnostic contains: Test code should follow the Google Java style
.addInputLines(
"in/B.java",
""\"
import java.util.Map;
class B {}
""\")
// Unused import, but in an output file, so not flagged.
.addOutputLines(
"out/B.java",
""\"
import java.util.Map;
class B {}
""\")
.doTest(TestMode.TEXT_MATCH);
}
}
""")
.doTest();
}
@DisabledForJreRange(max = JRE.JAVA_14)
@Test
void identificationAvoidTextBlocks() {
CompilationTestHelper.newInstance(ErrorProneTestHelperSourceFormat.class, getClass())
.setArgs("-XepOpt:ErrorProneTestHelperSourceFormat:AvoidTextBlocks=true")
.addSourceLines(
"A.java",
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import tech.picnic.errorprone.bugpatterns.EmptyMethod;
class A {
void m() {
CompilationTestHelper.newInstance(EmptyMethod.class, getClass())
// BUG: Diagnostic contains: No source code provided
.addSourceLines("A.java")
// BUG: Diagnostic contains: Source code is malformed:
.addSourceLines("B.java", "class B {")
// Well-formed code, so not flagged.
.addSourceLines("C.java", "class C {}")
// Malformed code, but not compile-time constant, so not flagged.
.addSourceLines("D.java", "class D {" + getClass())
// BUG: Diagnostic contains: Test code should follow the Google Java style
.addSourceLines("E.java", "class E { }")
// BUG: Diagnostic contains: Test code should not be specified using a single text block
.addSourceLines("F.java", ""\"
class F {}
""\")
// BUG: Diagnostic contains: Test code should follow the Google Java style (pay attention to
// trailing newlines)
.addSourceLines("G.java", "class G {}", "")
.doTest();
BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass())
// BUG: Diagnostic contains: Test code should follow the Google Java style
.addInputLines("A.java", "class A { }")
// BUG: Diagnostic contains: Test code should follow the Google Java style
.addOutputLines("A.java", "class A { }")
// BUG: Diagnostic contains: Test code should follow the Google Java style
.addInputLines("B.java", "import java.util.Map;", "", "class B {}")
// Unused import, but in an output file, so not flagged.
.addOutputLines("B.java", "import java.util.Map;", "", "class B {}")
.doTest(TestMode.TEXT_MATCH);
}
}
""")
.doTest();
}
// XXX: Add `replacement` test.
@DisabledForJreRange(max = JRE.JAVA_14)
@Test
void replacement() {
/*
* Verifies that import sorting and code formatting is performed unconditionally, while unused
* imports are removed unless part of a `BugCheckerRefactoringTestHelper` expected output file.
* Also verifies that text blocks are properly indented.
*/
BugCheckerRefactoringTestHelper.newInstance(ErrorProneTestHelperSourceFormat.class, getClass())
.addInputLines(
"A.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
"import com.google.errorprone.CompilationTestHelper;",
"import tech.picnic.errorprone.bugpatterns.EmptyMethod;",
"",
"class A {",
" private final CompilationTestHelper compilationTestHelper =",
" CompilationTestHelper.newInstance(EmptyMethod.class, getClass());",
" private final BugCheckerRefactoringTestHelper refactoringTestHelper =",
" BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass());",
"",
" void m() {",
" compilationTestHelper",
" .addSourceLines(",
" \"A.java\",",
" \"import java.util.Map;\",",
" \"import java.util.Collection;\",",
" \"import java.util.List;\",",
" \"\",",
" \"interface A extends List<A>, Map<A,A> { }\")",
" .doTest();",
"",
" refactoringTestHelper",
" .addInputLines(",
" \"A.java\",",
" \"import java.util.Map;\",",
" \"import java.util.Collection;\",",
" \"import java.util.List;\",",
" \"\",",
" \"interface A extends List<A>, Map<A,A> { }\")",
" .addOutputLines(",
" \"A.java\",",
" \"import java.util.Map;\",",
" \"import java.util.Collection;\",",
" \"import java.util.List;\",",
" \"\",",
" \"interface A extends List<A>, Map<A,A> { }\")",
" .doTest(TestMode.TEXT_MATCH);",
" }",
"}")
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import tech.picnic.errorprone.bugpatterns.EmptyMethod;
class A {
void m() {
CompilationTestHelper.newInstance(EmptyMethod.class, getClass())
.addSourceLines(
"A.java",
""\"
import java.util.Map;
import java.util.Collection;
import java.util.List;
interface A extends List<A>, Map<A,A> { }""\")
.addSourceLines("B.java", "class B {}")
.doTest();
BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass())
.addInputLines(
"in/A.java",
""\"
import java.util.Map;
import java.util.Collection;
import java.util.List;
interface A extends List<A>, Map<A,A> { }""\")
.addOutputLines(
"out/A.java",
""\"
import java.util.Map;
import java.util.Collection;
import java.util.List;
interface A extends List<A>, Map<A,A> { }""\")
.doTest(TestMode.TEXT_MATCH);
}
}
""")
.addOutputLines(
"out/A.java",
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import tech.picnic.errorprone.bugpatterns.EmptyMethod;
class A {
void m() {
CompilationTestHelper.newInstance(EmptyMethod.class, getClass())
.addSourceLines(
"A.java",
""\"
import java.util.List;
import java.util.Map;
interface A extends List<A>, Map<A, A> {}
""\")
.addSourceLines("B.java", ""\"
class B {}
""\")
.doTest();
BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass())
.addInputLines(
"in/A.java",
""\"
import java.util.List;
import java.util.Map;
interface A extends List<A>, Map<A, A> {}
""\")
.addOutputLines(
"out/A.java",
""\"
import java.util.Collection;
import java.util.List;
import java.util.Map;
interface A extends List<A>, Map<A, A> {}
""\")
.doTest(TestMode.TEXT_MATCH);
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
@Test
void replacementAvoidTextBlocks() {
/*
* Verifies that import sorting and code formatting is performed unconditionally, while unused
* imports are removed unless part of a `BugCheckerRefactoringTestHelper` expected output file.
*/
BugCheckerRefactoringTestHelper.newInstance(ErrorProneTestHelperSourceFormat.class, getClass())
.setArgs("-XepOpt:ErrorProneTestHelperSourceFormat:AvoidTextBlocks=true")
.addInputLines(
"in/A.java",
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import tech.picnic.errorprone.bugpatterns.EmptyMethod;
class A {
void m() {
CompilationTestHelper.newInstance(EmptyMethod.class, getClass())
.addSourceLines(
"A.java",
"import java.util.Map;",
"import java.util.Collection;",
"import java.util.List;",
"",
"interface A extends List<A>, Map<A,A> { }")
.doTest();
BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass())
.addInputLines(
"A.java",
"import java.util.Map;",
"import java.util.Collection;",
"import java.util.List;",
"",
"interface A extends List<A>, Map<A,A> { }")
.addOutputLines(
"A.java",
"import java.util.Map;",
"import java.util.Collection;",
"import java.util.List;",
"",
"interface A extends List<A>, Map<A,A> { }")
.doTest(TestMode.TEXT_MATCH);
}
}
""")
.addOutputLines(
"A.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
"import com.google.errorprone.CompilationTestHelper;",
"import tech.picnic.errorprone.bugpatterns.EmptyMethod;",
"",
"class A {",
" private final CompilationTestHelper compilationTestHelper =",
" CompilationTestHelper.newInstance(EmptyMethod.class, getClass());",
" private final BugCheckerRefactoringTestHelper refactoringTestHelper =",
" BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass());",
"",
" void m() {",
" compilationTestHelper",
" .addSourceLines(",
" \"A.java\",",
" \"import java.util.List;\",",
" \"import java.util.Map;\",",
" \"\",",
" \"interface A extends List<A>, Map<A, A> {}\")",
" .doTest();",
"",
" refactoringTestHelper",
" .addInputLines(",
" \"A.java\",",
" \"import java.util.List;\",",
" \"import java.util.Map;\",",
" \"\",",
" \"interface A extends List<A>, Map<A, A> {}\")",
" .addOutputLines(",
" \"A.java\",",
" \"import java.util.Collection;\",",
" \"import java.util.List;\",",
" \"import java.util.Map;\",",
" \"\",",
" \"interface A extends List<A>, Map<A, A> {}\")",
" .doTest(TestMode.TEXT_MATCH);",
" }",
"}")
"""
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import tech.picnic.errorprone.bugpatterns.EmptyMethod;
class A {
void m() {
CompilationTestHelper.newInstance(EmptyMethod.class, getClass())
.addSourceLines(
"A.java",
"import java.util.List;",
"import java.util.Map;",
"",
"interface A extends List<A>, Map<A, A> {}")
.doTest();
BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass())
.addInputLines(
"A.java",
"import java.util.List;",
"import java.util.Map;",
"",
"interface A extends List<A>, Map<A, A> {}")
.addOutputLines(
"A.java",
"import java.util.Collection;",
"import java.util.List;",
"import java.util.Map;",
"",
"interface A extends List<A>, Map<A, A> {}")
.doTest(TestMode.TEXT_MATCH);
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -9,74 +9,76 @@ final class ExplicitEnumOrderingTest {
CompilationTestHelper.newInstance(ExplicitEnumOrdering.class, getClass())
.addSourceLines(
"A.java",
"import static java.lang.annotation.RetentionPolicy.CLASS;",
"import static java.lang.annotation.RetentionPolicy.RUNTIME;",
"import static java.lang.annotation.RetentionPolicy.SOURCE;",
"import static java.time.chrono.IsoEra.BCE;",
"import static java.time.chrono.IsoEra.CE;",
"",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.Ordering;",
"import java.lang.annotation.RetentionPolicy;",
"import java.time.chrono.IsoEra;",
"",
"class A {",
" {",
" // The `List`-accepting overload is currently ignored.",
" Ordering.explicit(ImmutableList.of(RetentionPolicy.SOURCE, RetentionPolicy.CLASS));",
"",
" Ordering.explicit(IsoEra.BCE, IsoEra.CE);",
" // BUG: Diagnostic contains: IsoEra.CE",
" Ordering.explicit(IsoEra.BCE);",
" // BUG: Diagnostic contains: IsoEra.BCE",
" Ordering.explicit(IsoEra.CE);",
"",
" Ordering.explicit(RetentionPolicy.SOURCE, RetentionPolicy.CLASS, RetentionPolicy.RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.CLASS, RetentionPolicy.RUNTIME",
" Ordering.explicit(RetentionPolicy.SOURCE);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE, RetentionPolicy.RUNTIME",
" Ordering.explicit(RetentionPolicy.CLASS);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE, RetentionPolicy.CLASS",
" Ordering.explicit(RetentionPolicy.RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.RUNTIME",
" Ordering.explicit(RetentionPolicy.SOURCE, RetentionPolicy.CLASS);",
" // BUG: Diagnostic contains: RetentionPolicy.CLASS",
" Ordering.explicit(RetentionPolicy.SOURCE, RetentionPolicy.RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE",
" Ordering.explicit(RetentionPolicy.CLASS, RetentionPolicy.RUNTIME);",
"",
" Ordering.explicit(BCE, CE);",
" // BUG: Diagnostic contains: IsoEra.CE",
" Ordering.explicit(BCE);",
" // BUG: Diagnostic contains: IsoEra.BCE",
" Ordering.explicit(CE);",
"",
" Ordering.explicit(SOURCE, CLASS, RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.CLASS, RetentionPolicy.RUNTIME",
" Ordering.explicit(SOURCE);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE, RetentionPolicy.RUNTIME",
" Ordering.explicit(CLASS);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE, RetentionPolicy.CLASS",
" Ordering.explicit(RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.RUNTIME",
" Ordering.explicit(SOURCE, CLASS);",
" // BUG: Diagnostic contains: RetentionPolicy.CLASS",
" Ordering.explicit(SOURCE, RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE",
" Ordering.explicit(CLASS, RUNTIME);",
"",
" Ordering.explicit(RetentionPolicy.SOURCE, BCE, RetentionPolicy.CLASS, CE, RUNTIME);",
" Ordering.explicit(SOURCE, IsoEra.BCE, CLASS, IsoEra.CE, RetentionPolicy.RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.CLASS",
" Ordering.explicit(RetentionPolicy.SOURCE, BCE, CE, RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.CLASS",
" Ordering.explicit(IsoEra.BCE, SOURCE, IsoEra.CE, RetentionPolicy.RUNTIME);",
" // BUG: Diagnostic contains: IsoEra.CE, RetentionPolicy.RUNTIME",
" Ordering.explicit(IsoEra.BCE, SOURCE, RetentionPolicy.CLASS);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE, IsoEra.BCE",
" Ordering.explicit(CLASS, RUNTIME, CE);",
" }",
"}")
"""
import static java.lang.annotation.RetentionPolicy.CLASS;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import static java.time.chrono.IsoEra.BCE;
import static java.time.chrono.IsoEra.CE;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import java.lang.annotation.RetentionPolicy;
import java.time.chrono.IsoEra;
class A {
{
// The `List`-accepting overload is currently ignored.
Ordering.explicit(ImmutableList.of(RetentionPolicy.SOURCE, RetentionPolicy.CLASS));
Ordering.explicit(IsoEra.BCE, IsoEra.CE);
// BUG: Diagnostic contains: IsoEra.CE
Ordering.explicit(IsoEra.BCE);
// BUG: Diagnostic contains: IsoEra.BCE
Ordering.explicit(IsoEra.CE);
Ordering.explicit(RetentionPolicy.SOURCE, RetentionPolicy.CLASS, RetentionPolicy.RUNTIME);
// BUG: Diagnostic contains: RetentionPolicy.CLASS, RetentionPolicy.RUNTIME
Ordering.explicit(RetentionPolicy.SOURCE);
// BUG: Diagnostic contains: RetentionPolicy.SOURCE, RetentionPolicy.RUNTIME
Ordering.explicit(RetentionPolicy.CLASS);
// BUG: Diagnostic contains: RetentionPolicy.SOURCE, RetentionPolicy.CLASS
Ordering.explicit(RetentionPolicy.RUNTIME);
// BUG: Diagnostic contains: RetentionPolicy.RUNTIME
Ordering.explicit(RetentionPolicy.SOURCE, RetentionPolicy.CLASS);
// BUG: Diagnostic contains: RetentionPolicy.CLASS
Ordering.explicit(RetentionPolicy.SOURCE, RetentionPolicy.RUNTIME);
// BUG: Diagnostic contains: RetentionPolicy.SOURCE
Ordering.explicit(RetentionPolicy.CLASS, RetentionPolicy.RUNTIME);
Ordering.explicit(BCE, CE);
// BUG: Diagnostic contains: IsoEra.CE
Ordering.explicit(BCE);
// BUG: Diagnostic contains: IsoEra.BCE
Ordering.explicit(CE);
Ordering.explicit(SOURCE, CLASS, RUNTIME);
// BUG: Diagnostic contains: RetentionPolicy.CLASS, RetentionPolicy.RUNTIME
Ordering.explicit(SOURCE);
// BUG: Diagnostic contains: RetentionPolicy.SOURCE, RetentionPolicy.RUNTIME
Ordering.explicit(CLASS);
// BUG: Diagnostic contains: RetentionPolicy.SOURCE, RetentionPolicy.CLASS
Ordering.explicit(RUNTIME);
// BUG: Diagnostic contains: RetentionPolicy.RUNTIME
Ordering.explicit(SOURCE, CLASS);
// BUG: Diagnostic contains: RetentionPolicy.CLASS
Ordering.explicit(SOURCE, RUNTIME);
// BUG: Diagnostic contains: RetentionPolicy.SOURCE
Ordering.explicit(CLASS, RUNTIME);
Ordering.explicit(RetentionPolicy.SOURCE, BCE, RetentionPolicy.CLASS, CE, RUNTIME);
Ordering.explicit(SOURCE, IsoEra.BCE, CLASS, IsoEra.CE, RetentionPolicy.RUNTIME);
// BUG: Diagnostic contains: RetentionPolicy.CLASS
Ordering.explicit(RetentionPolicy.SOURCE, BCE, CE, RUNTIME);
// BUG: Diagnostic contains: RetentionPolicy.CLASS
Ordering.explicit(IsoEra.BCE, SOURCE, IsoEra.CE, RetentionPolicy.RUNTIME);
// BUG: Diagnostic contains: IsoEra.CE, RetentionPolicy.RUNTIME
Ordering.explicit(IsoEra.BCE, SOURCE, RetentionPolicy.CLASS);
// BUG: Diagnostic contains: RetentionPolicy.SOURCE, IsoEra.BCE
Ordering.explicit(CLASS, RUNTIME, CE);
}
}
""")
.doTest();
}
}

View File

@@ -12,55 +12,57 @@ final class FluxFlatMapUsageTest {
CompilationTestHelper.newInstance(FluxFlatMapUsage.class, getClass())
.addSourceLines(
"A.java",
"import java.util.function.BiFunction;",
"import java.util.function.Function;",
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.Mono;",
"",
"class A {",
" void m() {",
" // BUG: Diagnostic contains:",
" Flux.just(1).flatMap(Flux::just);",
" // BUG: Diagnostic contains:",
" Flux.just(1).<String>flatMap(i -> Flux.just(String.valueOf(i)));",
" // BUG: Diagnostic contains:",
" Flux.just(1).flatMapSequential(Flux::just);",
" // BUG: Diagnostic contains:",
" Flux.just(1).<String>flatMapSequential(i -> Flux.just(String.valueOf(i)));",
" // BUG: Diagnostic contains:",
" Flux.just(1, 2).groupBy(i -> i).flatMap(Flux::just);",
" // BUG: Diagnostic contains:",
" Flux.just(1, 2).groupBy(i -> i).<String>flatMap(i -> Flux.just(String.valueOf(i)));",
" // BUG: Diagnostic contains:",
" Flux.just(1, 2).groupBy(i -> i).flatMapSequential(Flux::just);",
" // BUG: Diagnostic contains:",
" Flux.just(1, 2).groupBy(i -> i).<String>flatMapSequential(i -> Flux.just(String.valueOf(i)));",
"",
" Mono.just(1).flatMap(Mono::just);",
" Flux.just(1).concatMap(Flux::just);",
"",
" Flux.just(1).flatMap(Flux::just, 1);",
" Flux.just(1).flatMap(Flux::just, 1, 1);",
" Flux.just(1).flatMap(Flux::just, throwable -> Flux.empty(), Flux::empty);",
"",
" Flux.just(1).flatMapSequential(Flux::just, 1);",
" Flux.just(1).flatMapSequential(Flux::just, 1, 1);",
"",
" // BUG: Diagnostic contains:",
" this.<String, Flux<String>>sink(Flux::flatMap);",
" // BUG: Diagnostic contains:",
" this.<Integer, Flux<Integer>>sink(Flux::<Integer>flatMap);",
"",
" // BUG: Diagnostic contains:",
" this.<String, Flux<String>>sink(Flux::flatMapSequential);",
" // BUG: Diagnostic contains:",
" this.<Integer, Flux<Integer>>sink(Flux::<Integer>flatMapSequential);",
"",
" this.<String, Mono<String>>sink(Mono::flatMap);",
" }",
"",
" private <T, P> void sink(BiFunction<P, Function<T, P>, P> fun) {}",
"}")
"""
import java.util.function.BiFunction;
import java.util.function.Function;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
class A {
void m() {
// BUG: Diagnostic contains:
Flux.just(1).flatMap(Flux::just);
// BUG: Diagnostic contains:
Flux.just(1).<String>flatMap(i -> Flux.just(String.valueOf(i)));
// BUG: Diagnostic contains:
Flux.just(1).flatMapSequential(Flux::just);
// BUG: Diagnostic contains:
Flux.just(1).<String>flatMapSequential(i -> Flux.just(String.valueOf(i)));
// BUG: Diagnostic contains:
Flux.just(1, 2).groupBy(i -> i).flatMap(Flux::just);
// BUG: Diagnostic contains:
Flux.just(1, 2).groupBy(i -> i).<String>flatMap(i -> Flux.just(String.valueOf(i)));
// BUG: Diagnostic contains:
Flux.just(1, 2).groupBy(i -> i).flatMapSequential(Flux::just);
// BUG: Diagnostic contains:
Flux.just(1, 2).groupBy(i -> i).<String>flatMapSequential(i -> Flux.just(String.valueOf(i)));
Mono.just(1).flatMap(Mono::just);
Flux.just(1).concatMap(Flux::just);
Flux.just(1).flatMap(Flux::just, 1);
Flux.just(1).flatMap(Flux::just, 1, 1);
Flux.just(1).flatMap(Flux::just, throwable -> Flux.empty(), Flux::empty);
Flux.just(1).flatMapSequential(Flux::just, 1);
Flux.just(1).flatMapSequential(Flux::just, 1, 1);
// BUG: Diagnostic contains:
this.<String, Flux<String>>sink(Flux::flatMap);
// BUG: Diagnostic contains:
this.<Integer, Flux<Integer>>sink(Flux::<Integer>flatMap);
// BUG: Diagnostic contains:
this.<String, Flux<String>>sink(Flux::flatMapSequential);
// BUG: Diagnostic contains:
this.<Integer, Flux<Integer>>sink(Flux::<Integer>flatMapSequential);
this.<String, Mono<String>>sink(Mono::flatMap);
}
private <T, P> void sink(BiFunction<P, Function<T, P>, P> fun) {}
}
""")
.doTest();
}
@@ -69,32 +71,36 @@ final class FluxFlatMapUsageTest {
BugCheckerRefactoringTestHelper.newInstance(FluxFlatMapUsage.class, getClass())
.addInputLines(
"A.java",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" private static final int MAX_CONCURRENCY = 8;",
"",
" void m() {",
" Flux.just(1).flatMap(Flux::just);",
" Flux.just(1).flatMapSequential(Flux::just);",
" Flux.just(1, 2).groupBy(i -> i).flatMap(Flux::just);",
" Flux.just(1, 2).groupBy(i -> i).flatMapSequential(Flux::just);",
" }",
"}")
"""
import reactor.core.publisher.Flux;
class A {
private static final int MAX_CONCURRENCY = 8;
void m() {
Flux.just(1).flatMap(Flux::just);
Flux.just(1).flatMapSequential(Flux::just);
Flux.just(1, 2).groupBy(i -> i).flatMap(Flux::just);
Flux.just(1, 2).groupBy(i -> i).flatMapSequential(Flux::just);
}
}
""")
.addOutputLines(
"A.java",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" private static final int MAX_CONCURRENCY = 8;",
"",
" void m() {",
" Flux.just(1).concatMap(Flux::just);",
" Flux.just(1).concatMap(Flux::just);",
" Flux.just(1, 2).groupBy(i -> i).flatMap(Flux::just, MAX_CONCURRENCY);",
" Flux.just(1, 2).groupBy(i -> i).flatMapSequential(Flux::just, MAX_CONCURRENCY);",
" }",
"}")
"""
import reactor.core.publisher.Flux;
class A {
private static final int MAX_CONCURRENCY = 8;
void m() {
Flux.just(1).concatMap(Flux::just);
Flux.just(1).concatMap(Flux::just);
Flux.just(1, 2).groupBy(i -> i).flatMap(Flux::just, MAX_CONCURRENCY);
Flux.just(1, 2).groupBy(i -> i).flatMapSequential(Flux::just, MAX_CONCURRENCY);
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
@@ -104,32 +110,36 @@ final class FluxFlatMapUsageTest {
.setFixChooser(FixChoosers.SECOND)
.addInputLines(
"A.java",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" private static final int MAX_CONCURRENCY = 8;",
"",
" void m() {",
" Flux.just(1).flatMap(Flux::just);",
" Flux.just(1).flatMapSequential(Flux::just);",
" Flux.just(1, 2).groupBy(i -> i).flatMap(Flux::just);",
" Flux.just(1, 2).groupBy(i -> i).flatMapSequential(Flux::just);",
" }",
"}")
"""
import reactor.core.publisher.Flux;
class A {
private static final int MAX_CONCURRENCY = 8;
void m() {
Flux.just(1).flatMap(Flux::just);
Flux.just(1).flatMapSequential(Flux::just);
Flux.just(1, 2).groupBy(i -> i).flatMap(Flux::just);
Flux.just(1, 2).groupBy(i -> i).flatMapSequential(Flux::just);
}
}
""")
.addOutputLines(
"A.java",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" private static final int MAX_CONCURRENCY = 8;",
"",
" void m() {",
" Flux.just(1).flatMap(Flux::just, MAX_CONCURRENCY);",
" Flux.just(1).flatMapSequential(Flux::just, MAX_CONCURRENCY);",
" Flux.just(1, 2).groupBy(i -> i).concatMap(Flux::just);",
" Flux.just(1, 2).groupBy(i -> i).concatMap(Flux::just);",
" }",
"}")
"""
import reactor.core.publisher.Flux;
class A {
private static final int MAX_CONCURRENCY = 8;
void m() {
Flux.just(1).flatMap(Flux::just, MAX_CONCURRENCY);
Flux.just(1).flatMapSequential(Flux::just, MAX_CONCURRENCY);
Flux.just(1, 2).groupBy(i -> i).concatMap(Flux::just);
Flux.just(1, 2).groupBy(i -> i).concatMap(Flux::just);
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -21,36 +21,38 @@ final class FluxImplicitBlockTest {
m -> Stream.of("SuppressWarnings", "toImmutableList", "toList").allMatch(m::contains))
.addSourceLines(
"A.java",
"import com.google.common.collect.ImmutableList;",
"import java.util.stream.Stream;",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" // BUG: Diagnostic matches: X",
" Flux.just(1).toIterable();",
" // BUG: Diagnostic matches: X",
" Flux.just(2).toStream();",
" // BUG: Diagnostic matches: X",
" long count = Flux.just(3).toStream().count();",
"",
" Flux.just(4).toIterable(1);",
" Flux.just(5).toIterable(2, null);",
" Flux.just(6).toStream(3);",
" new Foo().toIterable();",
" new Foo().toStream();",
" }",
"",
" class Foo<T> {",
" Iterable<T> toIterable() {",
" return ImmutableList.of();",
" }",
"",
" Stream<T> toStream() {",
" return Stream.empty();",
" }",
" }",
"}")
"""
import com.google.common.collect.ImmutableList;
import java.util.stream.Stream;
import reactor.core.publisher.Flux;
class A {
void m() {
// BUG: Diagnostic matches: X
Flux.just(1).toIterable();
// BUG: Diagnostic matches: X
Flux.just(2).toStream();
// BUG: Diagnostic matches: X
long count = Flux.just(3).toStream().count();
Flux.just(4).toIterable(1);
Flux.just(5).toIterable(2, null);
Flux.just(6).toStream(3);
new Foo().toIterable();
new Foo().toStream();
}
class Foo<T> {
Iterable<T> toIterable() {
return ImmutableList.of();
}
Stream<T> toStream() {
return Stream.empty();
}
}
}
""")
.doTest();
}
@@ -61,16 +63,18 @@ final class FluxImplicitBlockTest {
.expectErrorMessage("X", m -> !m.contains("toImmutableList"))
.addSourceLines(
"A.java",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" // BUG: Diagnostic matches: X",
" Flux.just(1).toIterable();",
" // BUG: Diagnostic matches: X",
" Flux.just(2).toStream();",
" }",
"}")
"""
import reactor.core.publisher.Flux;
class A {
void m() {
// BUG: Diagnostic matches: X
Flux.just(1).toIterable();
// BUG: Diagnostic matches: X
Flux.just(2).toStream();
}
}
""")
.doTest();
}
@@ -79,25 +83,29 @@ final class FluxImplicitBlockTest {
BugCheckerRefactoringTestHelper.newInstance(FluxImplicitBlock.class, getClass())
.addInputLines(
"A.java",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" Flux.just(1).toIterable();",
" Flux.just(2).toStream();",
" }",
"}")
"""
import reactor.core.publisher.Flux;
class A {
void m() {
Flux.just(1).toIterable();
Flux.just(2).toStream();
}
}
""")
.addOutputLines(
"A.java",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" @SuppressWarnings(\"FluxImplicitBlock\")",
" void m() {",
" Flux.just(1).toIterable();",
" Flux.just(2).toStream();",
" }",
"}")
"""
import reactor.core.publisher.Flux;
class A {
@SuppressWarnings("FluxImplicitBlock")
void m() {
Flux.just(1).toIterable();
Flux.just(2).toStream();
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
@@ -107,34 +115,38 @@ final class FluxImplicitBlockTest {
.setFixChooser(SECOND)
.addInputLines(
"A.java",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" Flux.just(1).toIterable();",
" Flux.just(2).toStream();",
" Flux.just(3).toIterable().iterator();",
" Flux.just(4).toStream().count();",
" Flux.just(5) /* a */./* b */ toIterable /* c */(/* d */ ) /* e */;",
" Flux.just(6) /* a */./* b */ toStream /* c */(/* d */ ) /* e */;",
" }",
"}")
"""
import reactor.core.publisher.Flux;
class A {
void m() {
Flux.just(1).toIterable();
Flux.just(2).toStream();
Flux.just(3).toIterable().iterator();
Flux.just(4).toStream().count();
Flux.just(5) /* a */./* b */ toIterable /* c */(/* d */ ) /* e */;
Flux.just(6) /* a */./* b */ toStream /* c */(/* d */ ) /* e */;
}
}
""")
.addOutputLines(
"A.java",
"import static com.google.common.collect.ImmutableList.toImmutableList;",
"",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" Flux.just(1).collect(toImmutableList()).block();",
" Flux.just(2).collect(toImmutableList()).block().stream();",
" Flux.just(3).collect(toImmutableList()).block().iterator();",
" Flux.just(4).collect(toImmutableList()).block().stream().count();",
" Flux.just(5).collect(toImmutableList()).block() /* e */;",
" Flux.just(6).collect(toImmutableList()).block().stream() /* e */;",
" }",
"}")
"""
import static com.google.common.collect.ImmutableList.toImmutableList;
import reactor.core.publisher.Flux;
class A {
void m() {
Flux.just(1).collect(toImmutableList()).block();
Flux.just(2).collect(toImmutableList()).block().stream();
Flux.just(3).collect(toImmutableList()).block().iterator();
Flux.just(4).collect(toImmutableList()).block().stream().count();
Flux.just(5).collect(toImmutableList()).block() /* e */;
Flux.just(6).collect(toImmutableList()).block().stream() /* e */;
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
@@ -144,34 +156,38 @@ final class FluxImplicitBlockTest {
.setFixChooser(THIRD)
.addInputLines(
"A.java",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" Flux.just(1).toIterable();",
" Flux.just(2).toStream();",
" Flux.just(3).toIterable().iterator();",
" Flux.just(4).toStream().count();",
" Flux.just(5) /* a */./* b */ toIterable /* c */(/* d */ ) /* e */;",
" Flux.just(6) /* a */./* b */ toStream /* c */(/* d */ ) /* e */;",
" }",
"}")
"""
import reactor.core.publisher.Flux;
class A {
void m() {
Flux.just(1).toIterable();
Flux.just(2).toStream();
Flux.just(3).toIterable().iterator();
Flux.just(4).toStream().count();
Flux.just(5) /* a */./* b */ toIterable /* c */(/* d */ ) /* e */;
Flux.just(6) /* a */./* b */ toStream /* c */(/* d */ ) /* e */;
}
}
""")
.addOutputLines(
"A.java",
"import static java.util.stream.Collectors.toList;",
"",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" Flux.just(1).collect(toList()).block();",
" Flux.just(2).collect(toList()).block().stream();",
" Flux.just(3).collect(toList()).block().iterator();",
" Flux.just(4).collect(toList()).block().stream().count();",
" Flux.just(5).collect(toList()).block() /* e */;",
" Flux.just(6).collect(toList()).block().stream() /* e */;",
" }",
"}")
"""
import static java.util.stream.Collectors.toList;
import reactor.core.publisher.Flux;
class A {
void m() {
Flux.just(1).collect(toList()).block();
Flux.just(2).collect(toList()).block().stream();
Flux.just(3).collect(toList()).block().iterator();
Flux.just(4).collect(toList()).block().stream().count();
Flux.just(5).collect(toList()).block() /* e */;
Flux.just(6).collect(toList()).block().stream() /* e */;
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,294 +11,296 @@ final class FormatStringConcatenationTest {
CompilationTestHelper.newInstance(FormatStringConcatenation.class, getClass())
.addSourceLines(
"A.java",
"import static com.google.common.base.Preconditions.checkArgument;",
"import static com.google.common.base.Preconditions.checkNotNull;",
"import static com.google.common.base.Preconditions.checkState;",
"import static com.google.common.base.Verify.verify;",
"import static org.assertj.core.api.Assertions.assertThat;",
"import static org.assertj.core.api.SoftAssertions.assertSoftly;",
"",
"import java.util.Formatter;",
"import java.util.Locale;",
"import org.assertj.core.api.Assertions;",
"import org.assertj.core.api.BDDAssertions;",
"import org.assertj.core.api.Fail;",
"import org.assertj.core.api.ThrowableAssertAlternative;",
"import org.assertj.core.api.WithAssertions;",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"import org.slf4j.Marker;",
"",
"class A {",
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
"",
" void negative() {",
" hashCode();",
" equals(new A());",
" equals(toString());",
" equals(0);",
" equals(\"str\");",
" equals(\"str\" + 0);",
" equals(0 + 0);",
" equals(0 - 0);",
" equals(\"str \" + toString());",
" }",
"",
" void assertj() {",
" assertThat(0).overridingErrorMessage(toString());",
" assertThat(0).overridingErrorMessage(\"str\");",
" assertThat(0).overridingErrorMessage(\"str \" + 0);",
" assertThat(0).overridingErrorMessage(\"str %s\", 2 * 3);",
" assertThat(0).overridingErrorMessage(\"str %s\", toString());",
" // BUG: Diagnostic contains:",
" assertThat(0).overridingErrorMessage(\"str \" + hashCode() / 2);",
" // BUG: Diagnostic contains:",
" assertThat(0).overridingErrorMessage((\"str \" + toString()));",
" // BUG: Diagnostic contains:",
" assertThat(0).overridingErrorMessage(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" assertThat(0).overridingErrorMessage(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" assertThat(0).withFailMessage(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" assertThat(0).withFailMessage(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" assertSoftly(softly -> softly.fail(\"str \" + toString()));",
" // BUG: Diagnostic contains:",
" assertSoftly(softly -> softly.fail(\"%s \" + toString(), \"arg\"));",
" assertSoftly(softly -> softly.fail(\"str \" + toString(), new Throwable()));",
"",
" // BUG: Diagnostic contains:",
" assertThat(\"\").isEqualTo(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" assertThat(\"\").isEqualTo(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" assertThat(new Error()).hasMessage(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" assertThat(new Error()).hasMessage(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" assertThat(new Error()).hasMessageContaining(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" assertThat(new Error()).hasMessageContaining(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" assertThat(new Error()).hasMessageEndingWith(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" assertThat(new Error()).hasMessageEndingWith(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" assertThat(new Error()).hasMessageStartingWith(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" assertThat(new Error()).hasMessageStartingWith(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" assertThat(new Error()).hasRootCauseMessage(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" assertThat(new Error()).hasRootCauseMessage(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" assertThat(new Error()).hasStackTraceContaining(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" assertThat(new Error()).hasStackTraceContaining(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" assertThat(0).as(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" assertThat(0).as(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" assertThat(0).describedAs(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" assertThat(0).describedAs(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" ((ThrowableAssertAlternative) null).withMessage(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" ((ThrowableAssertAlternative) null).withMessage(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" ((ThrowableAssertAlternative) null).withMessageContaining(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" ((ThrowableAssertAlternative) null).withMessageContaining(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" ((ThrowableAssertAlternative) null).withMessageEndingWith(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" ((ThrowableAssertAlternative) null).withMessageEndingWith(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" ((ThrowableAssertAlternative) null).withMessageStartingWith(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" ((ThrowableAssertAlternative) null).withMessageStartingWith(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" ((ThrowableAssertAlternative) null).withStackTraceContaining(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" ((ThrowableAssertAlternative) null).withStackTraceContaining(\"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" ((WithAssertions) null).fail(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" ((WithAssertions) null).fail(\"%s \" + toString(), \"arg\");",
" ((WithAssertions) null).fail(\"str \" + toString(), new Throwable());",
"",
" // BUG: Diagnostic contains:",
" Assertions.fail(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" Assertions.fail(\"%s \" + toString(), \"arg\");",
" Assertions.fail(\"str \" + toString(), new Throwable());",
"",
" // BUG: Diagnostic contains:",
" BDDAssertions.fail(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" BDDAssertions.fail(\"%s \" + toString(), \"arg\");",
" BDDAssertions.fail(\"str \" + toString(), new Throwable());",
"",
" // BUG: Diagnostic contains:",
" Fail.fail(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" Fail.fail(\"%s \" + toString(), \"arg\");",
" Fail.fail(\"str \" + toString(), new Throwable());",
" }",
"",
" void guava() {",
" checkArgument(true);",
" checkArgument(true, toString());",
" checkArgument(true, \"str\");",
" checkArgument(true, \"str \" + 0);",
" checkArgument(true, \"str %s\", 2 * 3);",
" checkArgument(true, \"str %s\", toString());",
" // BUG: Diagnostic contains:",
" checkArgument(true, \"str \" + hashCode() / 2);",
" // BUG: Diagnostic contains:",
" checkArgument(true, (\"str \" + toString()));",
" // BUG: Diagnostic contains:",
" checkArgument(true, \"str \" + toString());",
" // BUG: Diagnostic contains:",
" checkArgument(true, \"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" checkNotNull(true, \"str \" + toString());",
" // BUG: Diagnostic contains:",
" checkNotNull(true, \"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" checkState(true, \"str \" + toString());",
" // BUG: Diagnostic contains:",
" checkState(true, \"%s \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" verify(true, \"str \" + toString());",
" // BUG: Diagnostic contains:",
" verify(true, \"%s \" + toString(), \"arg\");",
" }",
"",
" void jdk() {",
" String.format(\"str\");",
" String.format(\"str \" + 0);",
" String.format(\"str {}\", 2 * 3);",
" String.format(\"str {}\", toString());",
" // BUG: Diagnostic contains:",
" String.format(\"str \" + hashCode() / 2);",
" // BUG: Diagnostic contains:",
" String.format((\"str \" + toString()));",
" // BUG: Diagnostic contains:",
" String.format(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" String.format(\"{} \" + toString(), \"arg\");",
"",
" String.format(Locale.ROOT, \"str\");",
" String.format(Locale.ROOT, \"str \" + 0);",
" String.format(Locale.ROOT, \"str {}\", 2 * 3);",
" String.format(Locale.ROOT, \"str {}\", toString());",
" // BUG: Diagnostic contains:",
" String.format(Locale.ROOT, (\"str \" + toString()));",
" // BUG: Diagnostic contains:",
" String.format(Locale.ROOT, \"str \" + toString());",
" // BUG: Diagnostic contains:",
" String.format(Locale.ROOT, \"{} \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" new Formatter().format(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" new Formatter().format(\"{} \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" new Formatter().format(Locale.ROOT, \"str \" + toString());",
" // BUG: Diagnostic contains:",
" new Formatter().format(Locale.ROOT, \"{} \" + toString(), \"arg\");",
" }",
"",
" void slf4j() {",
" LOG.debug(\"str\");",
" LOG.debug(\"str \" + 0);",
" LOG.debug(\"str {}\", 2 * 3);",
" LOG.debug(\"str {}\", toString());",
" // BUG: Diagnostic contains:",
" LOG.debug(\"str \" + hashCode() / 2);",
" // BUG: Diagnostic contains:",
" LOG.debug((\"str \" + toString()));",
" // BUG: Diagnostic contains:",
" LOG.debug(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" LOG.debug(\"{} \" + toString(), \"arg\");",
"",
" LOG.debug((Marker) null, \"str\");",
" LOG.debug((Marker) null, \"str \" + 0);",
" LOG.debug((Marker) null, \"str {}\", 2 * 3);",
" LOG.debug((Marker) null, \"str {}\", toString());",
" // BUG: Diagnostic contains:",
" LOG.debug((Marker) null, (\"str \" + toString()));",
" // BUG: Diagnostic contains:",
" LOG.debug((Marker) null, \"str \" + toString());",
" // BUG: Diagnostic contains:",
" LOG.debug((Marker) null, \"{} \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" LOG.error(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" LOG.error(\"{} \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" LOG.error((Marker) null, \"str \" + toString());",
" // BUG: Diagnostic contains:",
" LOG.error((Marker) null, \"{} \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" LOG.info(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" LOG.info(\"{} \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" LOG.info((Marker) null, \"str \" + toString());",
" // BUG: Diagnostic contains:",
" LOG.info((Marker) null, \"{} \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" LOG.trace(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" LOG.trace(\"{} \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" LOG.trace((Marker) null, \"str \" + toString());",
" // BUG: Diagnostic contains:",
" LOG.trace((Marker) null, \"{} \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" LOG.warn(\"str \" + toString());",
" // BUG: Diagnostic contains:",
" LOG.warn(\"{} \" + toString(), \"arg\");",
"",
" // BUG: Diagnostic contains:",
" LOG.warn((Marker) null, \"str \" + toString());",
" // BUG: Diagnostic contains:",
" LOG.warn((Marker) null, \"{} \" + toString(), \"arg\");",
" }",
"}")
"""
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.SoftAssertions.assertSoftly;
import java.util.Formatter;
import java.util.Locale;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.BDDAssertions;
import org.assertj.core.api.Fail;
import org.assertj.core.api.ThrowableAssertAlternative;
import org.assertj.core.api.WithAssertions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
class A {
private static final Logger LOG = LoggerFactory.getLogger(A.class);
void negative() {
hashCode();
equals(new A());
equals(toString());
equals(0);
equals("str");
equals("str" + 0);
equals(0 + 0);
equals(0 - 0);
equals("str " + toString());
}
void assertj() {
assertThat(0).overridingErrorMessage(toString());
assertThat(0).overridingErrorMessage("str");
assertThat(0).overridingErrorMessage("str " + 0);
assertThat(0).overridingErrorMessage("str %s", 2 * 3);
assertThat(0).overridingErrorMessage("str %s", toString());
// BUG: Diagnostic contains:
assertThat(0).overridingErrorMessage("str " + hashCode() / 2);
// BUG: Diagnostic contains:
assertThat(0).overridingErrorMessage(("str " + toString()));
// BUG: Diagnostic contains:
assertThat(0).overridingErrorMessage("str " + toString());
// BUG: Diagnostic contains:
assertThat(0).overridingErrorMessage("%s " + toString(), "arg");
// BUG: Diagnostic contains:
assertThat(0).withFailMessage("str " + toString());
// BUG: Diagnostic contains:
assertThat(0).withFailMessage("%s " + toString(), "arg");
// BUG: Diagnostic contains:
assertSoftly(softly -> softly.fail("str " + toString()));
// BUG: Diagnostic contains:
assertSoftly(softly -> softly.fail("%s " + toString(), "arg"));
assertSoftly(softly -> softly.fail("str " + toString(), new Throwable()));
// BUG: Diagnostic contains:
assertThat("").isEqualTo("str " + toString());
// BUG: Diagnostic contains:
assertThat("").isEqualTo("%s " + toString(), "arg");
// BUG: Diagnostic contains:
assertThat(new Error()).hasMessage("str " + toString());
// BUG: Diagnostic contains:
assertThat(new Error()).hasMessage("%s " + toString(), "arg");
// BUG: Diagnostic contains:
assertThat(new Error()).hasMessageContaining("str " + toString());
// BUG: Diagnostic contains:
assertThat(new Error()).hasMessageContaining("%s " + toString(), "arg");
// BUG: Diagnostic contains:
assertThat(new Error()).hasMessageEndingWith("str " + toString());
// BUG: Diagnostic contains:
assertThat(new Error()).hasMessageEndingWith("%s " + toString(), "arg");
// BUG: Diagnostic contains:
assertThat(new Error()).hasMessageStartingWith("str " + toString());
// BUG: Diagnostic contains:
assertThat(new Error()).hasMessageStartingWith("%s " + toString(), "arg");
// BUG: Diagnostic contains:
assertThat(new Error()).hasRootCauseMessage("str " + toString());
// BUG: Diagnostic contains:
assertThat(new Error()).hasRootCauseMessage("%s " + toString(), "arg");
// BUG: Diagnostic contains:
assertThat(new Error()).hasStackTraceContaining("str " + toString());
// BUG: Diagnostic contains:
assertThat(new Error()).hasStackTraceContaining("%s " + toString(), "arg");
// BUG: Diagnostic contains:
assertThat(0).as("str " + toString());
// BUG: Diagnostic contains:
assertThat(0).as("%s " + toString(), "arg");
// BUG: Diagnostic contains:
assertThat(0).describedAs("str " + toString());
// BUG: Diagnostic contains:
assertThat(0).describedAs("%s " + toString(), "arg");
// BUG: Diagnostic contains:
((ThrowableAssertAlternative) null).withMessage("str " + toString());
// BUG: Diagnostic contains:
((ThrowableAssertAlternative) null).withMessage("%s " + toString(), "arg");
// BUG: Diagnostic contains:
((ThrowableAssertAlternative) null).withMessageContaining("str " + toString());
// BUG: Diagnostic contains:
((ThrowableAssertAlternative) null).withMessageContaining("%s " + toString(), "arg");
// BUG: Diagnostic contains:
((ThrowableAssertAlternative) null).withMessageEndingWith("str " + toString());
// BUG: Diagnostic contains:
((ThrowableAssertAlternative) null).withMessageEndingWith("%s " + toString(), "arg");
// BUG: Diagnostic contains:
((ThrowableAssertAlternative) null).withMessageStartingWith("str " + toString());
// BUG: Diagnostic contains:
((ThrowableAssertAlternative) null).withMessageStartingWith("%s " + toString(), "arg");
// BUG: Diagnostic contains:
((ThrowableAssertAlternative) null).withStackTraceContaining("str " + toString());
// BUG: Diagnostic contains:
((ThrowableAssertAlternative) null).withStackTraceContaining("%s " + toString(), "arg");
// BUG: Diagnostic contains:
((WithAssertions) null).fail("str " + toString());
// BUG: Diagnostic contains:
((WithAssertions) null).fail("%s " + toString(), "arg");
((WithAssertions) null).fail("str " + toString(), new Throwable());
// BUG: Diagnostic contains:
Assertions.fail("str " + toString());
// BUG: Diagnostic contains:
Assertions.fail("%s " + toString(), "arg");
Assertions.fail("str " + toString(), new Throwable());
// BUG: Diagnostic contains:
BDDAssertions.fail("str " + toString());
// BUG: Diagnostic contains:
BDDAssertions.fail("%s " + toString(), "arg");
BDDAssertions.fail("str " + toString(), new Throwable());
// BUG: Diagnostic contains:
Fail.fail("str " + toString());
// BUG: Diagnostic contains:
Fail.fail("%s " + toString(), "arg");
Fail.fail("str " + toString(), new Throwable());
}
void guava() {
checkArgument(true);
checkArgument(true, toString());
checkArgument(true, "str");
checkArgument(true, "str " + 0);
checkArgument(true, "str %s", 2 * 3);
checkArgument(true, "str %s", toString());
// BUG: Diagnostic contains:
checkArgument(true, "str " + hashCode() / 2);
// BUG: Diagnostic contains:
checkArgument(true, ("str " + toString()));
// BUG: Diagnostic contains:
checkArgument(true, "str " + toString());
// BUG: Diagnostic contains:
checkArgument(true, "%s " + toString(), "arg");
// BUG: Diagnostic contains:
checkNotNull(true, "str " + toString());
// BUG: Diagnostic contains:
checkNotNull(true, "%s " + toString(), "arg");
// BUG: Diagnostic contains:
checkState(true, "str " + toString());
// BUG: Diagnostic contains:
checkState(true, "%s " + toString(), "arg");
// BUG: Diagnostic contains:
verify(true, "str " + toString());
// BUG: Diagnostic contains:
verify(true, "%s " + toString(), "arg");
}
void jdk() {
String.format("str");
String.format("str " + 0);
String.format("str {}", 2 * 3);
String.format("str {}", toString());
// BUG: Diagnostic contains:
String.format("str " + hashCode() / 2);
// BUG: Diagnostic contains:
String.format(("str " + toString()));
// BUG: Diagnostic contains:
String.format("str " + toString());
// BUG: Diagnostic contains:
String.format("{} " + toString(), "arg");
String.format(Locale.ROOT, "str");
String.format(Locale.ROOT, "str " + 0);
String.format(Locale.ROOT, "str {}", 2 * 3);
String.format(Locale.ROOT, "str {}", toString());
// BUG: Diagnostic contains:
String.format(Locale.ROOT, ("str " + toString()));
// BUG: Diagnostic contains:
String.format(Locale.ROOT, "str " + toString());
// BUG: Diagnostic contains:
String.format(Locale.ROOT, "{} " + toString(), "arg");
// BUG: Diagnostic contains:
new Formatter().format("str " + toString());
// BUG: Diagnostic contains:
new Formatter().format("{} " + toString(), "arg");
// BUG: Diagnostic contains:
new Formatter().format(Locale.ROOT, "str " + toString());
// BUG: Diagnostic contains:
new Formatter().format(Locale.ROOT, "{} " + toString(), "arg");
}
void slf4j() {
LOG.debug("str");
LOG.debug("str " + 0);
LOG.debug("str {}", 2 * 3);
LOG.debug("str {}", toString());
// BUG: Diagnostic contains:
LOG.debug("str " + hashCode() / 2);
// BUG: Diagnostic contains:
LOG.debug(("str " + toString()));
// BUG: Diagnostic contains:
LOG.debug("str " + toString());
// BUG: Diagnostic contains:
LOG.debug("{} " + toString(), "arg");
LOG.debug((Marker) null, "str");
LOG.debug((Marker) null, "str " + 0);
LOG.debug((Marker) null, "str {}", 2 * 3);
LOG.debug((Marker) null, "str {}", toString());
// BUG: Diagnostic contains:
LOG.debug((Marker) null, ("str " + toString()));
// BUG: Diagnostic contains:
LOG.debug((Marker) null, "str " + toString());
// BUG: Diagnostic contains:
LOG.debug((Marker) null, "{} " + toString(), "arg");
// BUG: Diagnostic contains:
LOG.error("str " + toString());
// BUG: Diagnostic contains:
LOG.error("{} " + toString(), "arg");
// BUG: Diagnostic contains:
LOG.error((Marker) null, "str " + toString());
// BUG: Diagnostic contains:
LOG.error((Marker) null, "{} " + toString(), "arg");
// BUG: Diagnostic contains:
LOG.info("str " + toString());
// BUG: Diagnostic contains:
LOG.info("{} " + toString(), "arg");
// BUG: Diagnostic contains:
LOG.info((Marker) null, "str " + toString());
// BUG: Diagnostic contains:
LOG.info((Marker) null, "{} " + toString(), "arg");
// BUG: Diagnostic contains:
LOG.trace("str " + toString());
// BUG: Diagnostic contains:
LOG.trace("{} " + toString(), "arg");
// BUG: Diagnostic contains:
LOG.trace((Marker) null, "str " + toString());
// BUG: Diagnostic contains:
LOG.trace((Marker) null, "{} " + toString(), "arg");
// BUG: Diagnostic contains:
LOG.warn("str " + toString());
// BUG: Diagnostic contains:
LOG.warn("{} " + toString(), "arg");
// BUG: Diagnostic contains:
LOG.warn((Marker) null, "str " + toString());
// BUG: Diagnostic contains:
LOG.warn((Marker) null, "{} " + toString(), "arg");
}
}
""")
.doTest();
}
@@ -307,102 +309,106 @@ final class FormatStringConcatenationTest {
BugCheckerRefactoringTestHelper.newInstance(FormatStringConcatenation.class, getClass())
.addInputLines(
"A.java",
"import static com.google.common.base.Preconditions.checkArgument;",
"import static org.assertj.core.api.Assertions.assertThat;",
"",
"import java.util.Locale;",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"import org.slf4j.Marker;",
"",
"class A {",
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
"",
" void assertj() {",
" assertThat(0).overridingErrorMessage(toString() + \" str\");",
" assertThat(0).overridingErrorMessage(\"str \" + toString());",
" assertThat(0).overridingErrorMessage(toString() + toString());",
" assertThat(0).overridingErrorMessage(\"str \" + toString() + \" word \" + new A().hashCode());",
" assertThat(0).overridingErrorMessage(\"str \" + (toString() + \" word \") + (hashCode() / 2));",
"",
" // Flagged but not auto-fixed.",
" assertThat(0).overridingErrorMessage(\"%s \" + toString(), \"arg\");",
" }",
"",
" void guava() {",
" checkArgument(true, \"str \" + toString());",
"",
" // Flagged but not auto-fixed.",
" checkArgument(true, \"%s \" + toString(), \"arg\");",
" }",
"",
" void jdk() {",
" String.format(\"str \" + toString());",
" String.format(Locale.ROOT, \"str \" + toString());",
"",
" // Flagged but not auto-fixed.",
" String.format(\"{} \" + toString(), \"arg\");",
" String.format(Locale.ROOT, \"{} \" + toString(), \"arg\");",
" }",
"",
" void slf4j() {",
" LOG.debug(\"str \" + toString());",
" LOG.debug((Marker) null, \"str \" + toString());",
"",
" // Flagged but not auto-fixed.",
" LOG.debug(\"{} \" + toString(), \"arg\");",
" LOG.debug((Marker) null, \"{} \" + toString(), \"arg\");",
" }",
"}")
"""
import static com.google.common.base.Preconditions.checkArgument;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
class A {
private static final Logger LOG = LoggerFactory.getLogger(A.class);
void assertj() {
assertThat(0).overridingErrorMessage(toString() + " str");
assertThat(0).overridingErrorMessage("str " + toString());
assertThat(0).overridingErrorMessage(toString() + toString());
assertThat(0).overridingErrorMessage("str " + toString() + " word " + new A().hashCode());
assertThat(0).overridingErrorMessage("str " + (toString() + " word ") + (hashCode() / 2));
// Flagged but not auto-fixed.
assertThat(0).overridingErrorMessage("%s " + toString(), "arg");
}
void guava() {
checkArgument(true, "str " + toString());
// Flagged but not auto-fixed.
checkArgument(true, "%s " + toString(), "arg");
}
void jdk() {
String.format("str " + toString());
String.format(Locale.ROOT, "str " + toString());
// Flagged but not auto-fixed.
String.format("{} " + toString(), "arg");
String.format(Locale.ROOT, "{} " + toString(), "arg");
}
void slf4j() {
LOG.debug("str " + toString());
LOG.debug((Marker) null, "str " + toString());
// Flagged but not auto-fixed.
LOG.debug("{} " + toString(), "arg");
LOG.debug((Marker) null, "{} " + toString(), "arg");
}
}
""")
.addOutputLines(
"A.java",
"import static com.google.common.base.Preconditions.checkArgument;",
"import static org.assertj.core.api.Assertions.assertThat;",
"",
"import java.util.Locale;",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"import org.slf4j.Marker;",
"",
"class A {",
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
"",
" void assertj() {",
" assertThat(0).overridingErrorMessage(\"%s str\", toString());",
" assertThat(0).overridingErrorMessage(\"str %s\", toString());",
" assertThat(0).overridingErrorMessage(\"%s%s\", toString(), toString());",
" assertThat(0).overridingErrorMessage(\"str %s word %s\", toString(), new A().hashCode());",
" assertThat(0).overridingErrorMessage(\"str %s word %s\", toString(), hashCode() / 2);",
"",
" // Flagged but not auto-fixed.",
" assertThat(0).overridingErrorMessage(\"%s \" + toString(), \"arg\");",
" }",
"",
" void guava() {",
" checkArgument(true, \"str %s\", toString());",
"",
" // Flagged but not auto-fixed.",
" checkArgument(true, \"%s \" + toString(), \"arg\");",
" }",
"",
" void jdk() {",
" String.format(\"str %s\", toString());",
" String.format(Locale.ROOT, \"str %s\", toString());",
"",
" // Flagged but not auto-fixed.",
" String.format(\"{} \" + toString(), \"arg\");",
" String.format(Locale.ROOT, \"{} \" + toString(), \"arg\");",
" }",
"",
" void slf4j() {",
" LOG.debug(\"str {}\", toString());",
" LOG.debug((Marker) null, \"str {}\", toString());",
"",
" // Flagged but not auto-fixed.",
" LOG.debug(\"{} \" + toString(), \"arg\");",
" LOG.debug((Marker) null, \"{} \" + toString(), \"arg\");",
" }",
"}")
"""
import static com.google.common.base.Preconditions.checkArgument;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
class A {
private static final Logger LOG = LoggerFactory.getLogger(A.class);
void assertj() {
assertThat(0).overridingErrorMessage("%s str", toString());
assertThat(0).overridingErrorMessage("str %s", toString());
assertThat(0).overridingErrorMessage("%s%s", toString(), toString());
assertThat(0).overridingErrorMessage("str %s word %s", toString(), new A().hashCode());
assertThat(0).overridingErrorMessage("str %s word %s", toString(), hashCode() / 2);
// Flagged but not auto-fixed.
assertThat(0).overridingErrorMessage("%s " + toString(), "arg");
}
void guava() {
checkArgument(true, "str %s", toString());
// Flagged but not auto-fixed.
checkArgument(true, "%s " + toString(), "arg");
}
void jdk() {
String.format("str %s", toString());
String.format(Locale.ROOT, "str %s", toString());
// Flagged but not auto-fixed.
String.format("{} " + toString(), "arg");
String.format(Locale.ROOT, "{} " + toString(), "arg");
}
void slf4j() {
LOG.debug("str {}", toString());
LOG.debug((Marker) null, "str {}", toString());
// Flagged but not auto-fixed.
LOG.debug("{} " + toString(), "arg");
LOG.debug((Marker) null, "{} " + toString(), "arg");
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -12,168 +12,170 @@ final class IdentityConversionTest {
CompilationTestHelper.newInstance(IdentityConversion.class, getClass())
.addSourceLines(
"A.java",
"import static com.google.errorprone.matchers.Matchers.instanceMethod;",
"import static com.google.errorprone.matchers.Matchers.staticMethod;",
"",
"import com.google.common.collect.ImmutableBiMap;",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableListMultimap;",
"import com.google.common.collect.ImmutableMap;",
"import com.google.common.collect.ImmutableMultimap;",
"import com.google.common.collect.ImmutableMultiset;",
"import com.google.common.collect.ImmutableRangeMap;",
"import com.google.common.collect.ImmutableRangeSet;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.common.collect.ImmutableSetMultimap;",
"import com.google.common.collect.ImmutableTable;",
"import com.google.errorprone.matchers.Matcher;",
"import com.google.errorprone.matchers.Matchers;",
"import reactor.adapter.rxjava.RxJava2Adapter;",
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.Mono;",
"",
"public final class A {",
" public void m() {",
" // BUG: Diagnostic contains:",
" Boolean b1 = Boolean.valueOf(Boolean.FALSE);",
" // BUG: Diagnostic contains:",
" Boolean b2 = Boolean.valueOf(false);",
" // BUG: Diagnostic contains:",
" boolean b3 = Boolean.valueOf(Boolean.FALSE);",
" // BUG: Diagnostic contains:",
" boolean b4 = Boolean.valueOf(false);",
"",
" // BUG: Diagnostic contains:",
" Byte byte1 = Byte.valueOf((Byte) Byte.MIN_VALUE);",
" // BUG: Diagnostic contains:",
" Byte byte2 = Byte.valueOf(Byte.MIN_VALUE);",
" // BUG: Diagnostic contains:",
" byte byte3 = Byte.valueOf((Byte) Byte.MIN_VALUE);",
" // BUG: Diagnostic contains:",
" byte byte4 = Byte.valueOf(Byte.MIN_VALUE);",
"",
" // BUG: Diagnostic contains:",
" Character c1 = Character.valueOf((Character) 'a');",
" // BUG: Diagnostic contains:",
" Character c2 = Character.valueOf('a');",
" // BUG: Diagnostic contains:",
" char c3 = Character.valueOf((Character) 'a');",
" // BUG: Diagnostic contains:",
" char c4 = Character.valueOf('a');",
"",
" // BUG: Diagnostic contains:",
" Double d1 = Double.valueOf((Double) 0.0);",
" // BUG: Diagnostic contains:",
" Double d2 = Double.valueOf(0.0);",
" // BUG: Diagnostic contains:",
" double d3 = Double.valueOf((Double) 0.0);",
" // BUG: Diagnostic contains:",
" double d4 = Double.valueOf(0.0);",
"",
" // BUG: Diagnostic contains:",
" Float f1 = Float.valueOf((Float) 0.0F);",
" // BUG: Diagnostic contains:",
" Float f2 = Float.valueOf(0.0F);",
" // BUG: Diagnostic contains:",
" float f3 = Float.valueOf((Float) 0.0F);",
" // BUG: Diagnostic contains:",
" float f4 = Float.valueOf(0.0F);",
"",
" // BUG: Diagnostic contains:",
" Integer i1 = Integer.valueOf((Integer) 1);",
" // BUG: Diagnostic contains:",
" Integer i2 = Integer.valueOf(1);",
" // BUG: Diagnostic contains:",
" int i3 = Integer.valueOf((Integer) 1);",
" // BUG: Diagnostic contains:",
" int i4 = Integer.valueOf(1);",
"",
" // BUG: Diagnostic contains:",
" Long l1 = Long.valueOf((Long) 1L);",
" // BUG: Diagnostic contains:",
" Long l2 = Long.valueOf(1L);",
" // BUG: Diagnostic contains:",
" long l3 = Long.valueOf((Long) 1L);",
" // BUG: Diagnostic contains:",
" long l4 = Long.valueOf(1L);",
"",
" Long l5 = Long.valueOf((Integer) 1);",
" Long l6 = Long.valueOf(1);",
" // BUG: Diagnostic contains:",
" long l7 = Long.valueOf((Integer) 1);",
" // BUG: Diagnostic contains:",
" long l8 = Long.valueOf(1);",
"",
" // BUG: Diagnostic contains:",
" Short s1 = Short.valueOf((Short) Short.MIN_VALUE);",
" // BUG: Diagnostic contains:",
" Short s2 = Short.valueOf(Short.MIN_VALUE);",
" // BUG: Diagnostic contains:",
" short s3 = Short.valueOf((Short) Short.MIN_VALUE);",
" // BUG: Diagnostic contains:",
" short s4 = Short.valueOf(Short.MIN_VALUE);",
"",
" // BUG: Diagnostic contains:",
" String boolStr = Boolean.valueOf(Boolean.FALSE).toString();",
" int boolHash = Boolean.valueOf(false).hashCode();",
" // BUG: Diagnostic contains:",
" int byteHash = Byte.valueOf((Byte) Byte.MIN_VALUE).hashCode();",
" String byteStr = Byte.valueOf(Byte.MIN_VALUE).toString();",
"",
" String str1 = String.valueOf(0);",
" // BUG: Diagnostic contains:",
" String str2 = String.valueOf(\"1\");",
"",
" // BUG: Diagnostic contains:",
" ImmutableBiMap<Object, Object> o1 = ImmutableBiMap.copyOf(ImmutableBiMap.of());",
" // BUG: Diagnostic contains:",
" ImmutableList<Object> o2 = ImmutableList.copyOf(ImmutableList.of());",
" ImmutableListMultimap<Object, Object> o3 =",
" // BUG: Diagnostic contains:",
" ImmutableListMultimap.copyOf(ImmutableListMultimap.of());",
" // BUG: Diagnostic contains:",
" ImmutableMap<Object, Object> o4 = ImmutableMap.copyOf(ImmutableMap.of());",
" // BUG: Diagnostic contains:",
" ImmutableMultimap<Object, Object> o5 = ImmutableMultimap.copyOf(ImmutableMultimap.of());",
" // BUG: Diagnostic contains:",
" ImmutableMultiset<Object> o6 = ImmutableMultiset.copyOf(ImmutableMultiset.of());",
" // BUG: Diagnostic contains:",
" ImmutableRangeMap<String, Object> o7 = ImmutableRangeMap.copyOf(ImmutableRangeMap.of());",
" // BUG: Diagnostic contains:",
" ImmutableRangeSet<String> o8 = ImmutableRangeSet.copyOf(ImmutableRangeSet.of());",
" // BUG: Diagnostic contains:",
" ImmutableSet<Object> o9 = ImmutableSet.copyOf(ImmutableSet.of());",
" ImmutableSetMultimap<Object, Object> o10 =",
" // BUG: Diagnostic contains:",
" ImmutableSetMultimap.copyOf(ImmutableSetMultimap.of());",
" // BUG: Diagnostic contains:",
" ImmutableTable<Object, Object, Object> o11 = ImmutableTable.copyOf(ImmutableTable.of());",
"",
" // BUG: Diagnostic contains:",
" Matcher allOf1 = Matchers.allOf(instanceMethod());",
" Matcher allOf2 = Matchers.allOf(instanceMethod(), staticMethod());",
" // BUG: Diagnostic contains:",
" Matcher anyOf1 = Matchers.anyOf(staticMethod());",
" Matcher anyOf2 = Matchers.anyOf(instanceMethod(), staticMethod());",
"",
" // BUG: Diagnostic contains:",
" Flux<Integer> flux1 = Flux.just(1).flatMap(e -> RxJava2Adapter.fluxToFlowable(Flux.just(2)));",
"",
" // BUG: Diagnostic contains:",
" Flux<Integer> flux2 = Flux.concat(Flux.just(1));",
" // BUG: Diagnostic contains:",
" Flux<Integer> flux3 = Flux.firstWithSignal(Flux.just(1));",
" // BUG: Diagnostic contains:",
" Flux<Integer> flux4 = Flux.from(Flux.just(1));",
" // BUG: Diagnostic contains:",
" Flux<Integer> flux5 = Flux.merge(Flux.just(1));",
"",
" // BUG: Diagnostic contains:",
" Mono<Integer> mono1 = Mono.from(Mono.just(1));",
" // BUG: Diagnostic contains:",
" Mono<Integer> mono2 = Mono.fromDirect(Mono.just(1));",
" }",
"}")
"""
import static com.google.errorprone.matchers.Matchers.instanceMethod;
import static com.google.errorprone.matchers.Matchers.staticMethod;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableRangeMap;
import com.google.common.collect.ImmutableRangeSet;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableTable;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import reactor.adapter.rxjava.RxJava2Adapter;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public final class A {
public void m() {
// BUG: Diagnostic contains:
Boolean b1 = Boolean.valueOf(Boolean.FALSE);
// BUG: Diagnostic contains:
Boolean b2 = Boolean.valueOf(false);
// BUG: Diagnostic contains:
boolean b3 = Boolean.valueOf(Boolean.FALSE);
// BUG: Diagnostic contains:
boolean b4 = Boolean.valueOf(false);
// BUG: Diagnostic contains:
Byte byte1 = Byte.valueOf((Byte) Byte.MIN_VALUE);
// BUG: Diagnostic contains:
Byte byte2 = Byte.valueOf(Byte.MIN_VALUE);
// BUG: Diagnostic contains:
byte byte3 = Byte.valueOf((Byte) Byte.MIN_VALUE);
// BUG: Diagnostic contains:
byte byte4 = Byte.valueOf(Byte.MIN_VALUE);
// BUG: Diagnostic contains:
Character c1 = Character.valueOf((Character) 'a');
// BUG: Diagnostic contains:
Character c2 = Character.valueOf('a');
// BUG: Diagnostic contains:
char c3 = Character.valueOf((Character) 'a');
// BUG: Diagnostic contains:
char c4 = Character.valueOf('a');
// BUG: Diagnostic contains:
Double d1 = Double.valueOf((Double) 0.0);
// BUG: Diagnostic contains:
Double d2 = Double.valueOf(0.0);
// BUG: Diagnostic contains:
double d3 = Double.valueOf((Double) 0.0);
// BUG: Diagnostic contains:
double d4 = Double.valueOf(0.0);
// BUG: Diagnostic contains:
Float f1 = Float.valueOf((Float) 0.0F);
// BUG: Diagnostic contains:
Float f2 = Float.valueOf(0.0F);
// BUG: Diagnostic contains:
float f3 = Float.valueOf((Float) 0.0F);
// BUG: Diagnostic contains:
float f4 = Float.valueOf(0.0F);
// BUG: Diagnostic contains:
Integer i1 = Integer.valueOf((Integer) 1);
// BUG: Diagnostic contains:
Integer i2 = Integer.valueOf(1);
// BUG: Diagnostic contains:
int i3 = Integer.valueOf((Integer) 1);
// BUG: Diagnostic contains:
int i4 = Integer.valueOf(1);
// BUG: Diagnostic contains:
Long l1 = Long.valueOf((Long) 1L);
// BUG: Diagnostic contains:
Long l2 = Long.valueOf(1L);
// BUG: Diagnostic contains:
long l3 = Long.valueOf((Long) 1L);
// BUG: Diagnostic contains:
long l4 = Long.valueOf(1L);
Long l5 = Long.valueOf((Integer) 1);
Long l6 = Long.valueOf(1);
// BUG: Diagnostic contains:
long l7 = Long.valueOf((Integer) 1);
// BUG: Diagnostic contains:
long l8 = Long.valueOf(1);
// BUG: Diagnostic contains:
Short s1 = Short.valueOf((Short) Short.MIN_VALUE);
// BUG: Diagnostic contains:
Short s2 = Short.valueOf(Short.MIN_VALUE);
// BUG: Diagnostic contains:
short s3 = Short.valueOf((Short) Short.MIN_VALUE);
// BUG: Diagnostic contains:
short s4 = Short.valueOf(Short.MIN_VALUE);
// BUG: Diagnostic contains:
String boolStr = Boolean.valueOf(Boolean.FALSE).toString();
int boolHash = Boolean.valueOf(false).hashCode();
// BUG: Diagnostic contains:
int byteHash = Byte.valueOf((Byte) Byte.MIN_VALUE).hashCode();
String byteStr = Byte.valueOf(Byte.MIN_VALUE).toString();
String str1 = String.valueOf(0);
// BUG: Diagnostic contains:
String str2 = String.valueOf("1");
// BUG: Diagnostic contains:
ImmutableBiMap<Object, Object> o1 = ImmutableBiMap.copyOf(ImmutableBiMap.of());
// BUG: Diagnostic contains:
ImmutableList<Object> o2 = ImmutableList.copyOf(ImmutableList.of());
ImmutableListMultimap<Object, Object> o3 =
// BUG: Diagnostic contains:
ImmutableListMultimap.copyOf(ImmutableListMultimap.of());
// BUG: Diagnostic contains:
ImmutableMap<Object, Object> o4 = ImmutableMap.copyOf(ImmutableMap.of());
// BUG: Diagnostic contains:
ImmutableMultimap<Object, Object> o5 = ImmutableMultimap.copyOf(ImmutableMultimap.of());
// BUG: Diagnostic contains:
ImmutableMultiset<Object> o6 = ImmutableMultiset.copyOf(ImmutableMultiset.of());
// BUG: Diagnostic contains:
ImmutableRangeMap<String, Object> o7 = ImmutableRangeMap.copyOf(ImmutableRangeMap.of());
// BUG: Diagnostic contains:
ImmutableRangeSet<String> o8 = ImmutableRangeSet.copyOf(ImmutableRangeSet.of());
// BUG: Diagnostic contains:
ImmutableSet<Object> o9 = ImmutableSet.copyOf(ImmutableSet.of());
ImmutableSetMultimap<Object, Object> o10 =
// BUG: Diagnostic contains:
ImmutableSetMultimap.copyOf(ImmutableSetMultimap.of());
// BUG: Diagnostic contains:
ImmutableTable<Object, Object, Object> o11 = ImmutableTable.copyOf(ImmutableTable.of());
// BUG: Diagnostic contains:
Matcher allOf1 = Matchers.allOf(instanceMethod());
Matcher allOf2 = Matchers.allOf(instanceMethod(), staticMethod());
// BUG: Diagnostic contains:
Matcher anyOf1 = Matchers.anyOf(staticMethod());
Matcher anyOf2 = Matchers.anyOf(instanceMethod(), staticMethod());
// BUG: Diagnostic contains:
Flux<Integer> flux1 = Flux.just(1).flatMap(e -> RxJava2Adapter.fluxToFlowable(Flux.just(2)));
// BUG: Diagnostic contains:
Flux<Integer> flux2 = Flux.concat(Flux.just(1));
// BUG: Diagnostic contains:
Flux<Integer> flux3 = Flux.firstWithSignal(Flux.just(1));
// BUG: Diagnostic contains:
Flux<Integer> flux4 = Flux.from(Flux.just(1));
// BUG: Diagnostic contains:
Flux<Integer> flux5 = Flux.merge(Flux.just(1));
// BUG: Diagnostic contains:
Mono<Integer> mono1 = Mono.from(Mono.just(1));
// BUG: Diagnostic contains:
Mono<Integer> mono2 = Mono.fromDirect(Mono.just(1));
}
}
""")
.doTest();
}
@@ -182,104 +184,108 @@ final class IdentityConversionTest {
BugCheckerRefactoringTestHelper.newInstance(IdentityConversion.class, getClass())
.addInputLines(
"A.java",
"import static com.google.errorprone.matchers.Matchers.staticMethod;",
"import static org.mockito.Mockito.when;",
"",
"import com.google.common.collect.ImmutableCollection;",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.errorprone.matchers.Matcher;",
"import com.google.errorprone.matchers.Matchers;",
"import java.util.ArrayList;",
"import java.util.Collection;",
"import org.reactivestreams.Publisher;",
"import reactor.adapter.rxjava.RxJava2Adapter;",
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.Mono;",
"",
"public final class A {",
" public void m() {",
" ImmutableSet<Object> set1 = ImmutableSet.copyOf(ImmutableSet.of());",
" ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());",
"",
" ImmutableCollection<Integer> list1 = ImmutableList.copyOf(ImmutableList.of(1));",
" ImmutableCollection<Integer> list2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));",
"",
" Collection<Integer> c1 = ImmutableSet.copyOf(ImmutableSet.of(1));",
" Collection<Integer> c2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));",
"",
" Flux<Integer> f1 = Flux.just(1).flatMap(e -> RxJava2Adapter.fluxToFlowable(Flux.just(2)));",
" Flux<Integer> f2 = Flux.concat(Flux.just(3));",
" Publisher<Integer> f3 = Flux.firstWithSignal(Flux.just(4));",
" Publisher<Integer> f4 = Flux.from(Flux.just(5));",
" Publisher<Integer> f5 = Flux.merge(Flux.just(6));",
"",
" Mono<Integer> m1 = Mono.from(Mono.just(7));",
" Publisher<Integer> m2 = Mono.fromDirect(Mono.just(8));",
"",
" bar(Flux.concat(Flux.just(9)));",
" bar(Mono.from(Mono.just(10)));",
"",
" Object o1 = ImmutableSet.copyOf(ImmutableList.of());",
" Object o2 = ImmutableSet.copyOf(ImmutableSet.of());",
"",
" Matcher matcher = Matchers.allOf(staticMethod());",
"",
" when(\"foo\".contains(\"f\")).thenAnswer(inv -> ImmutableSet.copyOf(ImmutableList.of(1)));",
" }",
"",
" void bar(Publisher<Integer> publisher) {}",
"}")
"""
import static com.google.errorprone.matchers.Matchers.staticMethod;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import java.util.ArrayList;
import java.util.Collection;
import org.reactivestreams.Publisher;
import reactor.adapter.rxjava.RxJava2Adapter;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public final class A {
public void m() {
ImmutableSet<Object> set1 = ImmutableSet.copyOf(ImmutableSet.of());
ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());
ImmutableCollection<Integer> list1 = ImmutableList.copyOf(ImmutableList.of(1));
ImmutableCollection<Integer> list2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));
Collection<Integer> c1 = ImmutableSet.copyOf(ImmutableSet.of(1));
Collection<Integer> c2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));
Flux<Integer> f1 = Flux.just(1).flatMap(e -> RxJava2Adapter.fluxToFlowable(Flux.just(2)));
Flux<Integer> f2 = Flux.concat(Flux.just(3));
Publisher<Integer> f3 = Flux.firstWithSignal(Flux.just(4));
Publisher<Integer> f4 = Flux.from(Flux.just(5));
Publisher<Integer> f5 = Flux.merge(Flux.just(6));
Mono<Integer> m1 = Mono.from(Mono.just(7));
Publisher<Integer> m2 = Mono.fromDirect(Mono.just(8));
bar(Flux.concat(Flux.just(9)));
bar(Mono.from(Mono.just(10)));
Object o1 = ImmutableSet.copyOf(ImmutableList.of());
Object o2 = ImmutableSet.copyOf(ImmutableSet.of());
Matcher matcher = Matchers.allOf(staticMethod());
when("foo".contains("f")).thenAnswer(inv -> ImmutableSet.copyOf(ImmutableList.of(1)));
}
void bar(Publisher<Integer> publisher) {}
}
""")
.addOutputLines(
"A.java",
"import static com.google.errorprone.matchers.Matchers.staticMethod;",
"import static org.mockito.Mockito.when;",
"",
"import com.google.common.collect.ImmutableCollection;",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.errorprone.matchers.Matcher;",
"import com.google.errorprone.matchers.Matchers;",
"import java.util.ArrayList;",
"import java.util.Collection;",
"import org.reactivestreams.Publisher;",
"import reactor.adapter.rxjava.RxJava2Adapter;",
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.Mono;",
"",
"public final class A {",
" public void m() {",
" ImmutableSet<Object> set1 = ImmutableSet.of();",
" ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());",
"",
" ImmutableCollection<Integer> list1 = ImmutableList.of(1);",
" ImmutableCollection<Integer> list2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));",
"",
" Collection<Integer> c1 = ImmutableSet.of(1);",
" Collection<Integer> c2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));",
"",
" Flux<Integer> f1 = Flux.just(1).flatMap(e -> Flux.just(2));",
" Flux<Integer> f2 = Flux.just(3);",
" Publisher<Integer> f3 = Flux.just(4);",
" Publisher<Integer> f4 = Flux.just(5);",
" Publisher<Integer> f5 = Flux.just(6);",
"",
" Mono<Integer> m1 = Mono.just(7);",
" Publisher<Integer> m2 = Mono.just(8);",
"",
" bar(Flux.just(9));",
" bar(Mono.just(10));",
"",
" Object o1 = ImmutableSet.copyOf(ImmutableList.of());",
" Object o2 = ImmutableSet.of();",
"",
" Matcher matcher = staticMethod();",
"",
" when(\"foo\".contains(\"f\")).thenAnswer(inv -> ImmutableSet.copyOf(ImmutableList.of(1)));",
" }",
"",
" void bar(Publisher<Integer> publisher) {}",
"}")
"""
import static com.google.errorprone.matchers.Matchers.staticMethod;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import java.util.ArrayList;
import java.util.Collection;
import org.reactivestreams.Publisher;
import reactor.adapter.rxjava.RxJava2Adapter;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public final class A {
public void m() {
ImmutableSet<Object> set1 = ImmutableSet.of();
ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());
ImmutableCollection<Integer> list1 = ImmutableList.of(1);
ImmutableCollection<Integer> list2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));
Collection<Integer> c1 = ImmutableSet.of(1);
Collection<Integer> c2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));
Flux<Integer> f1 = Flux.just(1).flatMap(e -> Flux.just(2));
Flux<Integer> f2 = Flux.just(3);
Publisher<Integer> f3 = Flux.just(4);
Publisher<Integer> f4 = Flux.just(5);
Publisher<Integer> f5 = Flux.just(6);
Mono<Integer> m1 = Mono.just(7);
Publisher<Integer> m2 = Mono.just(8);
bar(Flux.just(9));
bar(Mono.just(10));
Object o1 = ImmutableSet.copyOf(ImmutableList.of());
Object o2 = ImmutableSet.of();
Matcher matcher = staticMethod();
when("foo".contains("f")).thenAnswer(inv -> ImmutableSet.copyOf(ImmutableList.of(1)));
}
void bar(Publisher<Integer> publisher) {}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
@@ -289,38 +295,42 @@ final class IdentityConversionTest {
.setFixChooser(FixChoosers.SECOND)
.addInputLines(
"A.java",
"import com.google.common.collect.ImmutableCollection;",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import java.util.ArrayList;",
"",
"public final class A {",
" public void m() {",
" ImmutableSet<Object> set1 = ImmutableSet.copyOf(ImmutableSet.of());",
" ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());",
"",
" ImmutableCollection<Integer> list1 = ImmutableList.copyOf(ImmutableList.of(1));",
" ImmutableCollection<Integer> list2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));",
" }",
"}")
"""
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
public final class A {
public void m() {
ImmutableSet<Object> set1 = ImmutableSet.copyOf(ImmutableSet.of());
ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());
ImmutableCollection<Integer> list1 = ImmutableList.copyOf(ImmutableList.of(1));
ImmutableCollection<Integer> list2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));
}
}
""")
.addOutputLines(
"A.java",
"import com.google.common.collect.ImmutableCollection;",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import java.util.ArrayList;",
"",
"public final class A {",
" public void m() {",
" @SuppressWarnings(\"IdentityConversion\")",
" ImmutableSet<Object> set1 = ImmutableSet.copyOf(ImmutableSet.of());",
" ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());",
"",
" @SuppressWarnings(\"IdentityConversion\")",
" ImmutableCollection<Integer> list1 = ImmutableList.copyOf(ImmutableList.of(1));",
" ImmutableCollection<Integer> list2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));",
" }",
"}")
"""
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
public final class A {
public void m() {
@SuppressWarnings("IdentityConversion")
ImmutableSet<Object> set1 = ImmutableSet.copyOf(ImmutableSet.of());
ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());
@SuppressWarnings("IdentityConversion")
ImmutableCollection<Integer> list1 = ImmutableList.copyOf(ImmutableList.of(1));
ImmutableCollection<Integer> list2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,94 +11,96 @@ final class ImmutablesSortedSetComparatorTest {
CompilationTestHelper.newInstance(ImmutablesSortedSetComparator.class, getClass())
.addSourceLines(
"A.java",
"import com.google.common.collect.ContiguousSet;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.common.collect.ImmutableSortedSet;",
"import java.util.NavigableSet;",
"import java.util.Set;",
"import java.util.SortedSet;",
"import java.util.TreeSet;",
"import org.immutables.value.Value;",
"",
"interface A {",
" @Value.Immutable",
" interface ImmutableInterface {",
" Set<String> set();",
"",
" // BUG: Diagnostic contains:",
" SortedSet<String> sortedSet();",
"",
" @Value.NaturalOrder",
" SortedSet<String> sortedSet2();",
" }",
"",
" @Value.Modifiable",
" interface ModifiableInterfaceWithDefaults {",
" @Value.Default",
" default Set<Integer> set() {",
" return new TreeSet<>();",
" }",
"",
" @Value.Default",
" // BUG: Diagnostic contains:",
" default NavigableSet<Integer> navigableSet() {",
" return new TreeSet<>();",
" }",
"",
" @Value.Default",
" @Value.ReverseOrder",
" default NavigableSet<Integer> navigableSet2() {",
" return new TreeSet<>();",
" }",
"",
" default NavigableSet<Integer> nonPropertyNavigableSet() {",
" return new TreeSet<>();",
" }",
" }",
"",
" interface NonImmutablesInterface {",
" SortedSet<String> sortedSet();",
" }",
"",
" @Value.Immutable",
" abstract class AbstractImmutableWithDefaults {",
" @Value.Default",
" ImmutableSet<Integer> immutableSet() {",
" return ImmutableSet.of();",
" }",
"",
" @Value.Default",
" // BUG: Diagnostic contains:",
" ImmutableSortedSet<String> immutableSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
"",
" @Value.Default",
" @Value.NaturalOrder",
" ImmutableSortedSet<String> immutableSortedSet2() {",
" return ImmutableSortedSet.of();",
" }",
"",
" ImmutableSortedSet<String> nonPropertyImmutableSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
" }",
"",
" @Value.Modifiable",
" abstract class AbstractModifiable {",
" abstract ImmutableSet<Integer> immutableSet();",
"",
" // BUG: Diagnostic contains:",
" abstract ContiguousSet<Integer> contiguousSet();",
"",
" @Value.ReverseOrder",
" abstract ContiguousSet<Integer> contiguousSet2();",
" }",
"",
" abstract class AbstractNonImmutables {",
" abstract SortedSet<Integer> sortedSet();",
" }",
"}")
"""
import com.google.common.collect.ContiguousSet;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import java.util.NavigableSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.immutables.value.Value;
interface A {
@Value.Immutable
interface ImmutableInterface {
Set<String> set();
// BUG: Diagnostic contains:
SortedSet<String> sortedSet();
@Value.NaturalOrder
SortedSet<String> sortedSet2();
}
@Value.Modifiable
interface ModifiableInterfaceWithDefaults {
@Value.Default
default Set<Integer> set() {
return new TreeSet<>();
}
@Value.Default
// BUG: Diagnostic contains:
default NavigableSet<Integer> navigableSet() {
return new TreeSet<>();
}
@Value.Default
@Value.ReverseOrder
default NavigableSet<Integer> navigableSet2() {
return new TreeSet<>();
}
default NavigableSet<Integer> nonPropertyNavigableSet() {
return new TreeSet<>();
}
}
interface NonImmutablesInterface {
SortedSet<String> sortedSet();
}
@Value.Immutable
abstract class AbstractImmutableWithDefaults {
@Value.Default
ImmutableSet<Integer> immutableSet() {
return ImmutableSet.of();
}
@Value.Default
// BUG: Diagnostic contains:
ImmutableSortedSet<String> immutableSortedSet() {
return ImmutableSortedSet.of();
}
@Value.Default
@Value.NaturalOrder
ImmutableSortedSet<String> immutableSortedSet2() {
return ImmutableSortedSet.of();
}
ImmutableSortedSet<String> nonPropertyImmutableSortedSet() {
return ImmutableSortedSet.of();
}
}
@Value.Modifiable
abstract class AbstractModifiable {
abstract ImmutableSet<Integer> immutableSet();
// BUG: Diagnostic contains:
abstract ContiguousSet<Integer> contiguousSet();
@Value.ReverseOrder
abstract ContiguousSet<Integer> contiguousSet2();
}
abstract class AbstractNonImmutables {
abstract SortedSet<Integer> sortedSet();
}
}
""")
.doTest();
}
@@ -107,36 +109,40 @@ final class ImmutablesSortedSetComparatorTest {
BugCheckerRefactoringTestHelper.newInstance(ImmutablesSortedSetComparator.class, getClass())
.addInputLines(
"A.java",
"import com.google.common.collect.ImmutableSortedSet;",
"import java.util.SortedSet;",
"import org.immutables.value.Value;",
"",
"@Value.Immutable",
"abstract class A {",
" abstract ImmutableSortedSet<String> sortedSet();",
"",
" @Value.Modifiable",
" interface B {",
" SortedSet<String> sortedSet();",
" }",
"}")
"""
import com.google.common.collect.ImmutableSortedSet;
import java.util.SortedSet;
import org.immutables.value.Value;
@Value.Immutable
abstract class A {
abstract ImmutableSortedSet<String> sortedSet();
@Value.Modifiable
interface B {
SortedSet<String> sortedSet();
}
}
""")
.addOutputLines(
"A.java",
"import com.google.common.collect.ImmutableSortedSet;",
"import java.util.SortedSet;",
"import org.immutables.value.Value;",
"",
"@Value.Immutable",
"abstract class A {",
" @Value.NaturalOrder",
" abstract ImmutableSortedSet<String> sortedSet();",
"",
" @Value.Modifiable",
" interface B {",
" @Value.NaturalOrder",
" SortedSet<String> sortedSet();",
" }",
"}")
"""
import com.google.common.collect.ImmutableSortedSet;
import java.util.SortedSet;
import org.immutables.value.Value;
@Value.Immutable
abstract class A {
@Value.NaturalOrder
abstract ImmutableSortedSet<String> sortedSet();
@Value.Modifiable
interface B {
@Value.NaturalOrder
SortedSet<String> sortedSet();
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
@@ -145,33 +151,37 @@ final class ImmutablesSortedSetComparatorTest {
BugCheckerRefactoringTestHelper.newInstance(ImmutablesSortedSetComparator.class, getClass())
.addInputLines(
"MySpringService.java",
"import com.google.common.collect.ImmutableSortedSet;",
"import org.springframework.beans.factory.annotation.Value;",
"",
"class MySpringService {",
" MySpringService(@Value(\"${someProperty}\") String prop) {}",
" ;",
"",
" @org.immutables.value.Value.Immutable",
" interface A {",
" ImmutableSortedSet<String> sortedSet();",
" }",
"}")
"""
import com.google.common.collect.ImmutableSortedSet;
import org.springframework.beans.factory.annotation.Value;
class MySpringService {
MySpringService(@Value("${someProperty}") String prop) {}
;
@org.immutables.value.Value.Immutable
interface A {
ImmutableSortedSet<String> sortedSet();
}
}
""")
.addOutputLines(
"MySpringService.java",
"import com.google.common.collect.ImmutableSortedSet;",
"import org.springframework.beans.factory.annotation.Value;",
"",
"class MySpringService {",
" MySpringService(@Value(\"${someProperty}\") String prop) {}",
" ;",
"",
" @org.immutables.value.Value.Immutable",
" interface A {",
" @org.immutables.value.Value.NaturalOrder",
" ImmutableSortedSet<String> sortedSet();",
" }",
"}")
"""
import com.google.common.collect.ImmutableSortedSet;
import org.springframework.beans.factory.annotation.Value;
class MySpringService {
MySpringService(@Value("${someProperty}") String prop) {}
;
@org.immutables.value.Value.Immutable
interface A {
@org.immutables.value.Value.NaturalOrder
ImmutableSortedSet<String> sortedSet();
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,31 +11,33 @@ final class IsInstanceLambdaUsageTest {
CompilationTestHelper.newInstance(IsInstanceLambdaUsage.class, getClass())
.addSourceLines(
"A.java",
"import java.util.stream.Stream;",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" Integer localVariable = 0;",
"",
" Stream.of(0).map(i -> i + 1);",
" Stream.of(1).filter(Integer.class::isInstance);",
" Stream.of(2).filter(i -> i.getClass() instanceof Class);",
" Stream.of(3).filter(i -> localVariable instanceof Integer);",
" // XXX: Ideally this case is also flagged. Pick this up in the context of merging the",
" // `IsInstanceLambdaUsage` and `MethodReferenceUsage` checks, or introduce a separate check that",
" // simplifies unnecessary block lambda expressions.",
" Stream.of(4)",
" .filter(",
" i -> {",
" return localVariable instanceof Integer;",
" });",
" Flux.just(5, \"foo\").distinctUntilChanged(v -> v, (a, b) -> a instanceof Integer);",
"",
" // BUG: Diagnostic contains:",
" Stream.of(6).filter(i -> i instanceof Integer);",
" }",
"}")
"""
import java.util.stream.Stream;
import reactor.core.publisher.Flux;
class A {
void m() {
Integer localVariable = 0;
Stream.of(0).map(i -> i + 1);
Stream.of(1).filter(Integer.class::isInstance);
Stream.of(2).filter(i -> i.getClass() instanceof Class);
Stream.of(3).filter(i -> localVariable instanceof Integer);
// XXX: Ideally this case is also flagged. Pick this up in the context of merging the
// `IsInstanceLambdaUsage` and `MethodReferenceUsage` checks, or introduce a separate check that
// simplifies unnecessary block lambda expressions.
Stream.of(4)
.filter(
i -> {
return localVariable instanceof Integer;
});
Flux.just(5, "foo").distinctUntilChanged(v -> v, (a, b) -> a instanceof Integer);
// BUG: Diagnostic contains:
Stream.of(6).filter(i -> i instanceof Integer);
}
}
""")
.doTest();
}
@@ -44,22 +46,26 @@ final class IsInstanceLambdaUsageTest {
BugCheckerRefactoringTestHelper.newInstance(IsInstanceLambdaUsage.class, getClass())
.addInputLines(
"A.java",
"import java.util.stream.Stream;",
"",
"class A {",
" void m() {",
" Stream.of(1).filter(i -> i instanceof Integer);",
" }",
"}")
"""
import java.util.stream.Stream;
class A {
void m() {
Stream.of(1).filter(i -> i instanceof Integer);
}
}
""")
.addOutputLines(
"A.java",
"import java.util.stream.Stream;",
"",
"class A {",
" void m() {",
" Stream.of(1).filter(Integer.class::isInstance);",
" }",
"}")
"""
import java.util.stream.Stream;
class A {
void m() {
Stream.of(1).filter(Integer.class::isInstance);
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,83 +11,85 @@ final class JUnitClassModifiersTest {
CompilationTestHelper.newInstance(JUnitClassModifiers.class, getClass())
.addSourceLines(
"Container.java",
"import org.junit.jupiter.api.Test;",
"import org.junit.jupiter.params.ParameterizedTest;",
"import org.springframework.boot.test.context.TestConfiguration;",
"import org.springframework.context.annotation.Configuration;",
"",
"class Container {",
" final class FinalAndPackagePrivate {",
" @Test",
" void foo() {}",
" }",
"",
" final class FinalAndPackagePrivateWithCustomTestMethod {",
" @ParameterizedTest",
" void foo() {}",
" }",
"",
" public abstract class Abstract {",
" @Test",
" void foo() {}",
" }",
"",
" @Configuration",
" class WithConfigurationAnnotation {",
" @Test",
" void foo() {}",
" }",
"",
" @TestConfiguration",
" class WithConfigurationMetaAnnotation {",
" @Test",
" void foo() {}",
" }",
"",
" // BUG: Diagnostic contains:",
" private final class Private {",
" @Test",
" void foo() {}",
" }",
"",
" // BUG: Diagnostic contains:",
" protected final class Protected {",
" @Test",
" void foo() {}",
" }",
"",
" // BUG: Diagnostic contains:",
" public final class Public {",
" @Test",
" void foo() {}",
" }",
"",
" // BUG: Diagnostic contains:",
" class NonFinal {",
" @Test",
" void foo() {}",
" }",
"",
" // BUG: Diagnostic contains:",
" class NonFinalWithCustomTestMethod {",
" @ParameterizedTest",
" void foo() {}",
" }",
"",
" @Configuration",
" // BUG: Diagnostic contains:",
" public class PublicWithConfigurationAnnotation {",
" @Test",
" void foo() {}",
" }",
"",
" @TestConfiguration",
" // BUG: Diagnostic contains:",
" protected class ProtectedWithConfigurationMetaAnnotation {",
" @Test",
" void foo() {}",
" }",
"}")
"""
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Configuration;
class Container {
final class FinalAndPackagePrivate {
@Test
void foo() {}
}
final class FinalAndPackagePrivateWithCustomTestMethod {
@ParameterizedTest
void foo() {}
}
public abstract class Abstract {
@Test
void foo() {}
}
@Configuration
class WithConfigurationAnnotation {
@Test
void foo() {}
}
@TestConfiguration
class WithConfigurationMetaAnnotation {
@Test
void foo() {}
}
// BUG: Diagnostic contains:
private final class Private {
@Test
void foo() {}
}
// BUG: Diagnostic contains:
protected final class Protected {
@Test
void foo() {}
}
// BUG: Diagnostic contains:
public final class Public {
@Test
void foo() {}
}
// BUG: Diagnostic contains:
class NonFinal {
@Test
void foo() {}
}
// BUG: Diagnostic contains:
class NonFinalWithCustomTestMethod {
@ParameterizedTest
void foo() {}
}
@Configuration
// BUG: Diagnostic contains:
public class PublicWithConfigurationAnnotation {
@Test
void foo() {}
}
@TestConfiguration
// BUG: Diagnostic contains:
protected class ProtectedWithConfigurationMetaAnnotation {
@Test
void foo() {}
}
}
""")
.doTest();
}
@@ -96,34 +98,38 @@ final class JUnitClassModifiersTest {
BugCheckerRefactoringTestHelper.newInstance(JUnitClassModifiers.class, getClass())
.addInputLines(
"A.java",
"import org.junit.jupiter.api.Test;",
"import org.springframework.context.annotation.Configuration;",
"",
"public class A {",
" @Test",
" void foo() {}",
"",
" @Configuration",
" private static class B {",
" @Test",
" void bar() {}",
" }",
"}")
"""
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Configuration;
public class A {
@Test
void foo() {}
@Configuration
private static class B {
@Test
void bar() {}
}
}
""")
.addOutputLines(
"A.java",
"import org.junit.jupiter.api.Test;",
"import org.springframework.context.annotation.Configuration;",
"",
"final class A {",
" @Test",
" void foo() {}",
"",
" @Configuration",
" static class B {",
" @Test",
" void bar() {}",
" }",
"}")
"""
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Configuration;
final class A {
@Test
void foo() {}
@Configuration
static class B {
@Test
void bar() {}
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,330 +11,336 @@ final class JUnitMethodDeclarationTest {
CompilationTestHelper.newInstance(JUnitMethodDeclaration.class, getClass())
.addSourceLines(
"A.java",
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
"",
"import org.junit.jupiter.api.AfterAll;",
"import org.junit.jupiter.api.AfterEach;",
"import org.junit.jupiter.api.BeforeAll;",
"import org.junit.jupiter.api.BeforeEach;",
"import org.junit.jupiter.api.Test;",
"import org.junit.jupiter.params.ParameterizedTest;",
"",
"class A {",
" {",
" arguments();",
" }",
"",
" @BeforeAll",
" void setUp1() {}",
"",
" @BeforeAll",
" // BUG: Diagnostic contains:",
" public void setUp2() {}",
"",
" @BeforeAll",
" // BUG: Diagnostic contains:",
" protected void setUp3() {}",
"",
" @BeforeAll",
" // BUG: Diagnostic contains:",
" private void setUp4() {}",
"",
" @BeforeEach",
" void setup5() {}",
"",
" @BeforeEach",
" // BUG: Diagnostic contains:",
" public void setUp6() {}",
"",
" @BeforeEach",
" // BUG: Diagnostic contains:",
" protected void setUp7() {}",
"",
" @BeforeEach",
" // BUG: Diagnostic contains:",
" private void setUp8() {}",
"",
" @AfterEach",
" void tearDown1() {}",
"",
" @AfterEach",
" // BUG: Diagnostic contains:",
" public void tearDown2() {}",
"",
" @AfterEach",
" // BUG: Diagnostic contains:",
" protected void tearDown3() {}",
"",
" @AfterEach",
" // BUG: Diagnostic contains:",
" private void tearDown4() {}",
"",
" @AfterAll",
" void tearDown5() {}",
"",
" @AfterAll",
" // BUG: Diagnostic contains:",
" public void tearDown6() {}",
"",
" @AfterAll",
" // BUG: Diagnostic contains:",
" protected void tearDown7() {}",
"",
" @AfterAll",
" // BUG: Diagnostic contains:",
" private void tearDown8() {}",
"",
" @Test",
" void test() {}",
"",
" @Test",
" void method1() {}",
"",
" @Test",
" // BUG: Diagnostic contains:",
" void testMethod2() {}",
"",
" @Test",
" // BUG: Diagnostic contains:",
" public void method3() {}",
"",
" @Test",
" // BUG: Diagnostic contains:",
" protected void method4() {}",
"",
" @Test",
" // BUG: Diagnostic contains:",
" private void method5() {}",
"",
" @ParameterizedTest",
" void method6() {}",
"",
" @ParameterizedTest",
" // BUG: Diagnostic contains:",
" void testMethod7() {}",
"",
" @ParameterizedTest",
" // BUG: Diagnostic contains:",
" public void method8() {}",
"",
" @ParameterizedTest",
" // BUG: Diagnostic contains:",
" protected void method9() {}",
"",
" @ParameterizedTest",
" // BUG: Diagnostic contains:",
" private void method10() {}",
"",
" @BeforeEach",
" @BeforeAll",
" @AfterEach",
" @AfterAll",
" void testNonTestMethod1() {}",
"",
" public void testNonTestMethod2() {}",
"",
" protected void testNonTestMethod3() {}",
"",
" private void testNonTestMethod4() {}",
"",
" @Test",
" void test5() {}",
"",
" @Test",
" // BUG: Diagnostic contains: (but note that a method named `toString` is already defined in this",
" // class or a supertype)",
" void testToString() {}",
"",
" @Test",
" // BUG: Diagnostic contains: (but note that a method named `overload` is already defined in this",
" // class or a supertype)",
" void testOverload() {}",
"",
" void overload() {}",
"",
" @Test",
" // BUG: Diagnostic contains: (but note that `arguments` is already statically imported)",
" void testArguments() {}",
"",
" @Test",
" // BUG: Diagnostic contains: (but note that `public` is not a valid identifier)",
" void testPublic() {}",
"",
" @Test",
" // BUG: Diagnostic contains: (but note that `null` is not a valid identifier)",
" void testNull() {}",
"",
" @Test",
" // BUG: Diagnostic contains:",
" void testRecord() {}",
"",
" @Test",
" // BUG: Diagnostic contains:",
" void testMethodThatIsOverriddenWithoutOverrideAnnotation() {}",
"}")
"""
import static org.junit.jupiter.params.provider.Arguments.arguments;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
class A {
{
arguments();
}
@BeforeAll
void setUp1() {}
@BeforeAll
// BUG: Diagnostic contains:
public void setUp2() {}
@BeforeAll
// BUG: Diagnostic contains:
protected void setUp3() {}
@BeforeAll
// BUG: Diagnostic contains:
private void setUp4() {}
@BeforeEach
void setup5() {}
@BeforeEach
// BUG: Diagnostic contains:
public void setUp6() {}
@BeforeEach
// BUG: Diagnostic contains:
protected void setUp7() {}
@BeforeEach
// BUG: Diagnostic contains:
private void setUp8() {}
@AfterEach
void tearDown1() {}
@AfterEach
// BUG: Diagnostic contains:
public void tearDown2() {}
@AfterEach
// BUG: Diagnostic contains:
protected void tearDown3() {}
@AfterEach
// BUG: Diagnostic contains:
private void tearDown4() {}
@AfterAll
void tearDown5() {}
@AfterAll
// BUG: Diagnostic contains:
public void tearDown6() {}
@AfterAll
// BUG: Diagnostic contains:
protected void tearDown7() {}
@AfterAll
// BUG: Diagnostic contains:
private void tearDown8() {}
@Test
void test() {}
@Test
void method1() {}
@Test
// BUG: Diagnostic contains:
void testMethod2() {}
@Test
// BUG: Diagnostic contains:
public void method3() {}
@Test
// BUG: Diagnostic contains:
protected void method4() {}
@Test
// BUG: Diagnostic contains:
private void method5() {}
@ParameterizedTest
void method6() {}
@ParameterizedTest
// BUG: Diagnostic contains:
void testMethod7() {}
@ParameterizedTest
// BUG: Diagnostic contains:
public void method8() {}
@ParameterizedTest
// BUG: Diagnostic contains:
protected void method9() {}
@ParameterizedTest
// BUG: Diagnostic contains:
private void method10() {}
@BeforeEach
@BeforeAll
@AfterEach
@AfterAll
void testNonTestMethod1() {}
public void testNonTestMethod2() {}
protected void testNonTestMethod3() {}
private void testNonTestMethod4() {}
@Test
void test5() {}
@Test
// BUG: Diagnostic contains: (but note that a method named `toString` is already defined in this
// class or a supertype)
void testToString() {}
@Test
// BUG: Diagnostic contains: (but note that a method named `overload` is already defined in this
// class or a supertype)
void testOverload() {}
void overload() {}
@Test
// BUG: Diagnostic contains: (but note that `arguments` is already statically imported)
void testArguments() {}
@Test
// BUG: Diagnostic contains: (but note that `public` is not a valid identifier)
void testPublic() {}
@Test
// BUG: Diagnostic contains: (but note that `null` is not a valid identifier)
void testNull() {}
@Test
// BUG: Diagnostic contains:
void testRecord() {}
@Test
// BUG: Diagnostic contains:
void testMethodThatIsOverriddenWithoutOverrideAnnotation() {}
}
""")
.addSourceLines(
"B.java",
"import org.junit.jupiter.api.AfterAll;",
"import org.junit.jupiter.api.AfterEach;",
"import org.junit.jupiter.api.BeforeAll;",
"import org.junit.jupiter.api.BeforeEach;",
"import org.junit.jupiter.api.Test;",
"import org.junit.jupiter.params.ParameterizedTest;",
"",
"class B extends A {",
" @Override",
" @BeforeAll",
" void setUp1() {}",
"",
" @Override",
" @BeforeAll",
" public void setUp2() {}",
"",
" @Override",
" @BeforeAll",
" protected void setUp3() {}",
"",
" @Override",
" @BeforeEach",
" void setup5() {}",
"",
" @Override",
" @BeforeEach",
" public void setUp6() {}",
"",
" @Override",
" @BeforeEach",
" protected void setUp7() {}",
"",
" @Override",
" @AfterEach",
" void tearDown1() {}",
"",
" @Override",
" @AfterEach",
" public void tearDown2() {}",
"",
" @Override",
" @AfterEach",
" protected void tearDown3() {}",
"",
" @Override",
" @AfterAll",
" void tearDown5() {}",
"",
" @Override",
" @AfterAll",
" public void tearDown6() {}",
"",
" @Override",
" @AfterAll",
" protected void tearDown7() {}",
"",
" @Override",
" @Test",
" void test() {}",
"",
" @Override",
" @Test",
" void method1() {}",
"",
" @Override",
" @Test",
" void testMethod2() {}",
"",
" @Override",
" @Test",
" public void method3() {}",
"",
" @Override",
" @Test",
" protected void method4() {}",
"",
" @Override",
" @ParameterizedTest",
" void method6() {}",
"",
" @Override",
" @ParameterizedTest",
" void testMethod7() {}",
"",
" @Override",
" @ParameterizedTest",
" public void method8() {}",
"",
" @Override",
" @ParameterizedTest",
" protected void method9() {}",
"",
" @Override",
" @BeforeEach",
" @BeforeAll",
" @AfterEach",
" @AfterAll",
" void testNonTestMethod1() {}",
"",
" @Override",
" public void testNonTestMethod2() {}",
"",
" @Override",
" protected void testNonTestMethod3() {}",
"",
" @Override",
" @Test",
" void test5() {}",
"",
" @Override",
" @Test",
" void testToString() {}",
"",
" @Override",
" @Test",
" void testOverload() {}",
"",
" @Override",
" void overload() {}",
"",
" @Override",
" @Test",
" void testArguments() {}",
"",
" @Override",
" @Test",
" void testPublic() {}",
"",
" @Override",
" @Test",
" void testNull() {}",
"",
" @Override",
" @Test",
" void testRecord() {}",
"",
" @Test",
" void testMethodThatIsOverriddenWithoutOverrideAnnotation() {}",
"}")
"""
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
class B extends A {
@Override
@BeforeAll
void setUp1() {}
@Override
@BeforeAll
public void setUp2() {}
@Override
@BeforeAll
protected void setUp3() {}
@Override
@BeforeEach
void setup5() {}
@Override
@BeforeEach
public void setUp6() {}
@Override
@BeforeEach
protected void setUp7() {}
@Override
@AfterEach
void tearDown1() {}
@Override
@AfterEach
public void tearDown2() {}
@Override
@AfterEach
protected void tearDown3() {}
@Override
@AfterAll
void tearDown5() {}
@Override
@AfterAll
public void tearDown6() {}
@Override
@AfterAll
protected void tearDown7() {}
@Override
@Test
void test() {}
@Override
@Test
void method1() {}
@Override
@Test
void testMethod2() {}
@Override
@Test
public void method3() {}
@Override
@Test
protected void method4() {}
@Override
@ParameterizedTest
void method6() {}
@Override
@ParameterizedTest
void testMethod7() {}
@Override
@ParameterizedTest
public void method8() {}
@Override
@ParameterizedTest
protected void method9() {}
@Override
@BeforeEach
@BeforeAll
@AfterEach
@AfterAll
void testNonTestMethod1() {}
@Override
public void testNonTestMethod2() {}
@Override
protected void testNonTestMethod3() {}
@Override
@Test
void test5() {}
@Override
@Test
void testToString() {}
@Override
@Test
void testOverload() {}
@Override
void overload() {}
@Override
@Test
void testArguments() {}
@Override
@Test
void testPublic() {}
@Override
@Test
void testNull() {}
@Override
@Test
void testRecord() {}
@Test
void testMethodThatIsOverriddenWithoutOverrideAnnotation() {}
}
""")
.addSourceLines(
"C.java",
"import org.junit.jupiter.api.AfterAll;",
"import org.junit.jupiter.api.BeforeAll;",
"import org.junit.jupiter.api.Test;",
"",
"abstract class C {",
" @BeforeAll",
" public void setUp() {}",
"",
" @Test",
" void testMethod1() {}",
"",
" @AfterAll",
" // BUG: Diagnostic contains:",
" private void tearDown() {}",
"",
" @Test",
" // BUG: Diagnostic contains:",
" final void testMethod2() {}",
"}")
"""
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
abstract class C {
@BeforeAll
public void setUp() {}
@Test
void testMethod1() {}
@AfterAll
// BUG: Diagnostic contains:
private void tearDown() {}
@Test
// BUG: Diagnostic contains:
final void testMethod2() {}
}
""")
.doTest();
}
@@ -343,126 +349,130 @@ final class JUnitMethodDeclarationTest {
BugCheckerRefactoringTestHelper.newInstance(JUnitMethodDeclaration.class, getClass())
.addInputLines(
"A.java",
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
"",
"import org.junit.jupiter.api.AfterAll;",
"import org.junit.jupiter.api.AfterEach;",
"import org.junit.jupiter.api.BeforeAll;",
"import org.junit.jupiter.api.BeforeEach;",
"import org.junit.jupiter.api.RepeatedTest;",
"import org.junit.jupiter.api.Test;",
"import org.junit.jupiter.params.ParameterizedTest;",
"",
"class A {",
" {",
" arguments();",
" }",
"",
" @BeforeAll",
" public void setUp1() {}",
"",
" @BeforeEach",
" protected void setUp2() {}",
"",
" @AfterEach",
" private void setUp3() {}",
"",
" @AfterAll",
" private void setUp4() {}",
"",
" @Test",
" void testFoo() {}",
"",
" @ParameterizedTest",
" void testBar() {}",
"",
" @Test",
" public void baz() {}",
"",
" @RepeatedTest(2)",
" private void qux() {}",
"",
" @ParameterizedTest",
" protected void quux() {}",
"",
" @Test",
" public void testToString() {}",
"",
" @Test",
" public void testOverload() {}",
"",
" void overload() {}",
"",
" @Test",
" protected void testArguments() {}",
"",
" @Test",
" private void testClass() {}",
"",
" @Test",
" private void testTrue() {}",
"}")
"""
import static org.junit.jupiter.params.provider.Arguments.arguments;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
class A {
{
arguments();
}
@BeforeAll
public void setUp1() {}
@BeforeEach
protected void setUp2() {}
@AfterEach
private void setUp3() {}
@AfterAll
private void setUp4() {}
@Test
void testFoo() {}
@ParameterizedTest
void testBar() {}
@Test
public void baz() {}
@RepeatedTest(2)
private void qux() {}
@ParameterizedTest
protected void quux() {}
@Test
public void testToString() {}
@Test
public void testOverload() {}
void overload() {}
@Test
protected void testArguments() {}
@Test
private void testClass() {}
@Test
private void testTrue() {}
}
""")
.addOutputLines(
"A.java",
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
"",
"import org.junit.jupiter.api.AfterAll;",
"import org.junit.jupiter.api.AfterEach;",
"import org.junit.jupiter.api.BeforeAll;",
"import org.junit.jupiter.api.BeforeEach;",
"import org.junit.jupiter.api.RepeatedTest;",
"import org.junit.jupiter.api.Test;",
"import org.junit.jupiter.params.ParameterizedTest;",
"",
"class A {",
" {",
" arguments();",
" }",
"",
" @BeforeAll",
" void setUp1() {}",
"",
" @BeforeEach",
" void setUp2() {}",
"",
" @AfterEach",
" void setUp3() {}",
"",
" @AfterAll",
" void setUp4() {}",
"",
" @Test",
" void foo() {}",
"",
" @ParameterizedTest",
" void bar() {}",
"",
" @Test",
" void baz() {}",
"",
" @RepeatedTest(2)",
" void qux() {}",
"",
" @ParameterizedTest",
" void quux() {}",
"",
" @Test",
" void testToString() {}",
"",
" @Test",
" void testOverload() {}",
"",
" void overload() {}",
"",
" @Test",
" void testArguments() {}",
"",
" @Test",
" void testClass() {}",
"",
" @Test",
" void testTrue() {}",
"}")
"""
import static org.junit.jupiter.params.provider.Arguments.arguments;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
class A {
{
arguments();
}
@BeforeAll
void setUp1() {}
@BeforeEach
void setUp2() {}
@AfterEach
void setUp3() {}
@AfterAll
void setUp4() {}
@Test
void foo() {}
@ParameterizedTest
void bar() {}
@Test
void baz() {}
@RepeatedTest(2)
void qux() {}
@ParameterizedTest
void quux() {}
@Test
void testToString() {}
@Test
void testOverload() {}
void overload() {}
@Test
void testArguments() {}
@Test
void testClass() {}
@Test
void testTrue() {}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,25 +11,27 @@ final class JUnitNullaryParameterizedTestDeclarationTest {
CompilationTestHelper.newInstance(JUnitNullaryParameterizedTestDeclaration.class, getClass())
.addSourceLines(
"A.java",
"import org.junit.jupiter.api.Test;",
"import org.junit.jupiter.params.ParameterizedTest;",
"import org.junit.jupiter.params.provider.ValueSource;",
"",
"class A {",
" void nonTest() {}",
"",
" @Test",
" void nonParameterizedTest() {}",
"",
" @ParameterizedTest",
" @ValueSource(ints = {0, 1})",
" void goodParameterizedTest(int someInt) {}",
"",
" @ParameterizedTest",
" @ValueSource(ints = {0, 1})",
" // BUG: Diagnostic contains:",
" void nullaryParameterizedTest() {}",
"}")
"""
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class A {
void nonTest() {}
@Test
void nonParameterizedTest() {}
@ParameterizedTest
@ValueSource(ints = {0, 1})
void goodParameterizedTest(int someInt) {}
@ParameterizedTest
@ValueSource(ints = {0, 1})
// BUG: Diagnostic contains:
void nullaryParameterizedTest() {}
}
""")
.doTest();
}
@@ -39,117 +41,125 @@ final class JUnitNullaryParameterizedTestDeclarationTest {
JUnitNullaryParameterizedTestDeclaration.class, getClass())
.addInputLines(
"A.java",
"import org.junit.jupiter.params.ParameterizedTest;",
"import org.junit.jupiter.params.provider.ArgumentsProvider;",
"import org.junit.jupiter.params.provider.ArgumentsSource;",
"import org.junit.jupiter.params.provider.ArgumentsSources;",
"import org.junit.jupiter.params.provider.MethodSource;",
"import org.junit.jupiter.params.provider.ValueSource;",
"",
"class A {",
" @ParameterizedTest",
" void withoutArgumentSource() {}",
"",
" @ParameterizedTest",
" @ArgumentsSource(ArgumentsProvider.class)",
" void withCustomArgumentSource() {}",
"",
" @ParameterizedTest",
" @ArgumentsSources({",
" @ArgumentsSource(ArgumentsProvider.class),",
" @ArgumentsSource(ArgumentsProvider.class)",
" })",
" void withCustomerArgumentSources() {}",
"",
" /** Foo. */",
" @ParameterizedTest",
" @ValueSource(ints = {0, 1})",
" void withValueSourceAndJavadoc() {}",
"",
" @ParameterizedTest",
" @MethodSource(\"nonexistentMethod\")",
" @SuppressWarnings(\"foo\")",
" void withMethodSourceAndUnrelatedAnnotation() {}",
"",
" @org.junit.jupiter.params.ParameterizedTest",
" @ArgumentsSource(ArgumentsProvider.class)",
" @ValueSource(ints = {0, 1})",
" @MethodSource(\"nonexistentMethod\")",
" void withMultipleArgumentSourcesAndFullyQualifiedImport() {}",
"",
" class NestedWithTestAnnotationFirst {",
" @ParameterizedTest",
" @ValueSource(ints = {0, 1})",
" void withValueSource() {}",
" }",
"",
" class NestedWithTestAnnotationSecond {",
" @ValueSource(ints = {0, 1})",
" @ParameterizedTest",
" void withValueSource() {}",
" }",
"}")
"""
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.junit.jupiter.params.provider.ArgumentsSources;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
class A {
@ParameterizedTest
void withoutArgumentSource() {}
@ParameterizedTest
@ArgumentsSource(ArgumentsProvider.class)
void withCustomArgumentSource() {}
@ParameterizedTest
@ArgumentsSources({
@ArgumentsSource(ArgumentsProvider.class),
@ArgumentsSource(ArgumentsProvider.class)
})
void withCustomerArgumentSources() {}
/** Foo. */
@ParameterizedTest
@ValueSource(ints = {0, 1})
void withValueSourceAndJavadoc() {}
@ParameterizedTest
@MethodSource("nonexistentMethod")
@SuppressWarnings("foo")
void withMethodSourceAndUnrelatedAnnotation() {}
@org.junit.jupiter.params.ParameterizedTest
@ArgumentsSource(ArgumentsProvider.class)
@ValueSource(ints = {0, 1})
@MethodSource("nonexistentMethod")
void withMultipleArgumentSourcesAndFullyQualifiedImport() {}
class NestedWithTestAnnotationFirst {
@ParameterizedTest
@ValueSource(ints = {0, 1})
void withValueSource() {}
}
class NestedWithTestAnnotationSecond {
@ValueSource(ints = {0, 1})
@ParameterizedTest
void withValueSource() {}
}
}
""")
.addOutputLines(
"A.java",
"import org.junit.jupiter.api.Test;",
"import org.junit.jupiter.params.ParameterizedTest;",
"import org.junit.jupiter.params.provider.ArgumentsProvider;",
"import org.junit.jupiter.params.provider.ArgumentsSource;",
"import org.junit.jupiter.params.provider.ArgumentsSources;",
"import org.junit.jupiter.params.provider.MethodSource;",
"import org.junit.jupiter.params.provider.ValueSource;",
"",
"class A {",
" @Test",
" void withoutArgumentSource() {}",
"",
" @Test",
" void withCustomArgumentSource() {}",
"",
" @Test",
" void withCustomerArgumentSources() {}",
"",
" /** Foo. */",
" @Test",
" void withValueSourceAndJavadoc() {}",
"",
" @Test",
" @SuppressWarnings(\"foo\")",
" void withMethodSourceAndUnrelatedAnnotation() {}",
"",
" @Test",
" void withMultipleArgumentSourcesAndFullyQualifiedImport() {}",
"",
" class NestedWithTestAnnotationFirst {",
" @Test",
" void withValueSource() {}",
" }",
"",
" class NestedWithTestAnnotationSecond {",
" @Test",
" void withValueSource() {}",
" }",
"}")
"""
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.junit.jupiter.params.provider.ArgumentsSources;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
class A {
@Test
void withoutArgumentSource() {}
@Test
void withCustomArgumentSource() {}
@Test
void withCustomerArgumentSources() {}
/** Foo. */
@Test
void withValueSourceAndJavadoc() {}
@Test
@SuppressWarnings("foo")
void withMethodSourceAndUnrelatedAnnotation() {}
@Test
void withMultipleArgumentSourcesAndFullyQualifiedImport() {}
class NestedWithTestAnnotationFirst {
@Test
void withValueSource() {}
}
class NestedWithTestAnnotationSecond {
@Test
void withValueSource() {}
}
}
""")
.addInputLines(
"B.java",
"import org.junit.jupiter.params.ParameterizedTest;",
"",
"class B {",
" @ParameterizedTest",
" void scopeInWhichIdentifierTestIsAlreadyDeclared() {}",
"",
" class Test {}",
"}")
"""
import org.junit.jupiter.params.ParameterizedTest;
class B {
@ParameterizedTest
void scopeInWhichIdentifierTestIsAlreadyDeclared() {}
class Test {}
}
""")
.addOutputLines(
"B.java",
"import org.junit.jupiter.params.ParameterizedTest;",
"",
"class B {",
" @org.junit.jupiter.api.Test",
" void scopeInWhichIdentifierTestIsAlreadyDeclared() {}",
"",
" class Test {}",
"}")
"""
import org.junit.jupiter.params.ParameterizedTest;
class B {
@org.junit.jupiter.api.Test
void scopeInWhichIdentifierTestIsAlreadyDeclared() {}
class Test {}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,189 +11,191 @@ final class JUnitValueSourceTest {
CompilationTestHelper.newInstance(JUnitValueSource.class, getClass())
.addSourceLines(
"A.java",
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
"",
"import java.util.Optional;",
"import java.util.stream.Stream;",
"import org.junit.jupiter.params.ParameterizedTest;",
"import org.junit.jupiter.params.provider.Arguments;",
"import org.junit.jupiter.params.provider.MethodSource;",
"",
"class A {",
" private static Stream<Arguments> identificationTestCases() {",
" return Stream.of(arguments(1), Arguments.of(2));",
" }",
"",
" @ParameterizedTest",
" // BUG: Diagnostic contains:",
" @MethodSource(\"identificationTestCases\")",
" void identification(int foo) {}",
"",
" private static int[] identificationWithParensTestCases() {",
" return new int[] {1, 2};",
" }",
"",
" @ParameterizedTest",
" // BUG: Diagnostic contains:",
" @MethodSource(\"identificationWithParensTestCases()\")",
" void identificationWithParens(int foo) {}",
"",
" @ParameterizedTest",
" @MethodSource(\"valueFactoryMissingTestCases\")",
" void valueFactoryMissing(int foo) {}",
"",
" private static Stream<Arguments> multipleUsagesTestCases() {",
" return Stream.of(arguments(1), Arguments.of(2));",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"multipleUsagesTestCases\")",
" void multipleUsages1(int foo) {}",
"",
" @ParameterizedTest",
" @MethodSource(\"multipleUsagesTestCases()\")",
" void multipleUsages2(int bar) {}",
"",
" private static Stream<Arguments> valueFactoryRepeatedTestCases() {",
" return Stream.of(arguments(1), arguments(2));",
" }",
"",
" @ParameterizedTest",
" @MethodSource({\"valueFactoryRepeatedTestCases\", \"valueFactoryRepeatedTestCases\"})",
" void valueFactoryRepeated(int foo) {}",
"",
" private static Stream<Arguments> multipleParametersTestCases() {",
" return Stream.of(arguments(1, 2), arguments(3, 4));",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"multipleParametersTestCases\")",
" void multipleParameters(int first, int second) {}",
"",
" private static int[] arrayWithoutInitializersTestCases() {",
" return new int[1];",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"arrayWithoutInitializersTestCases\")",
" void arrayWithoutInitializers(int foo) {}",
"",
" private static Stream<Arguments> runtimeValueTestCases() {",
" int second = 2;",
" return Stream.of(arguments(1), arguments(second));",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"runtimeValueTestCases\")",
" void runtimeValue(int foo) {}",
"",
" private static Stream<Arguments> streamChainTestCases() {",
" return Stream.of(1, 2).map(Arguments::arguments);",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"streamChainTestCases\")",
" void streamChain(int number) {}",
"",
" private static Stream<Arguments> multipleReturnsTestCases() {",
" if (true) {",
" return Stream.of(arguments(1), arguments(2));",
" } else {",
" return Stream.of(arguments(3), arguments(4));",
" }",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"multipleReturnsTestCases\")",
" void multipleReturns(int number) {}",
"",
" private static Stream<Arguments> multipleFactoriesFooTestCases() {",
" return Stream.of(arguments(1));",
" }",
"",
" private static Stream<Arguments> multipleFactoriesBarTestCases() {",
" return Stream.of(arguments(1));",
" }",
"",
" @ParameterizedTest",
" @MethodSource({\"multipleFactoriesFooTestCases\", \"multipleFactoriesBarTestCases\"})",
" void multipleFactories(int i) {}",
"",
" private static Stream<Arguments> extraArgsTestCases() {",
" return Stream.of(arguments(1), arguments(1, 2));",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"extraArgsTestCases\")",
" void extraArgs(int... i) {}",
"",
" private static Stream<Arguments> localClassTestCases() {",
" class Foo {",
" Stream<Arguments> foo() {",
" return Stream.of(arguments(1), arguments(2));",
" }",
" }",
" return Stream.of(arguments(1), arguments(2));",
" }",
"",
" @ParameterizedTest",
" // BUG: Diagnostic contains:",
" @MethodSource(\"localClassTestCases\")",
" void localClass(int i) {}",
"",
" private static Stream<Arguments> lambdaReturnTestCases() {",
" int foo =",
" Optional.of(10)",
" .map(",
" i -> {",
" return i / 2;",
" })",
" .orElse(0);",
" return Stream.of(arguments(1), arguments(1));",
" }",
"",
" @ParameterizedTest",
" // BUG: Diagnostic contains:",
" @MethodSource(\"lambdaReturnTestCases\")",
" void lambdaReturn(int i) {}",
"",
" @ParameterizedTest",
" @MethodSource(\"tech.picnic.errorprone.Foo#fooTestCases\")",
" void staticMethodReference(int foo) {}",
"",
" private static Stream<Arguments> valueFactoryWithArgumentTestCases(int amount) {",
" return Stream.of(arguments(1), arguments(2));",
" }",
"",
" @ParameterizedTest",
" // BUG: Diagnostic contains:",
" @MethodSource(\"valueFactoryWithArgumentTestCases\")",
" void valueFactoryWithArgument(int foo) {}",
"",
" private static Arguments[] emptyArrayValueFactoryTestCases() {",
" return new Arguments[] {};",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"emptyArrayValueFactoryTestCases\")",
" void emptyArrayValueFactory(int foo) {}",
"",
" private static Stream<Arguments> emptyStreamValueFactoryTestCases() {",
" return Stream.of();",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"emptyStreamValueFactoryTestCases\")",
" void emptyStreamValueFactory(int foo) {}",
"",
" private static Arguments[] invalidValueFactoryArgumentsTestCases() {",
" return new Arguments[] {arguments(1), arguments(new Object() {})};",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"invalidValueFactoryArgumentsTestCases\")",
" void invalidValueFactoryArguments(int foo) {}",
"}")
"""
import static org.junit.jupiter.params.provider.Arguments.arguments;
import java.util.Optional;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class A {
private static Stream<Arguments> identificationTestCases() {
return Stream.of(arguments(1), Arguments.of(2));
}
@ParameterizedTest
// BUG: Diagnostic contains:
@MethodSource("identificationTestCases")
void identification(int foo) {}
private static int[] identificationWithParensTestCases() {
return new int[] {1, 2};
}
@ParameterizedTest
// BUG: Diagnostic contains:
@MethodSource("identificationWithParensTestCases()")
void identificationWithParens(int foo) {}
@ParameterizedTest
@MethodSource("valueFactoryMissingTestCases")
void valueFactoryMissing(int foo) {}
private static Stream<Arguments> multipleUsagesTestCases() {
return Stream.of(arguments(1), Arguments.of(2));
}
@ParameterizedTest
@MethodSource("multipleUsagesTestCases")
void multipleUsages1(int foo) {}
@ParameterizedTest
@MethodSource("multipleUsagesTestCases()")
void multipleUsages2(int bar) {}
private static Stream<Arguments> valueFactoryRepeatedTestCases() {
return Stream.of(arguments(1), arguments(2));
}
@ParameterizedTest
@MethodSource({"valueFactoryRepeatedTestCases", "valueFactoryRepeatedTestCases"})
void valueFactoryRepeated(int foo) {}
private static Stream<Arguments> multipleParametersTestCases() {
return Stream.of(arguments(1, 2), arguments(3, 4));
}
@ParameterizedTest
@MethodSource("multipleParametersTestCases")
void multipleParameters(int first, int second) {}
private static int[] arrayWithoutInitializersTestCases() {
return new int[1];
}
@ParameterizedTest
@MethodSource("arrayWithoutInitializersTestCases")
void arrayWithoutInitializers(int foo) {}
private static Stream<Arguments> runtimeValueTestCases() {
int second = 2;
return Stream.of(arguments(1), arguments(second));
}
@ParameterizedTest
@MethodSource("runtimeValueTestCases")
void runtimeValue(int foo) {}
private static Stream<Arguments> streamChainTestCases() {
return Stream.of(1, 2).map(Arguments::arguments);
}
@ParameterizedTest
@MethodSource("streamChainTestCases")
void streamChain(int number) {}
private static Stream<Arguments> multipleReturnsTestCases() {
if (true) {
return Stream.of(arguments(1), arguments(2));
} else {
return Stream.of(arguments(3), arguments(4));
}
}
@ParameterizedTest
@MethodSource("multipleReturnsTestCases")
void multipleReturns(int number) {}
private static Stream<Arguments> multipleFactoriesFooTestCases() {
return Stream.of(arguments(1));
}
private static Stream<Arguments> multipleFactoriesBarTestCases() {
return Stream.of(arguments(1));
}
@ParameterizedTest
@MethodSource({"multipleFactoriesFooTestCases", "multipleFactoriesBarTestCases"})
void multipleFactories(int i) {}
private static Stream<Arguments> extraArgsTestCases() {
return Stream.of(arguments(1), arguments(1, 2));
}
@ParameterizedTest
@MethodSource("extraArgsTestCases")
void extraArgs(int... i) {}
private static Stream<Arguments> localClassTestCases() {
class Foo {
Stream<Arguments> foo() {
return Stream.of(arguments(1), arguments(2));
}
}
return Stream.of(arguments(1), arguments(2));
}
@ParameterizedTest
// BUG: Diagnostic contains:
@MethodSource("localClassTestCases")
void localClass(int i) {}
private static Stream<Arguments> lambdaReturnTestCases() {
int foo =
Optional.of(10)
.map(
i -> {
return i / 2;
})
.orElse(0);
return Stream.of(arguments(1), arguments(1));
}
@ParameterizedTest
// BUG: Diagnostic contains:
@MethodSource("lambdaReturnTestCases")
void lambdaReturn(int i) {}
@ParameterizedTest
@MethodSource("tech.picnic.errorprone.Foo#fooTestCases")
void staticMethodReference(int foo) {}
private static Stream<Arguments> valueFactoryWithArgumentTestCases(int amount) {
return Stream.of(arguments(1), arguments(2));
}
@ParameterizedTest
// BUG: Diagnostic contains:
@MethodSource("valueFactoryWithArgumentTestCases")
void valueFactoryWithArgument(int foo) {}
private static Arguments[] emptyArrayValueFactoryTestCases() {
return new Arguments[] {};
}
@ParameterizedTest
@MethodSource("emptyArrayValueFactoryTestCases")
void emptyArrayValueFactory(int foo) {}
private static Stream<Arguments> emptyStreamValueFactoryTestCases() {
return Stream.of();
}
@ParameterizedTest
@MethodSource("emptyStreamValueFactoryTestCases")
void emptyStreamValueFactory(int foo) {}
private static Arguments[] invalidValueFactoryArgumentsTestCases() {
return new Arguments[] {arguments(1), arguments(new Object() {})};
}
@ParameterizedTest
@MethodSource("invalidValueFactoryArgumentsTestCases")
void invalidValueFactoryArguments(int foo) {}
}
""")
.doTest();
}
@@ -202,295 +204,299 @@ final class JUnitValueSourceTest {
BugCheckerRefactoringTestHelper.newInstance(JUnitValueSource.class, getClass())
.addInputLines(
"A.java",
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
"",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import java.util.List;",
"import java.util.Set;",
"import java.util.stream.DoubleStream;",
"import java.util.stream.IntStream;",
"import java.util.stream.LongStream;",
"import java.util.stream.Stream;",
"import org.junit.jupiter.params.ParameterizedTest;",
"import org.junit.jupiter.params.provider.Arguments;",
"import org.junit.jupiter.params.provider.MethodSource;",
"",
"class A {",
" private static final boolean CONST_BOOLEAN = false;",
" private static final byte CONST_BYTE = 42;",
" private static final char CONST_CHARACTER = 'a';",
" private static final short CONST_SHORT = 42;",
" private static final int CONST_INTEGER = 42;",
" private static final long CONST_LONG = 42;",
" private static final float CONST_FLOAT = 42;",
" private static final double CONST_DOUBLE = 42;",
" private static final String CONST_STRING = \"foo\";",
"",
" private static Stream<Arguments> streamOfBooleanArguments() {",
" return Stream.of(arguments(false), arguments(true), arguments(CONST_BOOLEAN));",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"streamOfBooleanArguments\")",
" void primitiveBoolean(boolean b) {}",
"",
" private static Stream<Object> streamOfBooleansAndBooleanArguments() {",
" return Stream.of(false, arguments(true), CONST_BOOLEAN);",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"streamOfBooleansAndBooleanArguments\")",
" void boxedBoolean(Boolean b) {}",
"",
" private static List<Arguments> listOfByteArguments() {",
" return List.of(arguments((byte) 0), arguments((byte) 1), arguments(CONST_BYTE));",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"listOfByteArguments\")",
" void primitiveByte(byte b) {}",
"",
" private static List<Object> listOfBytesAndByteArguments() {",
" return List.of((byte) 0, arguments((byte) 1), CONST_BYTE);",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"listOfBytesAndByteArguments\")",
" void boxedByte(Byte b) {}",
"",
" private static Set<Arguments> setOfCharacterArguments() {",
" return Set.of(arguments((char) 0), arguments((char) 1), arguments(CONST_CHARACTER));",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"setOfCharacterArguments\")",
" void primitiveCharacter(char c) {}",
"",
" private static Set<Object> setOfCharactersAndCharacterArguments() {",
" return Set.of((char) 0, arguments((char) 1), CONST_CHARACTER);",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"setOfCharactersAndCharacterArguments\")",
" void boxedCharacter(Character c) {}",
"",
" private static Arguments[] arrayOfShortArguments() {",
" return new Arguments[] {arguments((short) 0), arguments((short) 1), arguments(CONST_SHORT)};",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"arrayOfShortArguments\")",
" void primitiveShort(short s) {}",
"",
" private static Object[] arrayOfShortsAndShortArguments() {",
" return new Object[] {(short) 0, arguments((short) 1), CONST_SHORT};",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"arrayOfShortsAndShortArguments\")",
" void boxedShort(Short s) {}",
"",
" private static IntStream intStream() {",
" return IntStream.of(0, 1, CONST_INTEGER);",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"intStream\")",
" void primitiveInteger(int i) {}",
"",
" private static int[] intArray() {",
" return new int[] {0, 1, CONST_INTEGER};",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"intArray\")",
" void boxedInteger(Integer i) {}",
"",
" private static LongStream longStream() {",
" return LongStream.of(0, 1, CONST_LONG);",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"longStream\")",
" void primitiveLong(long l) {}",
"",
" private static long[] longArray() {",
" return new long[] {0, 1, CONST_LONG};",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"longArray\")",
" void boxedLong(Long l) {}",
"",
" private static ImmutableList<Arguments> immutableListOfFloatArguments() {",
" return ImmutableList.of(arguments(0.0F), arguments(1.0F), arguments(CONST_FLOAT));",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"immutableListOfFloatArguments\")",
" void primitiveFloat(float f) {}",
"",
" private static Stream<Object> streamOfFloatsAndFloatArguments() {",
" return Stream.of(0.0F, arguments(1.0F), CONST_FLOAT);",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"streamOfFloatsAndFloatArguments\")",
" void boxedFloat(Float f) {}",
"",
" private static DoubleStream doubleStream() {",
" return DoubleStream.of(0, 1, CONST_DOUBLE);",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"doubleStream\")",
" void primitiveDouble(double d) {}",
"",
" private static double[] doubleArray() {",
" return new double[] {0, 1, CONST_DOUBLE};",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"doubleArray\")",
" void boxedDouble(Double d) {}",
"",
" private static ImmutableSet<Arguments> immutableSetOfStringArguments() {",
" return ImmutableSet.of(arguments(\"foo\"), arguments(\"bar\"), arguments(CONST_STRING));",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"immutableSetOfStringArguments\")",
" void string(String s) {}",
"",
" private static Stream<Class<?>> streamOfClasses() {",
" return Stream.of(Stream.class, java.util.Map.class);",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"streamOfClasses\")",
" void clazz(Class<?> c) {}",
"",
" private static Stream<Arguments> sameNameFactoryTestCases() {",
" return Stream.of(arguments(1));",
" }",
"",
" private static Stream<Arguments> sameNameFactoryTestCases(int overload) {",
" return Stream.of(arguments(overload));",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"sameNameFactoryTestCases\")",
" void sameNameFactory(int i) {}",
"}")
"""
import static org.junit.jupiter.params.provider.Arguments.arguments;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.List;
import java.util.Set;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class A {
private static final boolean CONST_BOOLEAN = false;
private static final byte CONST_BYTE = 42;
private static final char CONST_CHARACTER = 'a';
private static final short CONST_SHORT = 42;
private static final int CONST_INTEGER = 42;
private static final long CONST_LONG = 42;
private static final float CONST_FLOAT = 42;
private static final double CONST_DOUBLE = 42;
private static final String CONST_STRING = "foo";
private static Stream<Arguments> streamOfBooleanArguments() {
return Stream.of(arguments(false), arguments(true), arguments(CONST_BOOLEAN));
}
@ParameterizedTest
@MethodSource("streamOfBooleanArguments")
void primitiveBoolean(boolean b) {}
private static Stream<Object> streamOfBooleansAndBooleanArguments() {
return Stream.of(false, arguments(true), CONST_BOOLEAN);
}
@ParameterizedTest
@MethodSource("streamOfBooleansAndBooleanArguments")
void boxedBoolean(Boolean b) {}
private static List<Arguments> listOfByteArguments() {
return List.of(arguments((byte) 0), arguments((byte) 1), arguments(CONST_BYTE));
}
@ParameterizedTest
@MethodSource("listOfByteArguments")
void primitiveByte(byte b) {}
private static List<Object> listOfBytesAndByteArguments() {
return List.of((byte) 0, arguments((byte) 1), CONST_BYTE);
}
@ParameterizedTest
@MethodSource("listOfBytesAndByteArguments")
void boxedByte(Byte b) {}
private static Set<Arguments> setOfCharacterArguments() {
return Set.of(arguments((char) 0), arguments((char) 1), arguments(CONST_CHARACTER));
}
@ParameterizedTest
@MethodSource("setOfCharacterArguments")
void primitiveCharacter(char c) {}
private static Set<Object> setOfCharactersAndCharacterArguments() {
return Set.of((char) 0, arguments((char) 1), CONST_CHARACTER);
}
@ParameterizedTest
@MethodSource("setOfCharactersAndCharacterArguments")
void boxedCharacter(Character c) {}
private static Arguments[] arrayOfShortArguments() {
return new Arguments[] {arguments((short) 0), arguments((short) 1), arguments(CONST_SHORT)};
}
@ParameterizedTest
@MethodSource("arrayOfShortArguments")
void primitiveShort(short s) {}
private static Object[] arrayOfShortsAndShortArguments() {
return new Object[] {(short) 0, arguments((short) 1), CONST_SHORT};
}
@ParameterizedTest
@MethodSource("arrayOfShortsAndShortArguments")
void boxedShort(Short s) {}
private static IntStream intStream() {
return IntStream.of(0, 1, CONST_INTEGER);
}
@ParameterizedTest
@MethodSource("intStream")
void primitiveInteger(int i) {}
private static int[] intArray() {
return new int[] {0, 1, CONST_INTEGER};
}
@ParameterizedTest
@MethodSource("intArray")
void boxedInteger(Integer i) {}
private static LongStream longStream() {
return LongStream.of(0, 1, CONST_LONG);
}
@ParameterizedTest
@MethodSource("longStream")
void primitiveLong(long l) {}
private static long[] longArray() {
return new long[] {0, 1, CONST_LONG};
}
@ParameterizedTest
@MethodSource("longArray")
void boxedLong(Long l) {}
private static ImmutableList<Arguments> immutableListOfFloatArguments() {
return ImmutableList.of(arguments(0.0F), arguments(1.0F), arguments(CONST_FLOAT));
}
@ParameterizedTest
@MethodSource("immutableListOfFloatArguments")
void primitiveFloat(float f) {}
private static Stream<Object> streamOfFloatsAndFloatArguments() {
return Stream.of(0.0F, arguments(1.0F), CONST_FLOAT);
}
@ParameterizedTest
@MethodSource("streamOfFloatsAndFloatArguments")
void boxedFloat(Float f) {}
private static DoubleStream doubleStream() {
return DoubleStream.of(0, 1, CONST_DOUBLE);
}
@ParameterizedTest
@MethodSource("doubleStream")
void primitiveDouble(double d) {}
private static double[] doubleArray() {
return new double[] {0, 1, CONST_DOUBLE};
}
@ParameterizedTest
@MethodSource("doubleArray")
void boxedDouble(Double d) {}
private static ImmutableSet<Arguments> immutableSetOfStringArguments() {
return ImmutableSet.of(arguments("foo"), arguments("bar"), arguments(CONST_STRING));
}
@ParameterizedTest
@MethodSource("immutableSetOfStringArguments")
void string(String s) {}
private static Stream<Class<?>> streamOfClasses() {
return Stream.of(Stream.class, java.util.Map.class);
}
@ParameterizedTest
@MethodSource("streamOfClasses")
void clazz(Class<?> c) {}
private static Stream<Arguments> sameNameFactoryTestCases() {
return Stream.of(arguments(1));
}
private static Stream<Arguments> sameNameFactoryTestCases(int overload) {
return Stream.of(arguments(overload));
}
@ParameterizedTest
@MethodSource("sameNameFactoryTestCases")
void sameNameFactory(int i) {}
}
""")
.addOutputLines(
"A.java",
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
"",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import java.util.List;",
"import java.util.Set;",
"import java.util.stream.DoubleStream;",
"import java.util.stream.IntStream;",
"import java.util.stream.LongStream;",
"import java.util.stream.Stream;",
"import org.junit.jupiter.params.ParameterizedTest;",
"import org.junit.jupiter.params.provider.Arguments;",
"import org.junit.jupiter.params.provider.MethodSource;",
"import org.junit.jupiter.params.provider.ValueSource;",
"",
"class A {",
" private static final boolean CONST_BOOLEAN = false;",
" private static final byte CONST_BYTE = 42;",
" private static final char CONST_CHARACTER = 'a';",
" private static final short CONST_SHORT = 42;",
" private static final int CONST_INTEGER = 42;",
" private static final long CONST_LONG = 42;",
" private static final float CONST_FLOAT = 42;",
" private static final double CONST_DOUBLE = 42;",
" private static final String CONST_STRING = \"foo\";",
"",
" @ParameterizedTest",
" @ValueSource(booleans = {false, true, CONST_BOOLEAN})",
" void primitiveBoolean(boolean b) {}",
"",
" @ParameterizedTest",
" @ValueSource(booleans = {false, true, CONST_BOOLEAN})",
" void boxedBoolean(Boolean b) {}",
"",
" @ParameterizedTest",
" @ValueSource(bytes = {(byte) 0, (byte) 1, CONST_BYTE})",
" void primitiveByte(byte b) {}",
"",
" @ParameterizedTest",
" @ValueSource(bytes = {(byte) 0, (byte) 1, CONST_BYTE})",
" void boxedByte(Byte b) {}",
"",
" @ParameterizedTest",
" @ValueSource(chars = {(char) 0, (char) 1, CONST_CHARACTER})",
" void primitiveCharacter(char c) {}",
"",
" @ParameterizedTest",
" @ValueSource(chars = {(char) 0, (char) 1, CONST_CHARACTER})",
" void boxedCharacter(Character c) {}",
"",
" @ParameterizedTest",
" @ValueSource(shorts = {(short) 0, (short) 1, CONST_SHORT})",
" void primitiveShort(short s) {}",
"",
" @ParameterizedTest",
" @ValueSource(shorts = {(short) 0, (short) 1, CONST_SHORT})",
" void boxedShort(Short s) {}",
"",
" @ParameterizedTest",
" @ValueSource(ints = {0, 1, CONST_INTEGER})",
" void primitiveInteger(int i) {}",
"",
" @ParameterizedTest",
" @ValueSource(ints = {0, 1, CONST_INTEGER})",
" void boxedInteger(Integer i) {}",
"",
" @ParameterizedTest",
" @ValueSource(longs = {0, 1, CONST_LONG})",
" void primitiveLong(long l) {}",
"",
" @ParameterizedTest",
" @ValueSource(longs = {0, 1, CONST_LONG})",
" void boxedLong(Long l) {}",
"",
" @ParameterizedTest",
" @ValueSource(floats = {0.0F, 1.0F, CONST_FLOAT})",
" void primitiveFloat(float f) {}",
"",
" @ParameterizedTest",
" @ValueSource(floats = {0.0F, 1.0F, CONST_FLOAT})",
" void boxedFloat(Float f) {}",
"",
" @ParameterizedTest",
" @ValueSource(doubles = {0, 1, CONST_DOUBLE})",
" void primitiveDouble(double d) {}",
"",
" @ParameterizedTest",
" @ValueSource(doubles = {0, 1, CONST_DOUBLE})",
" void boxedDouble(Double d) {}",
"",
" @ParameterizedTest",
" @ValueSource(strings = {\"foo\", \"bar\", CONST_STRING})",
" void string(String s) {}",
"",
" @ParameterizedTest",
" @ValueSource(classes = {Stream.class, java.util.Map.class})",
" void clazz(Class<?> c) {}",
"",
" private static Stream<Arguments> sameNameFactoryTestCases(int overload) {",
" return Stream.of(arguments(overload));",
" }",
"",
" @ParameterizedTest",
" @ValueSource(ints = 1)",
" void sameNameFactory(int i) {}",
"}")
"""
import static org.junit.jupiter.params.provider.Arguments.arguments;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.List;
import java.util.Set;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
class A {
private static final boolean CONST_BOOLEAN = false;
private static final byte CONST_BYTE = 42;
private static final char CONST_CHARACTER = 'a';
private static final short CONST_SHORT = 42;
private static final int CONST_INTEGER = 42;
private static final long CONST_LONG = 42;
private static final float CONST_FLOAT = 42;
private static final double CONST_DOUBLE = 42;
private static final String CONST_STRING = "foo";
@ParameterizedTest
@ValueSource(booleans = {false, true, CONST_BOOLEAN})
void primitiveBoolean(boolean b) {}
@ParameterizedTest
@ValueSource(booleans = {false, true, CONST_BOOLEAN})
void boxedBoolean(Boolean b) {}
@ParameterizedTest
@ValueSource(bytes = {(byte) 0, (byte) 1, CONST_BYTE})
void primitiveByte(byte b) {}
@ParameterizedTest
@ValueSource(bytes = {(byte) 0, (byte) 1, CONST_BYTE})
void boxedByte(Byte b) {}
@ParameterizedTest
@ValueSource(chars = {(char) 0, (char) 1, CONST_CHARACTER})
void primitiveCharacter(char c) {}
@ParameterizedTest
@ValueSource(chars = {(char) 0, (char) 1, CONST_CHARACTER})
void boxedCharacter(Character c) {}
@ParameterizedTest
@ValueSource(shorts = {(short) 0, (short) 1, CONST_SHORT})
void primitiveShort(short s) {}
@ParameterizedTest
@ValueSource(shorts = {(short) 0, (short) 1, CONST_SHORT})
void boxedShort(Short s) {}
@ParameterizedTest
@ValueSource(ints = {0, 1, CONST_INTEGER})
void primitiveInteger(int i) {}
@ParameterizedTest
@ValueSource(ints = {0, 1, CONST_INTEGER})
void boxedInteger(Integer i) {}
@ParameterizedTest
@ValueSource(longs = {0, 1, CONST_LONG})
void primitiveLong(long l) {}
@ParameterizedTest
@ValueSource(longs = {0, 1, CONST_LONG})
void boxedLong(Long l) {}
@ParameterizedTest
@ValueSource(floats = {0.0F, 1.0F, CONST_FLOAT})
void primitiveFloat(float f) {}
@ParameterizedTest
@ValueSource(floats = {0.0F, 1.0F, CONST_FLOAT})
void boxedFloat(Float f) {}
@ParameterizedTest
@ValueSource(doubles = {0, 1, CONST_DOUBLE})
void primitiveDouble(double d) {}
@ParameterizedTest
@ValueSource(doubles = {0, 1, CONST_DOUBLE})
void boxedDouble(Double d) {}
@ParameterizedTest
@ValueSource(strings = {"foo", "bar", CONST_STRING})
void string(String s) {}
@ParameterizedTest
@ValueSource(classes = {Stream.class, java.util.Map.class})
void clazz(Class<?> c) {}
private static Stream<Arguments> sameNameFactoryTestCases(int overload) {
return Stream.of(arguments(overload));
}
@ParameterizedTest
@ValueSource(ints = 1)
void sameNameFactory(int i) {}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -12,146 +12,148 @@ final class LexicographicalAnnotationAttributeListingTest {
CompilationTestHelper.newInstance(LexicographicalAnnotationAttributeListing.class, getClass())
.addSourceLines(
"A.java",
"import static java.math.RoundingMode.DOWN;",
"import static java.math.RoundingMode.UP;",
"",
"import com.fasterxml.jackson.annotation.JsonPropertyOrder;",
"import io.swagger.annotations.ApiImplicitParam;",
"import io.swagger.annotations.ApiImplicitParams;",
"import io.swagger.v3.oas.annotations.Parameter;",
"import io.swagger.v3.oas.annotations.Parameters;",
"import java.math.RoundingMode;",
"import javax.xml.bind.annotation.XmlType;",
"import org.springframework.context.annotation.PropertySource;",
"import org.springframework.test.context.TestPropertySource;",
"",
"interface A {",
" @interface Foo {",
" String[] value() default {};",
"",
" int[] ints() default {};",
"",
" Class<?>[] cls() default {};",
"",
" RoundingMode[] enums() default {};",
"",
" Bar[] anns() default {};",
" }",
"",
" @interface Bar {",
" String[] value() default {};",
" }",
"",
" @Foo({})",
" A noString();",
"",
" @Foo({\"a\"})",
" A oneString();",
"",
" @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();",
"",
" @Foo(ints = {})",
" A noInts();",
"",
" @Foo(ints = {0})",
" A oneInt();",
"",
" @Foo(ints = {0, 1})",
" A sortedInts();",
"",
" @Foo(ints = {1, 0})",
" A unsortedInts();",
"",
" @Foo(cls = {})",
" A noClasses();",
"",
" @Foo(cls = {int.class})",
" A oneClass();",
"",
" @Foo(cls = {int.class, long.class})",
" A sortedClasses();",
"",
" // BUG: Diagnostic contains:",
" @Foo(cls = {long.class, int.class})",
" A unsortedClasses();",
"",
" @Foo(enums = {})",
" A noEnums();",
"",
" @Foo(enums = {DOWN})",
" A oneEnum();",
"",
" @Foo(enums = {DOWN, UP})",
" A sortedEnums();",
"",
" // BUG: Diagnostic contains:",
" @Foo(enums = {UP, DOWN})",
" A unsortedEnums();",
"",
" @Foo(anns = {})",
" A noAnns();",
"",
" @Foo(anns = {@Bar(\"a\")})",
" A oneAnn();",
"",
" @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();",
"",
" @JsonPropertyOrder({\"field2\", \"field1\"})",
" A dto();",
"",
" @ApiImplicitParams({@ApiImplicitParam(\"p2\"), @ApiImplicitParam(\"p1\")})",
" A firstEndpoint();",
"",
" @Parameters({@Parameter(name = \"p2\"), @Parameter(name = \"p1\")})",
" A secondEndpoint();",
"",
" @XmlType(propOrder = {\"field2\", \"field1\"})",
" class XmlTypeDummy {}",
"",
" @PropertySource({\"field2\", \"field1\"})",
" class PropertySourceDummy {}",
"",
" @TestPropertySource(locations = {\"field2\", \"field1\"})",
" class FirstTestPropertySourceDummy {}",
"",
" @TestPropertySource({\"field2\", \"field1\"})",
" class SecondTestPropertySourceDummy {}",
"}")
"""
import static java.math.RoundingMode.DOWN;
import static java.math.RoundingMode.UP;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import java.math.RoundingMode;
import javax.xml.bind.annotation.XmlType;
import org.springframework.context.annotation.PropertySource;
import org.springframework.test.context.TestPropertySource;
interface A {
@interface Foo {
String[] value() default {};
int[] ints() default {};
Class<?>[] cls() default {};
RoundingMode[] enums() default {};
Bar[] anns() default {};
}
@interface Bar {
String[] value() default {};
}
@Foo({})
A noString();
@Foo({"a"})
A oneString();
@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();
@Foo(ints = {})
A noInts();
@Foo(ints = {0})
A oneInt();
@Foo(ints = {0, 1})
A sortedInts();
@Foo(ints = {1, 0})
A unsortedInts();
@Foo(cls = {})
A noClasses();
@Foo(cls = {int.class})
A oneClass();
@Foo(cls = {int.class, long.class})
A sortedClasses();
// BUG: Diagnostic contains:
@Foo(cls = {long.class, int.class})
A unsortedClasses();
@Foo(enums = {})
A noEnums();
@Foo(enums = {DOWN})
A oneEnum();
@Foo(enums = {DOWN, UP})
A sortedEnums();
// BUG: Diagnostic contains:
@Foo(enums = {UP, DOWN})
A unsortedEnums();
@Foo(anns = {})
A noAnns();
@Foo(anns = {@Bar("a")})
A oneAnn();
@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();
@JsonPropertyOrder({"field2", "field1"})
A dto();
@ApiImplicitParams({@ApiImplicitParam("p2"), @ApiImplicitParam("p1")})
A firstEndpoint();
@Parameters({@Parameter(name = "p2"), @Parameter(name = "p1")})
A secondEndpoint();
@XmlType(propOrder = {"field2", "field1"})
class XmlTypeDummy {}
@PropertySource({"field2", "field1"})
class PropertySourceDummy {}
@TestPropertySource(locations = {"field2", "field1"})
class FirstTestPropertySourceDummy {}
@TestPropertySource({"field2", "field1"})
class SecondTestPropertySourceDummy {}
}
""")
.doTest();
}
@@ -164,78 +166,82 @@ final class LexicographicalAnnotationAttributeListingTest {
LexicographicalAnnotationAttributeListing.class, getClass())
.addInputLines(
"A.java",
"import static java.math.RoundingMode.DOWN;",
"import static java.math.RoundingMode.UP;",
"",
"import java.math.RoundingMode;",
"",
"interface A {",
" @interface Foo {",
" String[] value() default {};",
"",
" Class<?>[] cls() default {};",
"",
" RoundingMode[] enums() default {};",
"",
" Bar[] anns() default {};",
" }",
"",
" @interface Bar {",
" String[] value() default {};",
" }",
"",
" @Foo({\" \", \"\", \"b\", \"a\"})",
" A unsortedString();",
"",
" @Foo(cls = {long.class, int.class})",
" A unsortedClasses();",
"",
" @Foo(enums = {UP, DOWN})",
" A unsortedEnums();",
"",
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
" A unsortedAnns();",
"",
" @Foo(anns = {@Bar(\"a\"), @Bar({\"b\", \"a\"})})",
" A unsortedInnderAnns();",
"}")
"""
import static java.math.RoundingMode.DOWN;
import static java.math.RoundingMode.UP;
import java.math.RoundingMode;
interface A {
@interface Foo {
String[] value() default {};
Class<?>[] cls() default {};
RoundingMode[] enums() default {};
Bar[] anns() default {};
}
@interface Bar {
String[] value() default {};
}
@Foo({" ", "", "b", "a"})
A unsortedString();
@Foo(cls = {long.class, int.class})
A unsortedClasses();
@Foo(enums = {UP, DOWN})
A unsortedEnums();
@Foo(anns = {@Bar("b"), @Bar("a")})
A unsortedAnns();
@Foo(anns = {@Bar("a"), @Bar({"b", "a"})})
A unsortedInnderAnns();
}
""")
.addOutputLines(
"A.java",
"import static java.math.RoundingMode.DOWN;",
"import static java.math.RoundingMode.UP;",
"",
"import java.math.RoundingMode;",
"",
"interface A {",
" @interface Foo {",
" String[] value() default {};",
"",
" Class<?>[] cls() default {};",
"",
" RoundingMode[] enums() default {};",
"",
" Bar[] anns() default {};",
" }",
"",
" @interface Bar {",
" String[] value() default {};",
" }",
"",
" @Foo({\"\", \" \", \"a\", \"b\"})",
" A unsortedString();",
"",
" @Foo(cls = {int.class, long.class})",
" A unsortedClasses();",
"",
" @Foo(enums = {DOWN, UP})",
" A unsortedEnums();",
"",
" @Foo(anns = {@Bar(\"a\"), @Bar(\"b\")})",
" A unsortedAnns();",
"",
" @Foo(anns = {@Bar(\"a\"), @Bar({\"a\", \"b\"})})",
" A unsortedInnderAnns();",
"}")
"""
import static java.math.RoundingMode.DOWN;
import static java.math.RoundingMode.UP;
import java.math.RoundingMode;
interface A {
@interface Foo {
String[] value() default {};
Class<?>[] cls() default {};
RoundingMode[] enums() default {};
Bar[] anns() default {};
}
@interface Bar {
String[] value() default {};
}
@Foo({"", " ", "a", "b"})
A unsortedString();
@Foo(cls = {int.class, long.class})
A unsortedClasses();
@Foo(enums = {DOWN, UP})
A unsortedEnums();
@Foo(anns = {@Bar("a"), @Bar("b")})
A unsortedAnns();
@Foo(anns = {@Bar("a"), @Bar({"a", "b"})})
A unsortedInnderAnns();
}
""")
.doTest(TestMode.TEXT_MATCH);
}
@@ -249,48 +255,50 @@ final class LexicographicalAnnotationAttributeListingTest {
"-XepOpt:LexicographicalAnnotationAttributeListing:Excludes=pkg.A.Bar#value"))
.addSourceLines(
"pkg/A.java",
"package pkg;",
"",
"interface A {",
" @interface Foo {",
" String[] value() default {};",
"",
" String[] value2() default {};",
" }",
"",
" @interface Bar {",
" String[] value() default {};",
"",
" String[] value2() default {};",
" }",
"",
" @interface Baz {",
" String[] value() default {};",
"",
" String[] value2() default {};",
" }",
"",
" // 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();",
"",
" @Baz({\"b\", \"a\"})",
" A bazValue();",
"",
" @Baz(value2 = {\"b\", \"a\"})",
" A bazValue2();",
"}")
"""
package pkg;
interface A {
@interface Foo {
String[] value() default {};
String[] value2() default {};
}
@interface Bar {
String[] value() default {};
String[] value2() default {};
}
@interface Baz {
String[] value() default {};
String[] value2() default {};
}
// 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();
@Baz({"b", "a"})
A bazValue();
@Baz(value2 = {"b", "a"})
A bazValue2();
}
""")
.doTest();
}
}

View File

@@ -11,122 +11,124 @@ final class LexicographicalAnnotationListingTest {
CompilationTestHelper.newInstance(LexicographicalAnnotationListing.class, getClass())
.addSourceLines(
"A.java",
"import java.lang.annotation.ElementType;",
"import java.lang.annotation.Repeatable;",
"import java.lang.annotation.Target;",
"",
"interface A {",
" @Repeatable(Foos.class)",
" @interface Foo {",
" String[] value() default {};",
"",
" int[] ints() default {};",
"",
" Bar[] anns() default {};",
" }",
"",
" @Target(ElementType.METHOD)",
" @interface Bar {",
" String[] value() default {};",
" }",
"",
" @interface Baz {",
" String[] str() default {};",
" }",
"",
" @interface Foos {",
" Foo[] value();",
" }",
"",
" @Target(ElementType.TYPE_USE)",
" @interface FooTypeUse {",
" String[] value() default {};",
" }",
"",
" @Target(ElementType.TYPE_USE)",
" @interface BarTypeUse {",
" String[] value() default {};",
" }",
"",
" // BUG: Diagnostic contains:",
" @Foo",
" @Bar",
" A unsortedSimpleCase();",
"",
" // BUG: Diagnostic contains:",
" @Foo()",
" @Bar()",
" A unsortedWithParens();",
"",
" @Foo()",
" A onlyOneAnnotation();",
"",
" @Bar",
" @Foo()",
" A sortedAnnotationsOneWithParens();",
"",
" // BUG: Diagnostic contains:",
" @Foo",
" @Baz",
" @Bar",
" A threeUnsortedAnnotationsSameInitialLetter();",
"",
" // BUG: Diagnostic contains:",
" @Bar",
" @Foo()",
" @Baz",
" A firstOrderedWithTwoUnsortedAnnotations();",
"",
" @Bar",
" @Baz",
" @Foo()",
" A threeSortedAnnotations();",
"",
" // BUG: Diagnostic contains:",
" @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\")})",
" @Baz",
" A unsortedWithNestedBar();",
"",
" @Bar",
" @Baz",
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
" A sortedWithNestedBar();",
"",
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
" @Foo(ints = {1, 2})",
" @Foo({\"b\"})",
" A sortedRepeatableAnnotation();",
"",
" // BUG: Diagnostic contains:",
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
" @Bar",
" @Foo(ints = {1, 2})",
" A unsortedRepeatableAnnotation();",
"",
" // BUG: Diagnostic contains:",
" default @FooTypeUse @BarTypeUse A unsortedTypeAnnotations() {",
" return null;",
" }",
"",
" // BUG: Diagnostic contains:",
" @Baz",
" @Bar",
" default @FooTypeUse @BarTypeUse A unsortedTypeUseAndOtherAnnotations() {",
" return null;",
" }",
"}")
"""
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Target;
interface A {
@Repeatable(Foos.class)
@interface Foo {
String[] value() default {};
int[] ints() default {};
Bar[] anns() default {};
}
@Target(ElementType.METHOD)
@interface Bar {
String[] value() default {};
}
@interface Baz {
String[] str() default {};
}
@interface Foos {
Foo[] value();
}
@Target(ElementType.TYPE_USE)
@interface FooTypeUse {
String[] value() default {};
}
@Target(ElementType.TYPE_USE)
@interface BarTypeUse {
String[] value() default {};
}
// BUG: Diagnostic contains:
@Foo
@Bar
A unsortedSimpleCase();
// BUG: Diagnostic contains:
@Foo()
@Bar()
A unsortedWithParens();
@Foo()
A onlyOneAnnotation();
@Bar
@Foo()
A sortedAnnotationsOneWithParens();
// BUG: Diagnostic contains:
@Foo
@Baz
@Bar
A threeUnsortedAnnotationsSameInitialLetter();
// BUG: Diagnostic contains:
@Bar
@Foo()
@Baz
A firstOrderedWithTwoUnsortedAnnotations();
@Bar
@Baz
@Foo()
A threeSortedAnnotations();
// BUG: Diagnostic contains:
@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")})
@Baz
A unsortedWithNestedBar();
@Bar
@Baz
@Foo(anns = {@Bar("b"), @Bar("a")})
A sortedWithNestedBar();
@Foo(anns = {@Bar("b"), @Bar("a")})
@Foo(ints = {1, 2})
@Foo({"b"})
A sortedRepeatableAnnotation();
// BUG: Diagnostic contains:
@Foo(anns = {@Bar("b"), @Bar("a")})
@Bar
@Foo(ints = {1, 2})
A unsortedRepeatableAnnotation();
// BUG: Diagnostic contains:
default @FooTypeUse @BarTypeUse A unsortedTypeAnnotations() {
return null;
}
// BUG: Diagnostic contains:
@Baz
@Bar
default @FooTypeUse @BarTypeUse A unsortedTypeUseAndOtherAnnotations() {
return null;
}
}
""")
.doTest();
}
@@ -135,166 +137,170 @@ final class LexicographicalAnnotationListingTest {
BugCheckerRefactoringTestHelper.newInstance(LexicographicalAnnotationListing.class, getClass())
.addInputLines(
"A.java",
"import java.lang.annotation.ElementType;",
"import java.lang.annotation.Repeatable;",
"import java.lang.annotation.Target;",
"",
"interface A {",
" @Repeatable(Foos.class)",
" @interface Foo {",
" String[] value() default {};",
"",
" int[] ints() default {};",
"",
" Bar[] anns() default {};",
" }",
"",
" @Target(ElementType.METHOD)",
" @interface Bar {",
" String[] value() default {};",
" }",
"",
" @interface Baz {",
" String[] str() default {};",
" }",
"",
" @interface Foos {",
" Foo[] value();",
" }",
"",
" @Target(ElementType.TYPE_USE)",
" @interface FooTypeUse {",
" String[] value() default {};",
" }",
"",
" @Target(ElementType.TYPE_USE)",
" @interface BarTypeUse {",
" String[] value() default {};",
" }",
"",
" @Bar",
" A singleAnnotation();",
"",
" @Bar",
" @Foo",
" A sortedAnnotations();",
"",
" @Foo",
" @Bar",
" A unsortedAnnotations();",
"",
" @Foo()",
" @Baz()",
" @Bar",
" A unsortedAnnotationsWithSomeParens();",
"",
" @Bar",
" @Baz(str = {\"a\", \"b\"})",
" @Foo()",
" A unsortedAnnotationsOneContainingAttributes();",
"",
" @Baz(str = {\"a\", \"b\"})",
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
" @Bar({\"b\"})",
" A unsortedAnnotationsWithAttributes();",
"",
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
" @Foo(ints = {1, 2})",
" @Foo({\"b\"})",
" A sortedRepeatableAnnotation();",
"",
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
" @Bar",
" @Foo(ints = {1, 2})",
" A unsortedRepeatableAnnotation();",
"",
" @Baz",
" @Bar",
" default @FooTypeUse @BarTypeUse A unsortedWithTypeUseAnnotations() {",
" return null;",
" }",
"}")
"""
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Target;
interface A {
@Repeatable(Foos.class)
@interface Foo {
String[] value() default {};
int[] ints() default {};
Bar[] anns() default {};
}
@Target(ElementType.METHOD)
@interface Bar {
String[] value() default {};
}
@interface Baz {
String[] str() default {};
}
@interface Foos {
Foo[] value();
}
@Target(ElementType.TYPE_USE)
@interface FooTypeUse {
String[] value() default {};
}
@Target(ElementType.TYPE_USE)
@interface BarTypeUse {
String[] value() default {};
}
@Bar
A singleAnnotation();
@Bar
@Foo
A sortedAnnotations();
@Foo
@Bar
A unsortedAnnotations();
@Foo()
@Baz()
@Bar
A unsortedAnnotationsWithSomeParens();
@Bar
@Baz(str = {"a", "b"})
@Foo()
A unsortedAnnotationsOneContainingAttributes();
@Baz(str = {"a", "b"})
@Foo(anns = {@Bar("b"), @Bar("a")})
@Bar({"b"})
A unsortedAnnotationsWithAttributes();
@Foo(anns = {@Bar("b"), @Bar("a")})
@Foo(ints = {1, 2})
@Foo({"b"})
A sortedRepeatableAnnotation();
@Foo(anns = {@Bar("b"), @Bar("a")})
@Bar
@Foo(ints = {1, 2})
A unsortedRepeatableAnnotation();
@Baz
@Bar
default @FooTypeUse @BarTypeUse A unsortedWithTypeUseAnnotations() {
return null;
}
}
""")
.addOutputLines(
"A.java",
"import java.lang.annotation.ElementType;",
"import java.lang.annotation.Repeatable;",
"import java.lang.annotation.Target;",
"",
"interface A {",
" @Repeatable(Foos.class)",
" @interface Foo {",
" String[] value() default {};",
"",
" int[] ints() default {};",
"",
" Bar[] anns() default {};",
" }",
"",
" @Target(ElementType.METHOD)",
" @interface Bar {",
" String[] value() default {};",
" }",
"",
" @interface Baz {",
" String[] str() default {};",
" }",
"",
" @interface Foos {",
" Foo[] value();",
" }",
"",
" @Target(ElementType.TYPE_USE)",
" @interface FooTypeUse {",
" String[] value() default {};",
" }",
"",
" @Target(ElementType.TYPE_USE)",
" @interface BarTypeUse {",
" String[] value() default {};",
" }",
"",
" @Bar",
" A singleAnnotation();",
"",
" @Bar",
" @Foo",
" A sortedAnnotations();",
"",
" @Bar",
" @Foo",
" A unsortedAnnotations();",
"",
" @Bar",
" @Baz()",
" @Foo()",
" A unsortedAnnotationsWithSomeParens();",
"",
" @Bar",
" @Baz(str = {\"a\", \"b\"})",
" @Foo()",
" A unsortedAnnotationsOneContainingAttributes();",
"",
" @Bar({\"b\"})",
" @Baz(str = {\"a\", \"b\"})",
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
" A unsortedAnnotationsWithAttributes();",
"",
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
" @Foo(ints = {1, 2})",
" @Foo({\"b\"})",
" A sortedRepeatableAnnotation();",
"",
" @Bar",
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
" @Foo(ints = {1, 2})",
" A unsortedRepeatableAnnotation();",
"",
" @Bar",
" @Baz",
" default @BarTypeUse @FooTypeUse A unsortedWithTypeUseAnnotations() {",
" return null;",
" }",
"}")
"""
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Target;
interface A {
@Repeatable(Foos.class)
@interface Foo {
String[] value() default {};
int[] ints() default {};
Bar[] anns() default {};
}
@Target(ElementType.METHOD)
@interface Bar {
String[] value() default {};
}
@interface Baz {
String[] str() default {};
}
@interface Foos {
Foo[] value();
}
@Target(ElementType.TYPE_USE)
@interface FooTypeUse {
String[] value() default {};
}
@Target(ElementType.TYPE_USE)
@interface BarTypeUse {
String[] value() default {};
}
@Bar
A singleAnnotation();
@Bar
@Foo
A sortedAnnotations();
@Bar
@Foo
A unsortedAnnotations();
@Bar
@Baz()
@Foo()
A unsortedAnnotationsWithSomeParens();
@Bar
@Baz(str = {"a", "b"})
@Foo()
A unsortedAnnotationsOneContainingAttributes();
@Bar({"b"})
@Baz(str = {"a", "b"})
@Foo(anns = {@Bar("b"), @Bar("a")})
A unsortedAnnotationsWithAttributes();
@Foo(anns = {@Bar("b"), @Bar("a")})
@Foo(ints = {1, 2})
@Foo({"b"})
A sortedRepeatableAnnotation();
@Bar
@Foo(anns = {@Bar("b"), @Bar("a")})
@Foo(ints = {1, 2})
A unsortedRepeatableAnnotation();
@Bar
@Baz
default @BarTypeUse @FooTypeUse A unsortedWithTypeUseAnnotations() {
return null;
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,308 +11,310 @@ final class MethodReferenceUsageTest {
CompilationTestHelper.newInstance(MethodReferenceUsage.class, getClass())
.addSourceLines(
"A.java",
"import com.google.common.collect.Streams;",
"import java.util.HashMap;",
"import java.util.Map;",
"import java.util.function.IntConsumer;",
"import java.util.function.IntFunction;",
"import java.util.stream.Stream;",
"",
"class A {",
" private final Stream<Integer> s = Stream.of(1);",
" private final Map<Integer, Integer> m = new HashMap<>();",
" private final Runnable thrower =",
" () -> {",
" throw new RuntimeException();",
" };",
"",
" void unaryExternalStaticFunctionCalls() {",
" s.forEach(String::valueOf);",
" // BUG: Diagnostic contains:",
" s.forEach(v -> String.valueOf(v));",
" s.forEach(",
" // BUG: Diagnostic contains:",
" (v) -> {",
" String.valueOf(v);",
" });",
" s.forEach(",
" // BUG: Diagnostic contains:",
" (Integer v) -> {",
" {",
" String.valueOf(v);",
" }",
" });",
" s.forEach(",
" v -> {",
" String.valueOf(v);",
" String.valueOf(v);",
" });",
"",
" s.map(String::valueOf);",
" // BUG: Diagnostic contains:",
" s.map(v -> String.valueOf(v));",
" // BUG: Diagnostic contains:",
" s.map((v) -> (String.valueOf(v)));",
" s.map(",
" // BUG: Diagnostic contains:",
" (Integer v) -> {",
" return String.valueOf(v);",
" });",
" s.map(",
" // BUG: Diagnostic contains:",
" (final Integer v) -> {",
" return (String.valueOf(v));",
" });",
" s.map(",
" v -> {",
" String.valueOf(v);",
" return String.valueOf(v);",
" });",
"",
" s.findFirst().orElseGet(() -> Integer.valueOf(\"0\"));",
" m.forEach((k, v) -> String.valueOf(v));",
" m.forEach((k, v) -> String.valueOf(k));",
" }",
"",
" void binaryExternalInstanceFunctionCalls() {",
" m.forEach(m::put);",
" // BUG: Diagnostic contains:",
" m.forEach((k, v) -> m.put(k, v));",
" m.forEach((k, v) -> m.put(v, k));",
" m.forEach(",
" // BUG: Diagnostic contains:",
" (Integer k, Integer v) -> {",
" m.put(k, v);",
" });",
" m.forEach(",
" (k, v) -> {",
" m.put(k, k);",
" });",
" m.forEach(",
" // BUG: Diagnostic contains:",
" (final Integer k, final Integer v) -> {",
" {",
" m.put(k, v);",
" }",
" });",
" m.forEach(",
" (k, v) -> {",
" {",
" m.put(v, v);",
" }",
" });",
" m.forEach((k, v) -> new HashMap<Integer, Integer>().put(k, v));",
" m.forEach(",
" (k, v) -> {",
" m.put(k, v);",
" m.put(k, v);",
" });",
"",
" Streams.zip(s, s, m::put);",
" // BUG: Diagnostic contains:",
" Streams.zip(s, s, (a, b) -> m.put(a, b));",
" Streams.zip(s, s, (a, b) -> m.put(b, a));",
" // BUG: Diagnostic contains:",
" Streams.zip(s, s, (Integer a, Integer b) -> (m.put(a, b)));",
" Streams.zip(s, s, (a, b) -> (m.put(a, a)));",
" Streams.zip(",
" s,",
" s,",
" // BUG: Diagnostic contains:",
" (final Integer a, final Integer b) -> {",
" return m.put(a, b);",
" });",
" Streams.zip(",
" s,",
" s,",
" (a, b) -> {",
" return m.put(b, b);",
" });",
" Streams.zip(",
" s,",
" s,",
" // BUG: Diagnostic contains:",
" (a, b) -> {",
" return (m.put(a, b));",
" });",
" Streams.zip(",
" s,",
" s,",
" (a, b) -> {",
" return (m.put(b, a));",
" });",
" Streams.zip(",
" s,",
" s,",
" (a, b) -> {",
" m.put(a, b);",
" return m.put(a, b);",
" });",
" }",
"",
" void nullaryExternalInstanceFunctionCalls() {",
" s.map(Integer::doubleValue);",
" // BUG: Diagnostic contains:",
" s.map(i -> i.doubleValue());",
" s.map(i -> i.toString());",
" s.map(i -> s.toString());",
"",
" // BUG: Diagnostic contains:",
" Stream.of(int.class).filter(c -> c.isEnum());",
" Stream.of((Class<?>) int.class).filter(Class::isEnum);",
" // BUG: Diagnostic contains:",
" Stream.of((Class<?>) int.class).filter(c -> c.isEnum());",
" }",
"",
" void localFunctionCalls() {",
" s.forEach(v -> ivoid0());",
" s.forEach(v -> iint0());",
" s.forEach(v -> svoid0());",
" s.forEach(v -> sint0());",
"",
" s.forEach(this::ivoid1);",
" // BUG: Diagnostic contains:",
" s.forEach(v -> ivoid1(v));",
" s.forEach(",
" // BUG: Diagnostic contains:",
" v -> {",
" ivoid1(v);",
" });",
" s.forEach(this::iint1);",
" // BUG: Diagnostic contains:",
" s.forEach(v -> iint1(v));",
" s.forEach(",
" // BUG: Diagnostic contains:",
" v -> {",
" iint1(v);",
" });",
"",
" s.forEach(A::svoid1);",
" // BUG: Diagnostic contains:",
" s.forEach(v -> svoid1(v));",
" s.forEach(",
" // BUG: Diagnostic contains:",
" v -> {",
" svoid1(v);",
" });",
" s.forEach(A::sint1);",
" // BUG: Diagnostic contains:",
" s.forEach(v -> sint1(v));",
" s.forEach(",
" // BUG: Diagnostic contains:",
" v -> {",
" sint1(v);",
" });",
"",
" s.forEach(v -> ivoid2(v, v));",
" s.forEach(v -> iint2(v, v));",
" s.forEach(v -> svoid2(v, v));",
" s.forEach(v -> sint2(v, v));",
"",
" m.forEach((k, v) -> ivoid0());",
" m.forEach((k, v) -> iint0());",
" m.forEach((k, v) -> svoid0());",
" m.forEach((k, v) -> sint0());",
"",
" m.forEach(this::ivoid2);",
" // BUG: Diagnostic contains:",
" m.forEach((k, v) -> ivoid2(k, v));",
" m.forEach(",
" // BUG: Diagnostic contains:",
" (k, v) -> {",
" ivoid2(k, v);",
" });",
" m.forEach(this::iint2);",
" // BUG: Diagnostic contains:",
" m.forEach((k, v) -> iint2(k, v));",
" m.forEach(",
" // BUG: Diagnostic contains:",
" (k, v) -> {",
" iint2(k, v);",
" });",
"",
" m.forEach(A::svoid2);",
" // BUG: Diagnostic contains:",
" m.forEach((k, v) -> svoid2(k, v));",
" m.forEach(",
" // BUG: Diagnostic contains:",
" (k, v) -> {",
" svoid2(k, v);",
" });",
" m.forEach(A::sint2);",
" // BUG: Diagnostic contains:",
" m.forEach((k, v) -> sint2(k, v));",
" m.forEach(",
" // BUG: Diagnostic contains:",
" (k, v) -> {",
" sint2(k, v);",
" });",
" }",
"",
" void functionCallsWhoseReplacementWouldBeAmbiguous() {",
" receiver(",
" i -> {",
" Integer.toString(i);",
" });",
" }",
"",
" void assortedOtherEdgeCases() {",
" s.forEach(v -> String.valueOf(v.toString()));",
" TernaryOp o1 = (a, b, c) -> String.valueOf(a);",
" TernaryOp o2 = (a, b, c) -> String.valueOf(b);",
" TernaryOp o3 = (a, b, c) -> String.valueOf(c);",
" TernaryOp o4 = (a, b, c) -> c.concat(a);",
" TernaryOp o5 = (a, b, c) -> c.concat(b);",
" TernaryOp o6 = (a, b, c) -> a.concat(c);",
" TernaryOp o7 = (a, b, c) -> b.concat(c);",
" }",
"",
" void receiver(IntFunction<?> op) {}",
"",
" void receiver(IntConsumer op) {}",
"",
" void ivoid0() {}",
"",
" void ivoid1(int a) {}",
"",
" void ivoid2(int a, int b) {}",
"",
" int iint0() {",
" return 0;",
" }",
"",
" int iint1(int a) {",
" return 0;",
" }",
"",
" int iint2(int a, int b) {",
" return 0;",
" }",
"",
" static void svoid0() {}",
"",
" static void svoid1(int a) {}",
"",
" static void svoid2(int a, int b) {}",
"",
" static void svoid3(int a, int b, int c) {}",
"",
" static int sint0() {",
" return 0;",
" }",
"",
" static int sint1(int a) {",
" return 0;",
" }",
"",
" static int sint2(int a, int b) {",
" return 0;",
" }",
"",
" interface TernaryOp {",
" String collect(String a, String b, String c);",
" }",
"}")
"""
import com.google.common.collect.Streams;
import java.util.HashMap;
import java.util.Map;
import java.util.function.IntConsumer;
import java.util.function.IntFunction;
import java.util.stream.Stream;
class A {
private final Stream<Integer> s = Stream.of(1);
private final Map<Integer, Integer> m = new HashMap<>();
private final Runnable thrower =
() -> {
throw new RuntimeException();
};
void unaryExternalStaticFunctionCalls() {
s.forEach(String::valueOf);
// BUG: Diagnostic contains:
s.forEach(v -> String.valueOf(v));
s.forEach(
// BUG: Diagnostic contains:
(v) -> {
String.valueOf(v);
});
s.forEach(
// BUG: Diagnostic contains:
(Integer v) -> {
{
String.valueOf(v);
}
});
s.forEach(
v -> {
String.valueOf(v);
String.valueOf(v);
});
s.map(String::valueOf);
// BUG: Diagnostic contains:
s.map(v -> String.valueOf(v));
// BUG: Diagnostic contains:
s.map((v) -> (String.valueOf(v)));
s.map(
// BUG: Diagnostic contains:
(Integer v) -> {
return String.valueOf(v);
});
s.map(
// BUG: Diagnostic contains:
(final Integer v) -> {
return (String.valueOf(v));
});
s.map(
v -> {
String.valueOf(v);
return String.valueOf(v);
});
s.findFirst().orElseGet(() -> Integer.valueOf("0"));
m.forEach((k, v) -> String.valueOf(v));
m.forEach((k, v) -> String.valueOf(k));
}
void binaryExternalInstanceFunctionCalls() {
m.forEach(m::put);
// BUG: Diagnostic contains:
m.forEach((k, v) -> m.put(k, v));
m.forEach((k, v) -> m.put(v, k));
m.forEach(
// BUG: Diagnostic contains:
(Integer k, Integer v) -> {
m.put(k, v);
});
m.forEach(
(k, v) -> {
m.put(k, k);
});
m.forEach(
// BUG: Diagnostic contains:
(final Integer k, final Integer v) -> {
{
m.put(k, v);
}
});
m.forEach(
(k, v) -> {
{
m.put(v, v);
}
});
m.forEach((k, v) -> new HashMap<Integer, Integer>().put(k, v));
m.forEach(
(k, v) -> {
m.put(k, v);
m.put(k, v);
});
Streams.zip(s, s, m::put);
// BUG: Diagnostic contains:
Streams.zip(s, s, (a, b) -> m.put(a, b));
Streams.zip(s, s, (a, b) -> m.put(b, a));
// BUG: Diagnostic contains:
Streams.zip(s, s, (Integer a, Integer b) -> (m.put(a, b)));
Streams.zip(s, s, (a, b) -> (m.put(a, a)));
Streams.zip(
s,
s,
// BUG: Diagnostic contains:
(final Integer a, final Integer b) -> {
return m.put(a, b);
});
Streams.zip(
s,
s,
(a, b) -> {
return m.put(b, b);
});
Streams.zip(
s,
s,
// BUG: Diagnostic contains:
(a, b) -> {
return (m.put(a, b));
});
Streams.zip(
s,
s,
(a, b) -> {
return (m.put(b, a));
});
Streams.zip(
s,
s,
(a, b) -> {
m.put(a, b);
return m.put(a, b);
});
}
void nullaryExternalInstanceFunctionCalls() {
s.map(Integer::doubleValue);
// BUG: Diagnostic contains:
s.map(i -> i.doubleValue());
s.map(i -> i.toString());
s.map(i -> s.toString());
// BUG: Diagnostic contains:
Stream.of(int.class).filter(c -> c.isEnum());
Stream.of((Class<?>) int.class).filter(Class::isEnum);
// BUG: Diagnostic contains:
Stream.of((Class<?>) int.class).filter(c -> c.isEnum());
}
void localFunctionCalls() {
s.forEach(v -> ivoid0());
s.forEach(v -> iint0());
s.forEach(v -> svoid0());
s.forEach(v -> sint0());
s.forEach(this::ivoid1);
// BUG: Diagnostic contains:
s.forEach(v -> ivoid1(v));
s.forEach(
// BUG: Diagnostic contains:
v -> {
ivoid1(v);
});
s.forEach(this::iint1);
// BUG: Diagnostic contains:
s.forEach(v -> iint1(v));
s.forEach(
// BUG: Diagnostic contains:
v -> {
iint1(v);
});
s.forEach(A::svoid1);
// BUG: Diagnostic contains:
s.forEach(v -> svoid1(v));
s.forEach(
// BUG: Diagnostic contains:
v -> {
svoid1(v);
});
s.forEach(A::sint1);
// BUG: Diagnostic contains:
s.forEach(v -> sint1(v));
s.forEach(
// BUG: Diagnostic contains:
v -> {
sint1(v);
});
s.forEach(v -> ivoid2(v, v));
s.forEach(v -> iint2(v, v));
s.forEach(v -> svoid2(v, v));
s.forEach(v -> sint2(v, v));
m.forEach((k, v) -> ivoid0());
m.forEach((k, v) -> iint0());
m.forEach((k, v) -> svoid0());
m.forEach((k, v) -> sint0());
m.forEach(this::ivoid2);
// BUG: Diagnostic contains:
m.forEach((k, v) -> ivoid2(k, v));
m.forEach(
// BUG: Diagnostic contains:
(k, v) -> {
ivoid2(k, v);
});
m.forEach(this::iint2);
// BUG: Diagnostic contains:
m.forEach((k, v) -> iint2(k, v));
m.forEach(
// BUG: Diagnostic contains:
(k, v) -> {
iint2(k, v);
});
m.forEach(A::svoid2);
// BUG: Diagnostic contains:
m.forEach((k, v) -> svoid2(k, v));
m.forEach(
// BUG: Diagnostic contains:
(k, v) -> {
svoid2(k, v);
});
m.forEach(A::sint2);
// BUG: Diagnostic contains:
m.forEach((k, v) -> sint2(k, v));
m.forEach(
// BUG: Diagnostic contains:
(k, v) -> {
sint2(k, v);
});
}
void functionCallsWhoseReplacementWouldBeAmbiguous() {
receiver(
i -> {
Integer.toString(i);
});
}
void assortedOtherEdgeCases() {
s.forEach(v -> String.valueOf(v.toString()));
TernaryOp o1 = (a, b, c) -> String.valueOf(a);
TernaryOp o2 = (a, b, c) -> String.valueOf(b);
TernaryOp o3 = (a, b, c) -> String.valueOf(c);
TernaryOp o4 = (a, b, c) -> c.concat(a);
TernaryOp o5 = (a, b, c) -> c.concat(b);
TernaryOp o6 = (a, b, c) -> a.concat(c);
TernaryOp o7 = (a, b, c) -> b.concat(c);
}
void receiver(IntFunction<?> op) {}
void receiver(IntConsumer op) {}
void ivoid0() {}
void ivoid1(int a) {}
void ivoid2(int a, int b) {}
int iint0() {
return 0;
}
int iint1(int a) {
return 0;
}
int iint2(int a, int b) {
return 0;
}
static void svoid0() {}
static void svoid1(int a) {}
static void svoid2(int a, int b) {}
static void svoid3(int a, int b, int c) {}
static int sint0() {
return 0;
}
static int sint1(int a) {
return 0;
}
static int sint2(int a, int b) {
return 0;
}
interface TernaryOp {
String collect(String a, String b, String c);
}
}
""")
.doTest();
}
@@ -321,111 +323,115 @@ final class MethodReferenceUsageTest {
BugCheckerRefactoringTestHelper.newInstance(MethodReferenceUsage.class, getClass())
.addInputLines(
"A.java",
"import static java.util.Collections.emptyList;",
"",
"import java.util.Collections;",
"import java.util.List;",
"import java.util.Map;",
"import java.util.function.IntSupplier;",
"import java.util.function.Supplier;",
"import java.util.stream.Stream;",
"",
"class A {",
" static class B extends A {",
" final A a = new B();",
" final B b = new B();",
"",
" IntSupplier intSup;",
" Supplier<List<?>> listSup;",
"",
" void m() {",
" intSup = () -> a.iint0();",
" intSup = () -> b.iint0();",
" intSup = () -> this.iint0();",
" intSup = () -> super.iint0();",
"",
" intSup = () -> a.sint0();",
" intSup = () -> b.sint0();",
" intSup = () -> this.sint0();",
" intSup = () -> super.sint0();",
" intSup = () -> A.sint0();",
" intSup = () -> B.sint0();",
"",
" listSup = () -> Collections.emptyList();",
" listSup = () -> emptyList();",
"",
" Stream.of((Class<?>) int.class).filter(c -> c.isEnum());",
" Stream.of((Map<?, ?>) null).map(Map::keySet).map(s -> s.size());",
" }",
"",
" @Override",
" int iint0() {",
" return 0;",
" }",
" }",
"",
" int iint0() {",
" return 0;",
" }",
"",
" static int sint0() {",
" return 0;",
" }",
"}")
"""
import static java.util.Collections.emptyList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
import java.util.stream.Stream;
class A {
static class B extends A {
final A a = new B();
final B b = new B();
IntSupplier intSup;
Supplier<List<?>> listSup;
void m() {
intSup = () -> a.iint0();
intSup = () -> b.iint0();
intSup = () -> this.iint0();
intSup = () -> super.iint0();
intSup = () -> a.sint0();
intSup = () -> b.sint0();
intSup = () -> this.sint0();
intSup = () -> super.sint0();
intSup = () -> A.sint0();
intSup = () -> B.sint0();
listSup = () -> Collections.emptyList();
listSup = () -> emptyList();
Stream.of((Class<?>) int.class).filter(c -> c.isEnum());
Stream.of((Map<?, ?>) null).map(Map::keySet).map(s -> s.size());
}
@Override
int iint0() {
return 0;
}
}
int iint0() {
return 0;
}
static int sint0() {
return 0;
}
}
""")
.addOutputLines(
"A.java",
"import static java.util.Collections.emptyList;",
"",
"import java.util.Collections;",
"import java.util.List;",
"import java.util.Map;",
"import java.util.Set;",
"import java.util.function.IntSupplier;",
"import java.util.function.Supplier;",
"import java.util.stream.Stream;",
"",
"class A {",
" static class B extends A {",
" final A a = new B();",
" final B b = new B();",
"",
" IntSupplier intSup;",
" Supplier<List<?>> listSup;",
"",
" void m() {",
" intSup = a::iint0;",
" intSup = b::iint0;",
" intSup = this::iint0;",
" intSup = super::iint0;",
"",
" intSup = () -> a.sint0();",
" intSup = () -> b.sint0();",
" intSup = () -> this.sint0();",
" intSup = () -> super.sint0();",
" intSup = A::sint0;",
" intSup = B::sint0;",
"",
" listSup = Collections::emptyList;",
" listSup = Collections::emptyList;",
"",
" Stream.of((Class<?>) int.class).filter(Class::isEnum);",
" Stream.of((Map<?, ?>) null).map(Map::keySet).map(Set::size);",
" }",
"",
" @Override",
" int iint0() {",
" return 0;",
" }",
" }",
"",
" int iint0() {",
" return 0;",
" }",
"",
" static int sint0() {",
" return 0;",
" }",
"}")
"""
import static java.util.Collections.emptyList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
import java.util.stream.Stream;
class A {
static class B extends A {
final A a = new B();
final B b = new B();
IntSupplier intSup;
Supplier<List<?>> listSup;
void m() {
intSup = a::iint0;
intSup = b::iint0;
intSup = this::iint0;
intSup = super::iint0;
intSup = () -> a.sint0();
intSup = () -> b.sint0();
intSup = () -> this.sint0();
intSup = () -> super.sint0();
intSup = A::sint0;
intSup = B::sint0;
listSup = Collections::emptyList;
listSup = Collections::emptyList;
Stream.of((Class<?>) int.class).filter(Class::isEnum);
Stream.of((Map<?, ?>) null).map(Map::keySet).map(Set::size);
}
@Override
int iint0() {
return 0;
}
}
int iint0() {
return 0;
}
static int sint0() {
return 0;
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,86 +11,88 @@ final class MockitoMockClassReferenceTest {
CompilationTestHelper.newInstance(MockitoMockClassReference.class, getClass())
.addSourceLines(
"A.java",
"import static org.mockito.Mockito.mock;",
"import static org.mockito.Mockito.spy;",
"import static org.mockito.Mockito.withSettings;",
"",
"import java.util.List;",
"import java.util.Objects;",
"import org.mockito.invocation.InvocationOnMock;",
"",
"class A {",
" {",
" Double d = Objects.requireNonNullElseGet(null, () -> mock(Double.class));",
" Double d2 =",
" Objects.requireNonNullElseGet(",
" null,",
" () -> {",
" return mock(Double.class);",
" });",
" }",
"",
" void m() {",
" Number variableMock = 42;",
" // BUG: Diagnostic contains:",
" variableMock = mock(Number.class);",
" // BUG: Diagnostic contains:",
" variableMock = mock(Number.class, \"name\");",
" // BUG: Diagnostic contains:",
" variableMock = mock(Number.class, InvocationOnMock::callRealMethod);",
" // BUG: Diagnostic contains:",
" variableMock = mock(Number.class, withSettings());",
" variableMock = mock(Integer.class);",
" variableMock = 42;",
" // BUG: Diagnostic contains:",
" List rawMock = mock(List.class);",
" // BUG: Diagnostic contains:",
" List<String> genericMock = mock(List.class);",
" var varMock = mock(Integer.class);",
" Class<? extends Number> numberType = Integer.class;",
" Number variableTypeMock = mock(numberType);",
" Object subtypeMock = mock(Integer.class);",
"",
" Number variableSpy = 42;",
" // BUG: Diagnostic contains:",
" variableSpy = spy(Number.class);",
" variableSpy = spy(Integer.class);",
" variableSpy = 42;",
" // BUG: Diagnostic contains:",
" List rawSpy = spy(List.class);",
" // BUG: Diagnostic contains:",
" List<String> genericSpy = spy(List.class);",
" var varSpy = spy(Integer.class);",
" Number variableTypeSpy = spy(numberType);",
" Object subtypeSpy = spy(Integer.class);",
" Object objectSpy = spy(new Object());",
"",
" Objects.hash(mock(Integer.class));",
" Integer i = mock(mock(Integer.class));",
" String s = new String(mock(String.class));",
" }",
"",
" Double getDoubleMock() {",
" return Objects.requireNonNullElseGet(",
" null,",
" () -> {",
" return mock(Double.class);",
" });",
" }",
"",
" Integer getIntegerMock() {",
" // BUG: Diagnostic contains:",
" return mock(Integer.class);",
" }",
"",
" <T> T getGenericMock(Class<T> clazz) {",
" return mock(clazz);",
" }",
"",
" Number getSubTypeMock() {",
" return mock(Integer.class);",
" }",
"}")
"""
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.withSettings;
import java.util.List;
import java.util.Objects;
import org.mockito.invocation.InvocationOnMock;
class A {
{
Double d = Objects.requireNonNullElseGet(null, () -> mock(Double.class));
Double d2 =
Objects.requireNonNullElseGet(
null,
() -> {
return mock(Double.class);
});
}
void m() {
Number variableMock = 42;
// BUG: Diagnostic contains:
variableMock = mock(Number.class);
// BUG: Diagnostic contains:
variableMock = mock(Number.class, "name");
// BUG: Diagnostic contains:
variableMock = mock(Number.class, InvocationOnMock::callRealMethod);
// BUG: Diagnostic contains:
variableMock = mock(Number.class, withSettings());
variableMock = mock(Integer.class);
variableMock = 42;
// BUG: Diagnostic contains:
List rawMock = mock(List.class);
// BUG: Diagnostic contains:
List<String> genericMock = mock(List.class);
var varMock = mock(Integer.class);
Class<? extends Number> numberType = Integer.class;
Number variableTypeMock = mock(numberType);
Object subtypeMock = mock(Integer.class);
Number variableSpy = 42;
// BUG: Diagnostic contains:
variableSpy = spy(Number.class);
variableSpy = spy(Integer.class);
variableSpy = 42;
// BUG: Diagnostic contains:
List rawSpy = spy(List.class);
// BUG: Diagnostic contains:
List<String> genericSpy = spy(List.class);
var varSpy = spy(Integer.class);
Number variableTypeSpy = spy(numberType);
Object subtypeSpy = spy(Integer.class);
Object objectSpy = spy(new Object());
Objects.hash(mock(Integer.class));
Integer i = mock(mock(Integer.class));
String s = new String(mock(String.class));
}
Double getDoubleMock() {
return Objects.requireNonNullElseGet(
null,
() -> {
return mock(Double.class);
});
}
Integer getIntegerMock() {
// BUG: Diagnostic contains:
return mock(Integer.class);
}
<T> T getGenericMock(Class<T> clazz) {
return mock(clazz);
}
Number getSubTypeMock() {
return mock(Integer.class);
}
}
""")
.doTest();
}
@@ -99,38 +101,42 @@ final class MockitoMockClassReferenceTest {
BugCheckerRefactoringTestHelper.newInstance(MockitoMockClassReference.class, getClass())
.addInputLines(
"A.java",
"import static org.mockito.Mockito.mock;",
"import static org.mockito.Mockito.spy;",
"import static org.mockito.Mockito.withSettings;",
"",
"import org.mockito.invocation.InvocationOnMock;",
"",
"class A {",
" void m() {",
" Number simpleMock = mock(Number.class);",
" Number namedMock = mock(Number.class, \"name\");",
" Number customAnswerMock = mock(Number.class, InvocationOnMock::callRealMethod);",
" Number customSettingsMock = mock(Number.class, withSettings());",
" Number simpleSpy = spy(Number.class);",
" }",
"}")
"""
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.withSettings;
import org.mockito.invocation.InvocationOnMock;
class A {
void m() {
Number simpleMock = mock(Number.class);
Number namedMock = mock(Number.class, "name");
Number customAnswerMock = mock(Number.class, InvocationOnMock::callRealMethod);
Number customSettingsMock = mock(Number.class, withSettings());
Number simpleSpy = spy(Number.class);
}
}
""")
.addOutputLines(
"A.java",
"import static org.mockito.Mockito.mock;",
"import static org.mockito.Mockito.spy;",
"import static org.mockito.Mockito.withSettings;",
"",
"import org.mockito.invocation.InvocationOnMock;",
"",
"class A {",
" void m() {",
" Number simpleMock = mock();",
" Number namedMock = mock(\"name\");",
" Number customAnswerMock = mock(InvocationOnMock::callRealMethod);",
" Number customSettingsMock = mock(withSettings());",
" Number simpleSpy = spy();",
" }",
"}")
"""
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.withSettings;
import org.mockito.invocation.InvocationOnMock;
class A {
void m() {
Number simpleMock = mock();
Number namedMock = mock("name");
Number customAnswerMock = mock(InvocationOnMock::callRealMethod);
Number customSettingsMock = mock(withSettings());
Number simpleSpy = spy();
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,40 +11,42 @@ final class MockitoStubbingTest {
CompilationTestHelper.newInstance(MockitoStubbing.class, getClass())
.addSourceLines(
"A.java",
"import static org.mockito.ArgumentMatchers.eq;",
"import static org.mockito.ArgumentMatchers.notNull;",
"import static org.mockito.Mockito.doAnswer;",
"import static org.mockito.Mockito.mock;",
"",
"import java.util.function.BiConsumer;",
"import java.util.function.Consumer;",
"import org.mockito.ArgumentMatchers;",
"",
"class A {",
" void m() {",
" Runnable runnable = mock(Runnable.class);",
" doAnswer(inv -> null).when(runnable).run();",
"",
" Consumer<String> consumer = mock(Consumer.class);",
" doAnswer(inv -> null).when(consumer).accept(\"foo\");",
" doAnswer(inv -> null).when(consumer).accept(notNull());",
" // BUG: Diagnostic contains:",
" doAnswer(inv -> null).when(consumer).accept(ArgumentMatchers.eq(\"foo\"));",
" // BUG: Diagnostic contains:",
" doAnswer(inv -> null).when(consumer).accept(eq(toString()));",
"",
" BiConsumer<Integer, String> biConsumer = mock(BiConsumer.class);",
" doAnswer(inv -> null).when(biConsumer).accept(0, \"foo\");",
" doAnswer(inv -> null).when(biConsumer).accept(eq(0), notNull());",
" doAnswer(inv -> null).when(biConsumer).accept(notNull(), eq(\"foo\"));",
" doAnswer(inv -> null)",
" .when(biConsumer)",
" // BUG: Diagnostic contains:",
" .accept(ArgumentMatchers.eq(0), ArgumentMatchers.eq(\"foo\"));",
" // BUG: Diagnostic contains:",
" doAnswer(inv -> null).when(biConsumer).accept(eq(hashCode()), eq(toString()));",
" }",
"}")
"""
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.mockito.ArgumentMatchers;
class A {
void m() {
Runnable runnable = mock(Runnable.class);
doAnswer(inv -> null).when(runnable).run();
Consumer<String> consumer = mock(Consumer.class);
doAnswer(inv -> null).when(consumer).accept("foo");
doAnswer(inv -> null).when(consumer).accept(notNull());
// BUG: Diagnostic contains:
doAnswer(inv -> null).when(consumer).accept(ArgumentMatchers.eq("foo"));
// BUG: Diagnostic contains:
doAnswer(inv -> null).when(consumer).accept(eq(toString()));
BiConsumer<Integer, String> biConsumer = mock(BiConsumer.class);
doAnswer(inv -> null).when(biConsumer).accept(0, "foo");
doAnswer(inv -> null).when(biConsumer).accept(eq(0), notNull());
doAnswer(inv -> null).when(biConsumer).accept(notNull(), eq("foo"));
doAnswer(inv -> null)
.when(biConsumer)
// BUG: Diagnostic contains:
.accept(ArgumentMatchers.eq(0), ArgumentMatchers.eq("foo"));
// BUG: Diagnostic contains:
doAnswer(inv -> null).when(biConsumer).accept(eq(hashCode()), eq(toString()));
}
}
""")
.doTest();
}
@@ -53,48 +55,52 @@ final class MockitoStubbingTest {
BugCheckerRefactoringTestHelper.newInstance(MockitoStubbing.class, getClass())
.addInputLines(
"A.java",
"import static org.mockito.ArgumentMatchers.eq;",
"import static org.mockito.Mockito.doAnswer;",
"import static org.mockito.Mockito.mock;",
"",
"import java.util.function.BiConsumer;",
"import java.util.function.Consumer;",
"import org.mockito.ArgumentMatchers;",
"",
"class A {",
" void m() {",
" Consumer<String> consumer = mock(Consumer.class);",
" doAnswer(inv -> null).when(consumer).accept(ArgumentMatchers.eq(\"foo\"));",
" doAnswer(inv -> null).when(consumer).accept(eq(toString()));",
"",
" BiConsumer<Integer, String> biConsumer = mock(BiConsumer.class);",
" doAnswer(inv -> null)",
" .when(biConsumer)",
" .accept(ArgumentMatchers.eq(0), ArgumentMatchers.eq(\"foo\"));",
" doAnswer(inv -> null).when(biConsumer).accept(eq(hashCode()), eq(toString()));",
" }",
"}")
"""
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.mockito.ArgumentMatchers;
class A {
void m() {
Consumer<String> consumer = mock(Consumer.class);
doAnswer(inv -> null).when(consumer).accept(ArgumentMatchers.eq("foo"));
doAnswer(inv -> null).when(consumer).accept(eq(toString()));
BiConsumer<Integer, String> biConsumer = mock(BiConsumer.class);
doAnswer(inv -> null)
.when(biConsumer)
.accept(ArgumentMatchers.eq(0), ArgumentMatchers.eq("foo"));
doAnswer(inv -> null).when(biConsumer).accept(eq(hashCode()), eq(toString()));
}
}
""")
.addOutputLines(
"A.java",
"import static org.mockito.ArgumentMatchers.eq;",
"import static org.mockito.Mockito.doAnswer;",
"import static org.mockito.Mockito.mock;",
"",
"import java.util.function.BiConsumer;",
"import java.util.function.Consumer;",
"import org.mockito.ArgumentMatchers;",
"",
"class A {",
" void m() {",
" Consumer<String> consumer = mock(Consumer.class);",
" doAnswer(inv -> null).when(consumer).accept(\"foo\");",
" doAnswer(inv -> null).when(consumer).accept(toString());",
"",
" BiConsumer<Integer, String> biConsumer = mock(BiConsumer.class);",
" doAnswer(inv -> null).when(biConsumer).accept(0, \"foo\");",
" doAnswer(inv -> null).when(biConsumer).accept(hashCode(), toString());",
" }",
"}")
"""
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.mockito.ArgumentMatchers;
class A {
void m() {
Consumer<String> consumer = mock(Consumer.class);
doAnswer(inv -> null).when(consumer).accept("foo");
doAnswer(inv -> null).when(consumer).accept(toString());
BiConsumer<Integer, String> biConsumer = mock(BiConsumer.class);
doAnswer(inv -> null).when(biConsumer).accept(0, "foo");
doAnswer(inv -> null).when(biConsumer).accept(hashCode(), toString());
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -9,18 +9,20 @@ final class MongoDBTextFilterUsageTest {
CompilationTestHelper.newInstance(MongoDBTextFilterUsage.class, getClass())
.addSourceLines(
"A.java",
"import com.mongodb.client.model.Filters;",
"import com.mongodb.client.model.TextSearchOptions;",
"",
"class A {",
" void m() {",
" Filters.eq(\"foo\", \"bar\");",
" // BUG: Diagnostic contains:",
" Filters.text(\"foo\");",
" // BUG: Diagnostic contains:",
" Filters.text(\"foo\", new TextSearchOptions());",
" }",
"}")
"""
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.TextSearchOptions;
class A {
void m() {
Filters.eq("foo", "bar");
// BUG: Diagnostic contains:
Filters.text("foo");
// BUG: Diagnostic contains:
Filters.text("foo", new TextSearchOptions());
}
}
""")
.doTest();
}
}

View File

@@ -9,31 +9,33 @@ final class NestedOptionalsTest {
CompilationTestHelper.newInstance(NestedOptionals.class, getClass())
.addSourceLines(
"A.java",
"import java.util.Optional;",
"import java.util.stream.Stream;",
"",
"class A {",
" void m() {",
" Optional.empty();",
" Optional.of(1);",
" // BUG: Diagnostic contains:",
" Optional.of(Optional.empty());",
" // BUG: Diagnostic contains:",
" Optional.of(Optional.of(1));",
"",
" Optional.ofNullable(null);",
" // BUG: Diagnostic contains:",
" Optional.ofNullable((Optional) null);",
"",
" Optional.of(\"foo\").map(String::length);",
" // BUG: Diagnostic contains:",
" Optional.of(\"foo\").map(Optional::of);",
"",
" Stream.of(\"foo\").findFirst();",
" // BUG: Diagnostic contains:",
" Stream.of(\"foo\").map(Optional::of).findFirst();",
" }",
"}")
"""
import java.util.Optional;
import java.util.stream.Stream;
class A {
void m() {
Optional.empty();
Optional.of(1);
// BUG: Diagnostic contains:
Optional.of(Optional.empty());
// BUG: Diagnostic contains:
Optional.of(Optional.of(1));
Optional.ofNullable(null);
// BUG: Diagnostic contains:
Optional.ofNullable((Optional) null);
Optional.of("foo").map(String::length);
// BUG: Diagnostic contains:
Optional.of("foo").map(Optional::of);
Stream.of("foo").findFirst();
// BUG: Diagnostic contains:
Stream.of("foo").map(Optional::of).findFirst();
}
}
""")
.doTest();
}
}

View File

@@ -9,29 +9,31 @@ final class NestedPublishersTest {
CompilationTestHelper.newInstance(NestedPublishers.class, getClass())
.addSourceLines(
"A.java",
"import org.reactivestreams.Publisher;",
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.GroupedFlux;",
"import reactor.core.publisher.Mono;",
"",
"class A {",
" void m() {",
" Mono.empty();",
" Flux.just(1);",
" Flux.just(1, 2).groupBy(i -> i).map(groupedFlux -> (GroupedFlux) groupedFlux);",
"",
" // BUG: Diagnostic contains:",
" Mono.just(Mono.empty());",
" // BUG: Diagnostic contains:",
" Flux.just(Flux.empty());",
" // BUG: Diagnostic contains:",
" Mono.just((Flux) Flux.just(1));",
" // BUG: Diagnostic contains:",
" Flux.just((Publisher) Mono.just(1));",
" // BUG: Diagnostic contains:",
" Mono.just(1).map(Mono::just);",
" }",
"}")
"""
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.GroupedFlux;
import reactor.core.publisher.Mono;
class A {
void m() {
Mono.empty();
Flux.just(1);
Flux.just(1, 2).groupBy(i -> i).map(groupedFlux -> (GroupedFlux) groupedFlux);
// BUG: Diagnostic contains:
Mono.just(Mono.empty());
// BUG: Diagnostic contains:
Flux.just(Flux.empty());
// BUG: Diagnostic contains:
Mono.just((Flux) Flux.just(1));
// BUG: Diagnostic contains:
Flux.just((Publisher) Mono.just(1));
// BUG: Diagnostic contains:
Mono.just(1).map(Mono::just);
}
}
""")
.doTest();
}
}

View File

@@ -11,95 +11,97 @@ final class NonEmptyMonoTest {
CompilationTestHelper.newInstance(NonEmptyMono.class, getClass())
.addSourceLines(
"A.java",
"import static com.google.common.collect.ImmutableList.toImmutableList;",
"import static java.util.function.Function.identity;",
"",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableMap;",
"import java.util.ArrayList;",
"import java.util.HashMap;",
"import java.util.List;",
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.Mono;",
"",
"class A {",
" void m() {",
" Mono.just(1).defaultIfEmpty(2);",
" Mono.just(1).single();",
" Mono.just(1).switchIfEmpty(Mono.just(2));",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).all(x -> true).defaultIfEmpty(true);",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).any(x -> true).single();",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).collect(toImmutableList()).switchIfEmpty(Mono.just(ImmutableList.of()));",
" // BUG: Diagnostic contains:",
" Flux.just(1).collect(ArrayList::new, List::add).defaultIfEmpty(new ArrayList<>());",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).collectList().single();",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).collectMap(identity()).switchIfEmpty(Mono.just(ImmutableMap.of()));",
" // BUG: Diagnostic contains:",
" Flux.just(1).collectMap(identity(), identity()).defaultIfEmpty(ImmutableMap.of());",
" // BUG: Diagnostic contains:",
" Flux.just(1).collectMap(identity(), identity(), HashMap::new).single();",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).collectMultimap(identity()).switchIfEmpty(Mono.just(ImmutableMap.of()));",
" // BUG: Diagnostic contains:",
" Flux.just(1).collectMultimap(identity(), identity()).defaultIfEmpty(ImmutableMap.of());",
" // BUG: Diagnostic contains:",
" Flux.just(1).collectMultimap(identity(), identity(), HashMap::new).single();",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).collectSortedList().defaultIfEmpty(ImmutableList.of());",
" // BUG: Diagnostic contains:",
" Flux.just(1).collectSortedList((o1, o2) -> 0).single();",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).count().switchIfEmpty(Mono.just(2L));",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).elementAt(0).defaultIfEmpty(1);",
" // BUG: Diagnostic contains:",
" Flux.just(1).elementAt(0, 2).single();",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).hasElement(2).switchIfEmpty(Mono.just(true));",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).hasElements().defaultIfEmpty(true);",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).last().single();",
" // BUG: Diagnostic contains:",
" Flux.just(1).last(2).switchIfEmpty(Mono.just(3));",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).reduceWith(() -> 0, Integer::sum).defaultIfEmpty(2);",
"",
" // BUG: Diagnostic contains:",
" Flux.just(1).single().single();",
" // BUG: Diagnostic contains:",
" Flux.just(1).single(2).switchIfEmpty(Mono.just(3));",
"",
" Flux.just(1).reduce(Integer::sum).defaultIfEmpty(2);",
" // BUG: Diagnostic contains:",
" Flux.just(1).reduce(2, Integer::sum).single();",
"",
" // BUG: Diagnostic contains:",
" Mono.just(1).defaultIfEmpty(1).switchIfEmpty(Mono.just(2));",
" // BUG: Diagnostic contains:",
" Mono.just(1).hasElement().defaultIfEmpty(true);",
" // BUG: Diagnostic contains:",
" Mono.just(1).single().single();",
" }",
"}")
"""
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.function.Function.identity;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
class A {
void m() {
Mono.just(1).defaultIfEmpty(2);
Mono.just(1).single();
Mono.just(1).switchIfEmpty(Mono.just(2));
// BUG: Diagnostic contains:
Flux.just(1).all(x -> true).defaultIfEmpty(true);
// BUG: Diagnostic contains:
Flux.just(1).any(x -> true).single();
// BUG: Diagnostic contains:
Flux.just(1).collect(toImmutableList()).switchIfEmpty(Mono.just(ImmutableList.of()));
// BUG: Diagnostic contains:
Flux.just(1).collect(ArrayList::new, List::add).defaultIfEmpty(new ArrayList<>());
// BUG: Diagnostic contains:
Flux.just(1).collectList().single();
// BUG: Diagnostic contains:
Flux.just(1).collectMap(identity()).switchIfEmpty(Mono.just(ImmutableMap.of()));
// BUG: Diagnostic contains:
Flux.just(1).collectMap(identity(), identity()).defaultIfEmpty(ImmutableMap.of());
// BUG: Diagnostic contains:
Flux.just(1).collectMap(identity(), identity(), HashMap::new).single();
// BUG: Diagnostic contains:
Flux.just(1).collectMultimap(identity()).switchIfEmpty(Mono.just(ImmutableMap.of()));
// BUG: Diagnostic contains:
Flux.just(1).collectMultimap(identity(), identity()).defaultIfEmpty(ImmutableMap.of());
// BUG: Diagnostic contains:
Flux.just(1).collectMultimap(identity(), identity(), HashMap::new).single();
// BUG: Diagnostic contains:
Flux.just(1).collectSortedList().defaultIfEmpty(ImmutableList.of());
// BUG: Diagnostic contains:
Flux.just(1).collectSortedList((o1, o2) -> 0).single();
// BUG: Diagnostic contains:
Flux.just(1).count().switchIfEmpty(Mono.just(2L));
// BUG: Diagnostic contains:
Flux.just(1).elementAt(0).defaultIfEmpty(1);
// BUG: Diagnostic contains:
Flux.just(1).elementAt(0, 2).single();
// BUG: Diagnostic contains:
Flux.just(1).hasElement(2).switchIfEmpty(Mono.just(true));
// BUG: Diagnostic contains:
Flux.just(1).hasElements().defaultIfEmpty(true);
// BUG: Diagnostic contains:
Flux.just(1).last().single();
// BUG: Diagnostic contains:
Flux.just(1).last(2).switchIfEmpty(Mono.just(3));
// BUG: Diagnostic contains:
Flux.just(1).reduceWith(() -> 0, Integer::sum).defaultIfEmpty(2);
// BUG: Diagnostic contains:
Flux.just(1).single().single();
// BUG: Diagnostic contains:
Flux.just(1).single(2).switchIfEmpty(Mono.just(3));
Flux.just(1).reduce(Integer::sum).defaultIfEmpty(2);
// BUG: Diagnostic contains:
Flux.just(1).reduce(2, Integer::sum).single();
// BUG: Diagnostic contains:
Mono.just(1).defaultIfEmpty(1).switchIfEmpty(Mono.just(2));
// BUG: Diagnostic contains:
Mono.just(1).hasElement().defaultIfEmpty(true);
// BUG: Diagnostic contains:
Mono.just(1).single().single();
}
}
""")
.doTest();
}
@@ -108,42 +110,46 @@ final class NonEmptyMonoTest {
BugCheckerRefactoringTestHelper.newInstance(NonEmptyMono.class, getClass())
.addInputLines(
"A.java",
"import static com.google.common.collect.ImmutableList.toImmutableList;",
"",
"import com.google.common.collect.ImmutableList;",
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.Mono;",
"",
"class A {",
" void m() {",
" Flux.just(1).collect(toImmutableList()).single();",
" Flux.just(1).collect(toImmutableList()).defaultIfEmpty(ImmutableList.of());",
" Flux.just(1).collect(toImmutableList()).switchIfEmpty(Mono.just(ImmutableList.of()));",
"",
" Mono.just(2).hasElement().single();",
" Mono.just(2).hasElement().defaultIfEmpty(true);",
" Mono.just(2).hasElement().switchIfEmpty(Mono.just(true));",
" }",
"}")
"""
import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
class A {
void m() {
Flux.just(1).collect(toImmutableList()).single();
Flux.just(1).collect(toImmutableList()).defaultIfEmpty(ImmutableList.of());
Flux.just(1).collect(toImmutableList()).switchIfEmpty(Mono.just(ImmutableList.of()));
Mono.just(2).hasElement().single();
Mono.just(2).hasElement().defaultIfEmpty(true);
Mono.just(2).hasElement().switchIfEmpty(Mono.just(true));
}
}
""")
.addOutputLines(
"A.java",
"import static com.google.common.collect.ImmutableList.toImmutableList;",
"",
"import com.google.common.collect.ImmutableList;",
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.Mono;",
"",
"class A {",
" void m() {",
" Flux.just(1).collect(toImmutableList());",
" Flux.just(1).collect(toImmutableList());",
" Flux.just(1).collect(toImmutableList());",
"",
" Mono.just(2).hasElement();",
" Mono.just(2).hasElement();",
" Mono.just(2).hasElement();",
" }",
"}")
"""
import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
class A {
void m() {
Flux.just(1).collect(toImmutableList());
Flux.just(1).collect(toImmutableList());
Flux.just(1).collect(toImmutableList());
Mono.just(2).hasElement();
Mono.just(2).hasElement();
Mono.just(2).hasElement();
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -40,71 +40,73 @@ final class NonStaticImportTest {
CompilationTestHelper.newInstance(NonStaticImport.class, getClass())
.addSourceLines(
"pkg/A.java",
"package pkg;",
"",
"// BUG: Diagnostic contains:",
"import static com.google.common.base.Strings.nullToEmpty;",
"// BUG: Diagnostic contains:",
"import static com.google.common.collect.ImmutableList.copyOf;",
"// BUG: Diagnostic contains:",
"import static java.lang.Integer.MAX_VALUE;",
"// BUG: Diagnostic contains:",
"import static java.lang.Integer.MIN_VALUE;",
"// BUG: Diagnostic contains:",
"import static java.time.Clock.systemUTC;",
"// BUG: Diagnostic contains:",
"import static java.time.Instant.MIN;",
"// BUG: Diagnostic contains:",
"import static java.time.ZoneOffset.SHORT_IDS;",
"import static java.time.ZoneOffset.UTC;",
"// BUG: Diagnostic contains:",
"import static java.util.Collections.min;",
"import static java.util.Locale.ENGLISH;",
"// BUG: Diagnostic contains:",
"import static java.util.Locale.ROOT;",
"// BUG: Diagnostic contains:",
"import static java.util.Optional.empty;",
"import static pkg.A.WithMethodThatIsSelectivelyFlagged.list;",
"",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import java.time.Instant;",
"import java.time.ZoneOffset;",
"import java.util.Locale;",
"import java.util.Map;",
"import pkg.A.Wrapper.ZERO;",
"",
"class A {",
" private Integer MIN_VALUE = 12;",
"",
" void m() {",
" nullToEmpty(null);",
" copyOf(ImmutableList.of());",
" int max = MAX_VALUE;",
" int min = MIN_VALUE;",
" systemUTC();",
" Instant minInstant = MIN;",
" Map<String, String> shortIds = SHORT_IDS;",
" ZoneOffset utc = UTC;",
" min(ImmutableSet.of());",
" Locale english = ENGLISH;",
" Locale root = ROOT;",
" empty();",
"",
" list();",
" new ZERO();",
" }",
"",
" static final class WithMethodThatIsSelectivelyFlagged {",
" static ImmutableList<String> list() {",
" return ImmutableList.of();",
" }",
" }",
"",
" static final class Wrapper {",
" static final class ZERO {}",
" }",
"}")
"""
package pkg;
// BUG: Diagnostic contains:
import static com.google.common.base.Strings.nullToEmpty;
// BUG: Diagnostic contains:
import static com.google.common.collect.ImmutableList.copyOf;
// BUG: Diagnostic contains:
import static java.lang.Integer.MAX_VALUE;
// BUG: Diagnostic contains:
import static java.lang.Integer.MIN_VALUE;
// BUG: Diagnostic contains:
import static java.time.Clock.systemUTC;
// BUG: Diagnostic contains:
import static java.time.Instant.MIN;
// BUG: Diagnostic contains:
import static java.time.ZoneOffset.SHORT_IDS;
import static java.time.ZoneOffset.UTC;
// BUG: Diagnostic contains:
import static java.util.Collections.min;
import static java.util.Locale.ENGLISH;
// BUG: Diagnostic contains:
import static java.util.Locale.ROOT;
// BUG: Diagnostic contains:
import static java.util.Optional.empty;
import static pkg.A.WithMethodThatIsSelectivelyFlagged.list;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.Locale;
import java.util.Map;
import pkg.A.Wrapper.ZERO;
class A {
private Integer MIN_VALUE = 12;
void m() {
nullToEmpty(null);
copyOf(ImmutableList.of());
int max = MAX_VALUE;
int min = MIN_VALUE;
systemUTC();
Instant minInstant = MIN;
Map<String, String> shortIds = SHORT_IDS;
ZoneOffset utc = UTC;
min(ImmutableSet.of());
Locale english = ENGLISH;
Locale root = ROOT;
empty();
list();
new ZERO();
}
static final class WithMethodThatIsSelectivelyFlagged {
static ImmutableList<String> list() {
return ImmutableList.of();
}
}
static final class Wrapper {
static final class ZERO {}
}
}
""")
.doTest();
}
@@ -113,84 +115,88 @@ final class NonStaticImportTest {
BugCheckerRefactoringTestHelper.newInstance(NonStaticImport.class, getClass())
.addInputLines(
"A.java",
"import static com.google.common.collect.ImmutableList.copyOf;",
"import static com.google.common.collect.ImmutableSet.of;",
"import static java.time.Clock.systemUTC;",
"import static java.time.Instant.MAX;",
"import static java.time.Instant.MIN;",
"import static java.util.Collections.min;",
"import static java.util.Locale.ROOT;",
"import static java.util.Optional.empty;",
"",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import java.time.Clock;",
"import java.time.Instant;",
"import java.util.Locale;",
"import java.util.Optional;",
"",
"class A {",
" void m() {",
" systemUTC();",
" Clock.systemUTC();",
"",
" Optional<Integer> o1 = empty();",
" Optional<Integer> o2 = Optional.empty();",
"",
" Object l1 = copyOf(ImmutableList.of());",
" Object l2 = ImmutableList.copyOf(ImmutableList.of());",
"",
" Locale lo1 = ROOT;",
" Locale lo2 = Locale.ROOT;",
"",
" Instant i1 = MIN;",
" Instant i2 = MAX;",
"",
" ImmutableSet.of(min(of()));",
" }",
"",
" private static final class WithCustomConstant {",
" private static final Instant MIN = Instant.EPOCH;",
" private static final Instant OTHER = MIN;",
" private static final Instant OTHER_MAX = MAX;",
" }",
"}")
"""
import static com.google.common.collect.ImmutableList.copyOf;
import static com.google.common.collect.ImmutableSet.of;
import static java.time.Clock.systemUTC;
import static java.time.Instant.MAX;
import static java.time.Instant.MIN;
import static java.util.Collections.min;
import static java.util.Locale.ROOT;
import static java.util.Optional.empty;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.time.Clock;
import java.time.Instant;
import java.util.Locale;
import java.util.Optional;
class A {
void m() {
systemUTC();
Clock.systemUTC();
Optional<Integer> o1 = empty();
Optional<Integer> o2 = Optional.empty();
Object l1 = copyOf(ImmutableList.of());
Object l2 = ImmutableList.copyOf(ImmutableList.of());
Locale lo1 = ROOT;
Locale lo2 = Locale.ROOT;
Instant i1 = MIN;
Instant i2 = MAX;
ImmutableSet.of(min(of()));
}
private static final class WithCustomConstant {
private static final Instant MIN = Instant.EPOCH;
private static final Instant OTHER = MIN;
private static final Instant OTHER_MAX = MAX;
}
}
""")
.addOutputLines(
"A.java",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import java.time.Clock;",
"import java.time.Instant;",
"import java.util.Collections;",
"import java.util.Locale;",
"import java.util.Optional;",
"",
"class A {",
" void m() {",
" Clock.systemUTC();",
" Clock.systemUTC();",
"",
" Optional<Integer> o1 = Optional.empty();",
" Optional<Integer> o2 = Optional.empty();",
"",
" Object l1 = ImmutableList.copyOf(ImmutableList.of());",
" Object l2 = ImmutableList.copyOf(ImmutableList.of());",
"",
" Locale lo1 = Locale.ROOT;",
" Locale lo2 = Locale.ROOT;",
"",
" Instant i1 = Instant.MIN;",
" Instant i2 = Instant.MAX;",
"",
" ImmutableSet.of(Collections.min(ImmutableSet.of()));",
" }",
"",
" private static final class WithCustomConstant {",
" private static final Instant MIN = Instant.EPOCH;",
" private static final Instant OTHER = MIN;",
" private static final Instant OTHER_MAX = Instant.MAX;",
" }",
"}")
"""
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.time.Clock;
import java.time.Instant;
import java.util.Collections;
import java.util.Locale;
import java.util.Optional;
class A {
void m() {
Clock.systemUTC();
Clock.systemUTC();
Optional<Integer> o1 = Optional.empty();
Optional<Integer> o2 = Optional.empty();
Object l1 = ImmutableList.copyOf(ImmutableList.of());
Object l2 = ImmutableList.copyOf(ImmutableList.of());
Locale lo1 = Locale.ROOT;
Locale lo2 = Locale.ROOT;
Instant i1 = Instant.MIN;
Instant i2 = Instant.MAX;
ImmutableSet.of(Collections.min(ImmutableSet.of()));
}
private static final class WithCustomConstant {
private static final Instant MIN = Instant.EPOCH;
private static final Instant OTHER = MIN;
private static final Instant OTHER_MAX = Instant.MAX;
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -15,21 +15,23 @@ final class RedundantStringConversionTest {
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"class A {",
" private final Object o = new Object();",
" private final String s = o.toString();",
"",
" String[] m() {",
" return new String[] {",
" o.toString(),",
" // BUG: Diagnostic contains:",
" s.toString(),",
" String.valueOf(o),",
" // BUG: Diagnostic contains:",
" String.valueOf(s),",
" };",
" }",
"}")
"""
class A {
private final Object o = new Object();
private final String s = o.toString();
String[] m() {
return new String[] {
o.toString(),
// BUG: Diagnostic contains:
s.toString(),
String.valueOf(o),
// BUG: Diagnostic contains:
String.valueOf(s),
};
}
}
""")
.doTest();
}
@@ -38,59 +40,61 @@ final class RedundantStringConversionTest {
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"import java.math.BigInteger;",
"import java.util.Objects;",
"",
"class A {",
" private final BigInteger i = BigInteger.ZERO;",
"",
" void m1() {",
" String s = i.toString();",
" // BUG: Diagnostic contains:",
" s += this.toString();",
" s += super.toString();",
" // BUG: Diagnostic contains:",
" s += i.toString();",
" s += i.toString(16);",
" // BUG: Diagnostic contains:",
" s += Objects.toString(i);",
" // BUG: Diagnostic contains:",
" s += Objects.toString(null);",
" // BUG: Diagnostic contains:",
" s += String.valueOf(i);",
" // BUG: Diagnostic contains:",
" s += String.valueOf(0);",
" // BUG: Diagnostic contains:",
" s += String.valueOf((String) null);",
" s += String.valueOf(null);",
" s += String.valueOf(new char[0]);",
" s += String.valueOf(new char[0], 0, 0);",
" // BUG: Diagnostic contains:",
" s += Boolean.toString(false);",
" // BUG: Diagnostic contains:",
" s += Byte.toString((byte) 0);",
" // BUG: Diagnostic contains:",
" s += Character.toString((char) 0);",
" // BUG: Diagnostic contains:",
" s += Short.toString((short) 0);",
" // BUG: Diagnostic contains:",
" s += Integer.toString(0);",
" // BUG: Diagnostic contains:",
" s += Long.toString(0);",
" // BUG: Diagnostic contains:",
" s += Float.toString((float) 0.0);",
" // BUG: Diagnostic contains:",
" s += Double.toString(0.0);",
" }",
"",
" void m2() {",
" int i = 0;",
" i += 1;",
" i -= 1;",
" i *= 1;",
" i /= 1;",
" }",
"}")
"""
import java.math.BigInteger;
import java.util.Objects;
class A {
private final BigInteger i = BigInteger.ZERO;
void m1() {
String s = i.toString();
// BUG: Diagnostic contains:
s += this.toString();
s += super.toString();
// BUG: Diagnostic contains:
s += i.toString();
s += i.toString(16);
// BUG: Diagnostic contains:
s += Objects.toString(i);
// BUG: Diagnostic contains:
s += Objects.toString(null);
// BUG: Diagnostic contains:
s += String.valueOf(i);
// BUG: Diagnostic contains:
s += String.valueOf(0);
// BUG: Diagnostic contains:
s += String.valueOf((String) null);
s += String.valueOf(null);
s += String.valueOf(new char[0]);
s += String.valueOf(new char[0], 0, 0);
// BUG: Diagnostic contains:
s += Boolean.toString(false);
// BUG: Diagnostic contains:
s += Byte.toString((byte) 0);
// BUG: Diagnostic contains:
s += Character.toString((char) 0);
// BUG: Diagnostic contains:
s += Short.toString((short) 0);
// BUG: Diagnostic contains:
s += Integer.toString(0);
// BUG: Diagnostic contains:
s += Long.toString(0);
// BUG: Diagnostic contains:
s += Float.toString((float) 0.0);
// BUG: Diagnostic contains:
s += Double.toString(0.0);
}
void m2() {
int i = 0;
i += 1;
i -= 1;
i *= 1;
i /= 1;
}
}
""")
.doTest();
}
@@ -99,93 +103,95 @@ final class RedundantStringConversionTest {
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"import java.math.BigInteger;",
"",
"class A {",
" private final BigInteger i = BigInteger.ZERO;",
" private final String s = i.toString();",
"",
" String[] m1() {",
" return new String[] {",
" // BUG: Diagnostic contains:",
" s + this.toString(),",
" s + super.toString(),",
" // BUG: Diagnostic contains:",
" s + i.toString(),",
" s + i.toString(16),",
" // BUG: Diagnostic contains:",
" s + String.valueOf(i),",
" // BUG: Diagnostic contains:",
" s + String.valueOf(0),",
" // BUG: Diagnostic contains:",
" s + String.valueOf((String) null),",
" s + String.valueOf(null),",
" s + String.valueOf(new char[0]),",
" s + String.valueOf(new char[0], 0, 0),",
" //",
" 42 + this.toString(),",
" 42 + super.toString(),",
" 42 + i.toString(),",
" 42 + i.toString(16),",
" 42 + String.valueOf(i),",
" // BUG: Diagnostic contains:",
" 42 + String.valueOf((String) null),",
" 42 + String.valueOf(null),",
" 42 + String.valueOf(new char[0]),",
" 42 + String.valueOf(new char[0], 0, 0),",
"",
" // BUG: Diagnostic contains:",
" this.toString() + s,",
" super.toString() + s,",
" // BUG: Diagnostic contains:",
" i.toString() + s,",
" i.toString(16) + s,",
" // BUG: Diagnostic contains:",
" String.valueOf(i) + s,",
" // BUG: Diagnostic contains:",
" String.valueOf(0) + s,",
" // BUG: Diagnostic contains:",
" String.valueOf((String) null) + s,",
" String.valueOf(null) + s,",
" String.valueOf(new char[0]) + s,",
" String.valueOf(new char[0], 0, 0) + s,",
" //",
" this.toString() + 42,",
" super.toString() + 42,",
" i.toString() + 42,",
" i.toString(16) + 42,",
" String.valueOf(i) + 42,",
" String.valueOf(0) + 42,",
" // BUG: Diagnostic contains:",
" String.valueOf((String) null) + 42,",
" String.valueOf(null) + 42,",
" String.valueOf(new char[0]) + 42,",
" String.valueOf(new char[0], 0, 0) + 42,",
"",
" // BUG: Diagnostic contains:",
" this.toString() + this.toString(),",
" super.toString() + super.toString(),",
" // BUG: Diagnostic contains:",
" i.toString() + i.toString(),",
" i.toString(16) + i.toString(16),",
" // BUG: Diagnostic contains:",
" String.valueOf(i) + String.valueOf(i),",
" // BUG: Diagnostic contains:",
" String.valueOf(0) + String.valueOf(0),",
" // BUG: Diagnostic contains:",
" String.valueOf((String) null) + String.valueOf((String) null),",
" String.valueOf(null) + String.valueOf(null),",
" String.valueOf(new char[0]) + String.valueOf(new char[0]),",
" String.valueOf(new char[0], 0, 0) + String.valueOf(new char[0], 0, 0),",
" };",
" }",
"",
" int[] m2() {",
" return new int[] {",
" 1 + 1, 1 - 1, 1 * 1, 1 / 1,",
" };",
" }",
"}")
"""
import java.math.BigInteger;
class A {
private final BigInteger i = BigInteger.ZERO;
private final String s = i.toString();
String[] m1() {
return new String[] {
// BUG: Diagnostic contains:
s + this.toString(),
s + super.toString(),
// BUG: Diagnostic contains:
s + i.toString(),
s + i.toString(16),
// BUG: Diagnostic contains:
s + String.valueOf(i),
// BUG: Diagnostic contains:
s + String.valueOf(0),
// BUG: Diagnostic contains:
s + String.valueOf((String) null),
s + String.valueOf(null),
s + String.valueOf(new char[0]),
s + String.valueOf(new char[0], 0, 0),
//
42 + this.toString(),
42 + super.toString(),
42 + i.toString(),
42 + i.toString(16),
42 + String.valueOf(i),
// BUG: Diagnostic contains:
42 + String.valueOf((String) null),
42 + String.valueOf(null),
42 + String.valueOf(new char[0]),
42 + String.valueOf(new char[0], 0, 0),
// BUG: Diagnostic contains:
this.toString() + s,
super.toString() + s,
// BUG: Diagnostic contains:
i.toString() + s,
i.toString(16) + s,
// BUG: Diagnostic contains:
String.valueOf(i) + s,
// BUG: Diagnostic contains:
String.valueOf(0) + s,
// BUG: Diagnostic contains:
String.valueOf((String) null) + s,
String.valueOf(null) + s,
String.valueOf(new char[0]) + s,
String.valueOf(new char[0], 0, 0) + s,
//
this.toString() + 42,
super.toString() + 42,
i.toString() + 42,
i.toString(16) + 42,
String.valueOf(i) + 42,
String.valueOf(0) + 42,
// BUG: Diagnostic contains:
String.valueOf((String) null) + 42,
String.valueOf(null) + 42,
String.valueOf(new char[0]) + 42,
String.valueOf(new char[0], 0, 0) + 42,
// BUG: Diagnostic contains:
this.toString() + this.toString(),
super.toString() + super.toString(),
// BUG: Diagnostic contains:
i.toString() + i.toString(),
i.toString(16) + i.toString(16),
// BUG: Diagnostic contains:
String.valueOf(i) + String.valueOf(i),
// BUG: Diagnostic contains:
String.valueOf(0) + String.valueOf(0),
// BUG: Diagnostic contains:
String.valueOf((String) null) + String.valueOf((String) null),
String.valueOf(null) + String.valueOf(null),
String.valueOf(new char[0]) + String.valueOf(new char[0]),
String.valueOf(new char[0], 0, 0) + String.valueOf(new char[0], 0, 0),
};
}
int[] m2() {
return new int[] {
1 + 1, 1 - 1, 1 * 1, 1 / 1,
};
}
}
""")
.doTest();
}
@@ -194,52 +200,54 @@ final class RedundantStringConversionTest {
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"import java.math.BigInteger;",
"",
"class A {",
" private final BigInteger i = BigInteger.ZERO;",
" private final String s = i.toString();",
"",
" void m() {",
" StringBuilder sb = new StringBuilder();",
"",
" sb.append(1);",
" sb.append(i);",
" // BUG: Diagnostic contains:",
" sb.append(i.toString());",
" sb.append(i.toString(16));",
" // BUG: Diagnostic contains:",
" sb.append(String.valueOf(i));",
" // BUG: Diagnostic contains:",
" sb.append(String.valueOf(0));",
" // BUG: Diagnostic contains:",
" sb.append(String.valueOf((String) null));",
" sb.append(String.valueOf(null));",
" sb.append(String.valueOf(new char[0]));",
" sb.append(String.valueOf(new char[0], 0, 0));",
" sb.append(s);",
" sb.append(\"constant\");",
"",
" sb.insert(0, 1);",
" sb.insert(0, i);",
" // BUG: Diagnostic contains:",
" sb.insert(0, i.toString());",
" sb.insert(0, i.toString(16));",
" // BUG: Diagnostic contains:",
" sb.insert(0, String.valueOf(i));",
" // BUG: Diagnostic contains:",
" sb.insert(0, String.valueOf(0));",
" // BUG: Diagnostic contains:",
" sb.insert(0, String.valueOf((String) null));",
" sb.insert(0, String.valueOf(null));",
" sb.insert(0, String.valueOf(new char[0]));",
" sb.insert(0, String.valueOf(new char[0], 0, 0));",
" sb.insert(0, s);",
" sb.insert(0, \"constant\");",
"",
" sb.replace(0, 1, i.toString());",
" }",
"}")
"""
import java.math.BigInteger;
class A {
private final BigInteger i = BigInteger.ZERO;
private final String s = i.toString();
void m() {
StringBuilder sb = new StringBuilder();
sb.append(1);
sb.append(i);
// BUG: Diagnostic contains:
sb.append(i.toString());
sb.append(i.toString(16));
// BUG: Diagnostic contains:
sb.append(String.valueOf(i));
// BUG: Diagnostic contains:
sb.append(String.valueOf(0));
// BUG: Diagnostic contains:
sb.append(String.valueOf((String) null));
sb.append(String.valueOf(null));
sb.append(String.valueOf(new char[0]));
sb.append(String.valueOf(new char[0], 0, 0));
sb.append(s);
sb.append("constant");
sb.insert(0, 1);
sb.insert(0, i);
// BUG: Diagnostic contains:
sb.insert(0, i.toString());
sb.insert(0, i.toString(16));
// BUG: Diagnostic contains:
sb.insert(0, String.valueOf(i));
// BUG: Diagnostic contains:
sb.insert(0, String.valueOf(0));
// BUG: Diagnostic contains:
sb.insert(0, String.valueOf((String) null));
sb.insert(0, String.valueOf(null));
sb.insert(0, String.valueOf(new char[0]));
sb.insert(0, String.valueOf(new char[0], 0, 0));
sb.insert(0, s);
sb.insert(0, "constant");
sb.replace(0, 1, i.toString());
}
}
""")
.doTest();
}
@@ -249,43 +257,45 @@ final class RedundantStringConversionTest {
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"import java.util.Formattable;",
"import java.util.Locale;",
"",
"class A {",
" private final Locale locale = Locale.ROOT;",
" private final Formattable f = (formatter, flags, width, precision) -> {};",
" private final Object o = new Object();",
" private final String s = o.toString();",
"",
" void m() {",
" String.format(s, f);",
" String.format(s, o);",
" String.format(s, s);",
" String.format(s, f.toString());",
" // BUG: Diagnostic contains:",
" String.format(s, o.toString());",
" // BUG: Diagnostic contains:",
" String.format(s, String.valueOf(o));",
"",
" String.format(locale, s, f);",
" String.format(locale, s, o);",
" String.format(locale, s, s);",
" String.format(locale, s, f.toString());",
" // BUG: Diagnostic contains:",
" String.format(locale, s, o.toString());",
" // BUG: Diagnostic contains:",
" String.format(locale, s, String.valueOf(o));",
"",
" String.format(o.toString(), o);",
" // BUG: Diagnostic contains:",
" String.format(s.toString(), o);",
" String.format(locale.toString(), s, o);",
" String.format(locale, o.toString(), o);",
" // BUG: Diagnostic contains:",
" String.format(locale, s.toString(), o);",
" }",
"}")
"""
import java.util.Formattable;
import java.util.Locale;
class A {
private final Locale locale = Locale.ROOT;
private final Formattable f = (formatter, flags, width, precision) -> {};
private final Object o = new Object();
private final String s = o.toString();
void m() {
String.format(s, f);
String.format(s, o);
String.format(s, s);
String.format(s, f.toString());
// BUG: Diagnostic contains:
String.format(s, o.toString());
// BUG: Diagnostic contains:
String.format(s, String.valueOf(o));
String.format(locale, s, f);
String.format(locale, s, o);
String.format(locale, s, s);
String.format(locale, s, f.toString());
// BUG: Diagnostic contains:
String.format(locale, s, o.toString());
// BUG: Diagnostic contains:
String.format(locale, s, String.valueOf(o));
String.format(o.toString(), o);
// BUG: Diagnostic contains:
String.format(s.toString(), o);
String.format(locale.toString(), s, o);
String.format(locale, o.toString(), o);
// BUG: Diagnostic contains:
String.format(locale, s.toString(), o);
}
}
""")
.doTest();
}
@@ -294,58 +304,60 @@ final class RedundantStringConversionTest {
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"import static com.google.common.base.Preconditions.checkArgument;",
"import static com.google.common.base.Preconditions.checkNotNull;",
"import static com.google.common.base.Preconditions.checkState;",
"import static com.google.common.base.Verify.verify;",
"import static com.google.common.base.Verify.verifyNotNull;",
"",
"import java.util.Formattable;",
"",
"class A {",
" private final Formattable f = (formatter, flags, width, precision) -> {};",
" private final Object o = new Object();",
" private final String s = o.toString();",
"",
" void m() {",
" checkState(true, s, f);",
" // BUG: Diagnostic contains:",
" checkState(true, s, f.toString());",
" checkState(true, f.toString(), f);",
" // BUG: Diagnostic contains:",
" checkState(true, s.toString(), f);",
"",
" checkArgument(true, s, f);",
" // BUG: Diagnostic contains:",
" checkArgument(true, s, f.toString());",
" checkArgument(true, f.toString(), f);",
" // BUG: Diagnostic contains:",
" checkArgument(true, s.toString(), f);",
"",
" checkNotNull(o, s, f);",
" // BUG: Diagnostic contains:",
" checkNotNull(o, s, f.toString());",
" checkNotNull(o, f.toString(), f);",
" // BUG: Diagnostic contains:",
" checkNotNull(o, s.toString(), f);",
" checkNotNull(o.toString(), s, f);",
"",
" verify(true, s, f);",
" // BUG: Diagnostic contains:",
" verify(true, s, f.toString());",
" verify(true, f.toString(), f);",
" // BUG: Diagnostic contains:",
" verify(true, s.toString(), f);",
"",
" verifyNotNull(o, s, f);",
" // BUG: Diagnostic contains:",
" verifyNotNull(o, s, f.toString());",
" verifyNotNull(o, f.toString(), f);",
" // BUG: Diagnostic contains:",
" verifyNotNull(o, s.toString(), f);",
" verifyNotNull(o.toString(), s, f);",
" }",
"}")
"""
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
import static com.google.common.base.Verify.verifyNotNull;
import java.util.Formattable;
class A {
private final Formattable f = (formatter, flags, width, precision) -> {};
private final Object o = new Object();
private final String s = o.toString();
void m() {
checkState(true, s, f);
// BUG: Diagnostic contains:
checkState(true, s, f.toString());
checkState(true, f.toString(), f);
// BUG: Diagnostic contains:
checkState(true, s.toString(), f);
checkArgument(true, s, f);
// BUG: Diagnostic contains:
checkArgument(true, s, f.toString());
checkArgument(true, f.toString(), f);
// BUG: Diagnostic contains:
checkArgument(true, s.toString(), f);
checkNotNull(o, s, f);
// BUG: Diagnostic contains:
checkNotNull(o, s, f.toString());
checkNotNull(o, f.toString(), f);
// BUG: Diagnostic contains:
checkNotNull(o, s.toString(), f);
checkNotNull(o.toString(), s, f);
verify(true, s, f);
// BUG: Diagnostic contains:
verify(true, s, f.toString());
verify(true, f.toString(), f);
// BUG: Diagnostic contains:
verify(true, s.toString(), f);
verifyNotNull(o, s, f);
// BUG: Diagnostic contains:
verifyNotNull(o, s, f.toString());
verifyNotNull(o, f.toString(), f);
// BUG: Diagnostic contains:
verifyNotNull(o, s.toString(), f);
verifyNotNull(o.toString(), s, f);
}
}
""")
.doTest();
}
@@ -354,53 +366,55 @@ final class RedundantStringConversionTest {
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addSourceLines(
"A.java",
"import java.util.Formattable;",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"import org.slf4j.Marker;",
"import org.slf4j.MarkerFactory;",
"",
"class A {",
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
"",
" private final Marker marker = MarkerFactory.getMarker(A.class.getName());",
" private final Formattable f = (formatter, flags, width, precision) -> {};",
" private final Object o = new Object();",
" private final String s = f.toString();",
" private final Throwable t = new Throwable();",
"",
" void m() {",
" LOG.trace(s, f);",
" // BUG: Diagnostic contains:",
" LOG.debug(s, f.toString());",
" LOG.info(s, t.toString());",
" LOG.warn(s, o, t.toString());",
" // BUG: Diagnostic contains:",
" LOG.error(s, o.toString(), t.toString());",
" // BUG: Diagnostic contains:",
" LOG.trace(s, t.toString(), o);",
"",
" LOG.trace(marker, s, f);",
" // BUG: Diagnostic contains:",
" LOG.debug(marker, s, f.toString());",
" LOG.info(marker, s, t.toString());",
" LOG.warn(marker, s, o, t.toString());",
" // BUG: Diagnostic contains:",
" LOG.error(marker, s, o.toString(), t.toString());",
" // BUG: Diagnostic contains:",
" LOG.trace(marker, s, t.toString(), o);",
"",
" LOG.trace(f.toString(), f);",
" // BUG: Diagnostic contains:",
" LOG.debug(s.toString(), f);",
" LOG.info(t.toString(), f);",
" LOG.warn(marker.toString(), s, f);",
" LOG.error(marker, o.toString(), f);",
" // BUG: Diagnostic contains:",
" LOG.trace(marker, s.toString(), f);",
" LOG.debug(marker, t.toString(), f);",
" }",
"}")
"""
import java.util.Formattable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
class A {
private static final Logger LOG = LoggerFactory.getLogger(A.class);
private final Marker marker = MarkerFactory.getMarker(A.class.getName());
private final Formattable f = (formatter, flags, width, precision) -> {};
private final Object o = new Object();
private final String s = f.toString();
private final Throwable t = new Throwable();
void m() {
LOG.trace(s, f);
// BUG: Diagnostic contains:
LOG.debug(s, f.toString());
LOG.info(s, t.toString());
LOG.warn(s, o, t.toString());
// BUG: Diagnostic contains:
LOG.error(s, o.toString(), t.toString());
// BUG: Diagnostic contains:
LOG.trace(s, t.toString(), o);
LOG.trace(marker, s, f);
// BUG: Diagnostic contains:
LOG.debug(marker, s, f.toString());
LOG.info(marker, s, t.toString());
LOG.warn(marker, s, o, t.toString());
// BUG: Diagnostic contains:
LOG.error(marker, s, o.toString(), t.toString());
// BUG: Diagnostic contains:
LOG.trace(marker, s, t.toString(), o);
LOG.trace(f.toString(), f);
// BUG: Diagnostic contains:
LOG.debug(s.toString(), f);
LOG.info(t.toString(), f);
LOG.warn(marker.toString(), s, f);
LOG.error(marker, o.toString(), f);
// BUG: Diagnostic contains:
LOG.trace(marker, s.toString(), f);
LOG.debug(marker, t.toString(), f);
}
}
""")
.doTest();
}
@@ -412,91 +426,93 @@ final class RedundantStringConversionTest {
"-XepOpt:RedundantStringConversion:ExtraConversionMethods=java.lang.Enum#name(),A#name(),A.B#toString(int)"))
.addSourceLines(
"A.java",
"import java.math.RoundingMode;",
"import java.util.Objects;",
"",
"class A {",
" static class B {",
" String name() {",
" return toString();",
" }",
"",
" static String toString(int i) {",
" return Integer.toString(i);",
" }",
"",
" static String toString(int i, int j) {",
" return Integer.toString(i * j);",
" }",
" }",
"",
" enum E {",
" ELEM;",
"",
" public String toString() {",
" return \"__\" + name() + \"__\";",
" }",
" }",
"",
" private final B b = new B();",
" private final String s = b.toString();",
"",
" String[] builtin() {",
" return new String[] {",
" // BUG: Diagnostic contains:",
" s + b.toString(),",
" // BUG: Diagnostic contains:",
" s + Objects.toString(b),",
" // BUG: Diagnostic contains:",
" s + String.valueOf(b),",
" // BUG: Diagnostic contains:",
" s + Boolean.toString(false),",
" // BUG: Diagnostic contains:",
" s + Byte.toString((byte) 0),",
" // BUG: Diagnostic contains:",
" s + Character.toString((char) 0),",
" // BUG: Diagnostic contains:",
" s + Short.toString((short) 0),",
" // BUG: Diagnostic contains:",
" s + Integer.toString(0),",
" s + Integer.toString(0, 16),",
" // BUG: Diagnostic contains:",
" s + Long.toString(0),",
" s + Long.toString(0, 16),",
" // BUG: Diagnostic contains:",
" s + Float.toString((float) 0.0),",
" // BUG: Diagnostic contains:",
" s + Double.toString(0.0),",
" };",
" }",
"",
" String[] custom() {",
" return new String[] {",
" s + b.name(),",
" // BUG: Diagnostic contains:",
" s + RoundingMode.UP.name(),",
" // BUG: Diagnostic contains:",
" s + mode().name(),",
" s + A.name(),",
" s + A.toString(42),",
" // BUG: Diagnostic contains:",
" s + B.toString(42),",
" s + B.toString(42, 42),",
" };",
" }",
"",
" static String name() {",
" return A.class.toString();",
" }",
"",
" RoundingMode mode() {",
" return RoundingMode.UP;",
" }",
"",
" static String toString(int i) {",
" return Integer.toString(i);",
" }",
"}")
"""
import java.math.RoundingMode;
import java.util.Objects;
class A {
static class B {
String name() {
return toString();
}
static String toString(int i) {
return Integer.toString(i);
}
static String toString(int i, int j) {
return Integer.toString(i * j);
}
}
enum E {
ELEM;
public String toString() {
return "__" + name() + "__";
}
}
private final B b = new B();
private final String s = b.toString();
String[] builtin() {
return new String[] {
// BUG: Diagnostic contains:
s + b.toString(),
// BUG: Diagnostic contains:
s + Objects.toString(b),
// BUG: Diagnostic contains:
s + String.valueOf(b),
// BUG: Diagnostic contains:
s + Boolean.toString(false),
// BUG: Diagnostic contains:
s + Byte.toString((byte) 0),
// BUG: Diagnostic contains:
s + Character.toString((char) 0),
// BUG: Diagnostic contains:
s + Short.toString((short) 0),
// BUG: Diagnostic contains:
s + Integer.toString(0),
s + Integer.toString(0, 16),
// BUG: Diagnostic contains:
s + Long.toString(0),
s + Long.toString(0, 16),
// BUG: Diagnostic contains:
s + Float.toString((float) 0.0),
// BUG: Diagnostic contains:
s + Double.toString(0.0),
};
}
String[] custom() {
return new String[] {
s + b.name(),
// BUG: Diagnostic contains:
s + RoundingMode.UP.name(),
// BUG: Diagnostic contains:
s + mode().name(),
s + A.name(),
s + A.toString(42),
// BUG: Diagnostic contains:
s + B.toString(42),
s + B.toString(42, 42),
};
}
static String name() {
return A.class.toString();
}
RoundingMode mode() {
return RoundingMode.UP;
}
static String toString(int i) {
return Integer.toString(i);
}
}
""")
.doTest();
}
@@ -505,32 +521,36 @@ final class RedundantStringConversionTest {
BugCheckerRefactoringTestHelper.newInstance(RedundantStringConversion.class, getClass())
.addInputLines(
"A.java",
"class A {",
" private final Object o = new Object();",
" private final String s = o.toString();",
"",
" void m() {",
" String v1 = s.toString();",
" String v2 = \"foo\".toString();",
" String v3 = v2 + super.toString();",
" String v4 = 42 + String.valueOf((String) null);",
" String.format(\"%s\", o.toString());",
" }",
"}")
"""
class A {
private final Object o = new Object();
private final String s = o.toString();
void m() {
String v1 = s.toString();
String v2 = "foo".toString();
String v3 = v2 + super.toString();
String v4 = 42 + String.valueOf((String) null);
String.format("%s", o.toString());
}
}
""")
.addOutputLines(
"A.java",
"class A {",
" private final Object o = new Object();",
" private final String s = o.toString();",
"",
" void m() {",
" String v1 = s;",
" String v2 = \"foo\";",
" String v3 = v2 + super.toString();",
" String v4 = 42 + (String) null;",
" String.format(\"%s\", o);",
" }",
"}")
"""
class A {
private final Object o = new Object();
private final String s = o.toString();
void m() {
String v1 = s;
String v2 = "foo";
String v3 = v2 + super.toString();
String v4 = 42 + (String) null;
String.format("%s", o);
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,23 +11,25 @@ final class RefasterAnyOfUsageTest {
CompilationTestHelper.newInstance(RefasterAnyOfUsage.class, getClass())
.addSourceLines(
"A.java",
"import com.google.errorprone.refaster.Refaster;",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"",
"class A {",
" @BeforeTemplate",
" String before(String str) {",
" // BUG: Diagnostic contains:",
" Refaster.anyOf();",
" // BUG: Diagnostic contains:",
" return Refaster.anyOf(str);",
" }",
"",
" @BeforeTemplate",
" Object before2(String str, Object obj) {",
" return Refaster.anyOf(str, obj);",
" }",
"}")
"""
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
class A {
@BeforeTemplate
String before(String str) {
// BUG: Diagnostic contains:
Refaster.anyOf();
// BUG: Diagnostic contains:
return Refaster.anyOf(str);
}
@BeforeTemplate
Object before2(String str, Object obj) {
return Refaster.anyOf(str, obj);
}
}
""")
.doTest();
}
@@ -36,28 +38,32 @@ final class RefasterAnyOfUsageTest {
BugCheckerRefactoringTestHelper.newInstance(RefasterAnyOfUsage.class, getClass())
.addInputLines(
"A.java",
"import com.google.errorprone.refaster.Refaster;",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"",
"class A {",
" @BeforeTemplate",
" String before(String str) {",
" Refaster.anyOf();",
" return Refaster.anyOf(str);",
" }",
"}")
"""
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
class A {
@BeforeTemplate
String before(String str) {
Refaster.anyOf();
return Refaster.anyOf(str);
}
}
""")
.addOutputLines(
"A.java",
"import com.google.errorprone.refaster.Refaster;",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"",
"class A {",
" @BeforeTemplate",
" String before(String str) {",
" Refaster.anyOf();",
" return str;",
" }",
"}")
"""
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
class A {
@BeforeTemplate
String before(String str) {
Refaster.anyOf();
return str;
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,121 +11,129 @@ final class RefasterRuleModifiersTest {
CompilationTestHelper.newInstance(RefasterRuleModifiers.class, getClass())
.addSourceLines(
"A.java",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"",
"final class A {",
" @BeforeTemplate",
" String before(String str) {",
" return str;",
" }",
"",
" String nonRefasterMethod(String str) {",
" return str;",
" }",
"",
" static final class Inner {",
" @BeforeTemplate",
" String before(String str) {",
" return str;",
" }",
" }",
"}")
"""
import com.google.errorprone.refaster.annotation.BeforeTemplate;
final class A {
@BeforeTemplate
String before(String str) {
return str;
}
String nonRefasterMethod(String str) {
return str;
}
static final class Inner {
@BeforeTemplate
String before(String str) {
return str;
}
}
}
""")
.addSourceLines(
"B.java",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"import com.google.errorprone.refaster.annotation.Placeholder;",
"",
"abstract class B<I, O> {",
" @Placeholder",
" abstract O someFunction(I input);",
"",
" @BeforeTemplate",
" String before(I input) {",
" return String.valueOf(someFunction(input));",
" }",
"",
" abstract static class Inner<I, O> {",
" @Placeholder",
" abstract O someFunction(I input);",
"",
" @BeforeTemplate",
" String before(I input) {",
" return String.valueOf(someFunction(input));",
" }",
" }",
"}")
"""
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.Placeholder;
abstract class B<I, O> {
@Placeholder
abstract O someFunction(I input);
@BeforeTemplate
String before(I input) {
return String.valueOf(someFunction(input));
}
abstract static class Inner<I, O> {
@Placeholder
abstract O someFunction(I input);
@BeforeTemplate
String before(I input) {
return String.valueOf(someFunction(input));
}
}
}
""")
.addSourceLines(
"C.java",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"",
"// BUG: Diagnostic contains:",
"class C {",
" @BeforeTemplate",
" // BUG: Diagnostic contains:",
" final String beforeFinal(String str) {",
" return str;",
" }",
"",
" @BeforeTemplate",
" // BUG: Diagnostic contains:",
" private String beforePrivate(String str) {",
" return str;",
" }",
"",
" @BeforeTemplate",
" // BUG: Diagnostic contains:",
" public String beforePublic(String str) {",
" return str;",
" }",
"",
" @BeforeTemplate",
" // BUG: Diagnostic contains:",
" static String beforeStatic(String str) {",
" return str;",
" }",
"",
" @BeforeTemplate",
" // BUG: Diagnostic contains:",
" synchronized String beforeSynchronized(String str) {",
" return str;",
" }",
"",
" // BUG: Diagnostic contains:",
" abstract static class AbstractInner {",
" @BeforeTemplate",
" String before(String str) {",
" return str;",
" }",
" }",
"",
" // BUG: Diagnostic contains:",
" static class NonFinalInner {",
" @BeforeTemplate",
" String before(String str) {",
" return str;",
" }",
" }",
"",
" // BUG: Diagnostic contains:",
" final class NonStaticInner {",
" @BeforeTemplate",
" String before(String str) {",
" return str;",
" }",
" }",
"}")
"""
import com.google.errorprone.refaster.annotation.BeforeTemplate;
// BUG: Diagnostic contains:
class C {
@BeforeTemplate
// BUG: Diagnostic contains:
final String beforeFinal(String str) {
return str;
}
@BeforeTemplate
// BUG: Diagnostic contains:
private String beforePrivate(String str) {
return str;
}
@BeforeTemplate
// BUG: Diagnostic contains:
public String beforePublic(String str) {
return str;
}
@BeforeTemplate
// BUG: Diagnostic contains:
static String beforeStatic(String str) {
return str;
}
@BeforeTemplate
// BUG: Diagnostic contains:
synchronized String beforeSynchronized(String str) {
return str;
}
// BUG: Diagnostic contains:
abstract static class AbstractInner {
@BeforeTemplate
String before(String str) {
return str;
}
}
// BUG: Diagnostic contains:
static class NonFinalInner {
@BeforeTemplate
String before(String str) {
return str;
}
}
// BUG: Diagnostic contains:
final class NonStaticInner {
@BeforeTemplate
String before(String str) {
return str;
}
}
}
""")
.addSourceLines(
"D.java",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"",
"// BUG: Diagnostic contains:",
"abstract class D {",
" @BeforeTemplate",
" // BUG: Diagnostic contains:",
" protected String beforeProtected(String str) {",
" return str;",
" }",
"}")
"""
import com.google.errorprone.refaster.annotation.BeforeTemplate;
// BUG: Diagnostic contains:
abstract class D {
@BeforeTemplate
// BUG: Diagnostic contains:
protected String beforeProtected(String str) {
return str;
}
}
""")
.doTest();
}
@@ -134,70 +142,78 @@ final class RefasterRuleModifiersTest {
BugCheckerRefactoringTestHelper.newInstance(RefasterRuleModifiers.class, getClass())
.addInputLines(
"A.java",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"",
"class A {",
" @BeforeTemplate",
" private static String before(String str) {",
" return str;",
" }",
"}")
"""
import com.google.errorprone.refaster.annotation.BeforeTemplate;
class A {
@BeforeTemplate
private static String before(String str) {
return str;
}
}
""")
.addOutputLines(
"A.java",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"",
"final class A {",
" @BeforeTemplate",
" String before(String str) {",
" return str;",
" }",
"}")
"""
import com.google.errorprone.refaster.annotation.BeforeTemplate;
final class A {
@BeforeTemplate
String before(String str) {
return str;
}
}
""")
.addInputLines(
"B.java",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"import com.google.errorprone.refaster.annotation.Placeholder;",
"",
"final class B {",
" abstract class WithoutPlaceholder {",
" @BeforeTemplate",
" protected synchronized String before(String str) {",
" return str;",
" }",
" }",
"",
" abstract class WithPlaceholder<I, O> {",
" @Placeholder",
" public abstract O someFunction(I input);",
"",
" @BeforeTemplate",
" public final String before(I input) {",
" return String.valueOf(someFunction(input));",
" }",
" }",
"}")
"""
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.Placeholder;
final class B {
abstract class WithoutPlaceholder {
@BeforeTemplate
protected synchronized String before(String str) {
return str;
}
}
abstract class WithPlaceholder<I, O> {
@Placeholder
public abstract O someFunction(I input);
@BeforeTemplate
public final String before(I input) {
return String.valueOf(someFunction(input));
}
}
}
""")
.addOutputLines(
"B.java",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"import com.google.errorprone.refaster.annotation.Placeholder;",
"",
"final class B {",
" static final class WithoutPlaceholder {",
" @BeforeTemplate",
" String before(String str) {",
" return str;",
" }",
" }",
"",
" abstract static class WithPlaceholder<I, O> {",
" @Placeholder",
" abstract O someFunction(I input);",
"",
" @BeforeTemplate",
" String before(I input) {",
" return String.valueOf(someFunction(input));",
" }",
" }",
"}")
"""
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.Placeholder;
final class B {
static final class WithoutPlaceholder {
@BeforeTemplate
String before(String str) {
return str;
}
}
abstract static class WithPlaceholder<I, O> {
@Placeholder
abstract O someFunction(I input);
@BeforeTemplate
String before(I input) {
return String.valueOf(someFunction(input));
}
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -9,133 +9,135 @@ 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 org.springframework.http.HttpMethod;",
"import org.springframework.ui.Model;",
"import org.springframework.validation.BindingResult;",
"import org.springframework.web.bind.annotation.DeleteMapping;",
"import org.springframework.web.bind.annotation.GetMapping;",
"import org.springframework.web.bind.annotation.PatchMapping;",
"import org.springframework.web.bind.annotation.PathVariable;",
"import org.springframework.web.bind.annotation.PostMapping;",
"import org.springframework.web.bind.annotation.PutMapping;",
"import org.springframework.web.bind.annotation.RequestAttribute;",
"import org.springframework.web.bind.annotation.RequestBody;",
"import org.springframework.web.bind.annotation.RequestHeader;",
"import org.springframework.web.bind.annotation.RequestMapping;",
"import org.springframework.web.bind.annotation.RequestParam;",
"import org.springframework.web.bind.annotation.RequestPart;",
"import org.springframework.web.context.request.NativeWebRequest;",
"import org.springframework.web.context.request.WebRequest;",
"import org.springframework.web.server.ServerWebExchange;",
"import org.springframework.web.util.UriBuilder;",
"import org.springframework.web.util.UriComponentsBuilder;",
"",
"interface A {",
" A noMapping();",
"",
" A noMapping(String param);",
"",
" @DeleteMapping",
" A properNoParameters();",
"",
" @GetMapping",
" A properPathVariable(@PathVariable String param);",
"",
" @PatchMapping",
" A properRequestAttribute(@RequestAttribute String attribute);",
"",
" @PostMapping",
" A properRequestBody(@RequestBody String body);",
"",
" @PutMapping",
" A properRequestHeader(@RequestHeader String header);",
"",
" @RequestMapping",
" A properRequestParam(@RequestParam String param);",
"",
" @RequestMapping",
" A properRequestPart(@RequestPart String part);",
"",
" @RequestMapping",
" A properInputStream(InputStream input);",
"",
" @RequestMapping",
" A properZoneId(ZoneId zoneId);",
"",
" @RequestMapping",
" A properLocale(Locale locale);",
"",
" @RequestMapping",
" A properTimeZone(TimeZone timeZone);",
"",
" @RequestMapping",
" A properHttpServletRequest(HttpServletRequest request);",
"",
" @RequestMapping",
" A properHttpServletResponse(HttpServletResponse response);",
"",
" @RequestMapping",
" A properHttpMethod(HttpMethod method);",
"",
" @RequestMapping",
" A properModel(Model model);",
"",
" @RequestMapping",
" A properBindingResult(BindingResult result);",
"",
" @RequestMapping",
" A properNativeWebRequest(NativeWebRequest request);",
"",
" @RequestMapping",
" A properWebRequest(WebRequest request);",
"",
" @RequestMapping",
" A properServerWebExchange(ServerWebExchange exchange);",
"",
" @RequestMapping",
" A properServerUriBuilder(UriBuilder builder);",
"",
" @RequestMapping",
" A properServerUriComponentsBuilder(UriComponentsBuilder builder);",
"",
" @DeleteMapping",
" // BUG: Diagnostic contains:",
" A delete(String param);",
"",
" @GetMapping",
" // BUG: Diagnostic contains:",
" A get(String param);",
"",
" @PatchMapping",
" // BUG: Diagnostic contains:",
" A patch(String param);",
"",
" @PostMapping",
" // BUG: Diagnostic contains:",
" A post(String param);",
"",
" @PutMapping",
" // BUG: Diagnostic contains:",
" A put(String param);",
"",
" @RequestMapping",
" // BUG: Diagnostic contains:",
" A requestMultiple(String param, String param2);",
"",
" @RequestMapping",
" // BUG: Diagnostic contains:",
" A requestFirstParamViolation(String param, @PathVariable String param2);",
"",
" @RequestMapping",
" // BUG: Diagnostic contains:",
" A requestSecondParamViolation(@RequestBody String param, String param2);",
"}")
"""
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 org.springframework.http.HttpMethod;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriBuilder;
import org.springframework.web.util.UriComponentsBuilder;
interface A {
A noMapping();
A noMapping(String param);
@DeleteMapping
A properNoParameters();
@GetMapping
A properPathVariable(@PathVariable String param);
@PatchMapping
A properRequestAttribute(@RequestAttribute String attribute);
@PostMapping
A properRequestBody(@RequestBody String body);
@PutMapping
A properRequestHeader(@RequestHeader String header);
@RequestMapping
A properRequestParam(@RequestParam String param);
@RequestMapping
A properRequestPart(@RequestPart String part);
@RequestMapping
A properInputStream(InputStream input);
@RequestMapping
A properZoneId(ZoneId zoneId);
@RequestMapping
A properLocale(Locale locale);
@RequestMapping
A properTimeZone(TimeZone timeZone);
@RequestMapping
A properHttpServletRequest(HttpServletRequest request);
@RequestMapping
A properHttpServletResponse(HttpServletResponse response);
@RequestMapping
A properHttpMethod(HttpMethod method);
@RequestMapping
A properModel(Model model);
@RequestMapping
A properBindingResult(BindingResult result);
@RequestMapping
A properNativeWebRequest(NativeWebRequest request);
@RequestMapping
A properWebRequest(WebRequest request);
@RequestMapping
A properServerWebExchange(ServerWebExchange exchange);
@RequestMapping
A properServerUriBuilder(UriBuilder builder);
@RequestMapping
A properServerUriComponentsBuilder(UriComponentsBuilder builder);
@DeleteMapping
// BUG: Diagnostic contains:
A delete(String param);
@GetMapping
// BUG: Diagnostic contains:
A get(String param);
@PatchMapping
// BUG: Diagnostic contains:
A patch(String param);
@PostMapping
// BUG: Diagnostic contains:
A post(String param);
@PutMapping
// BUG: Diagnostic contains:
A put(String param);
@RequestMapping
// BUG: Diagnostic contains:
A requestMultiple(String param, String param2);
@RequestMapping
// BUG: Diagnostic contains:
A requestFirstParamViolation(String param, @PathVariable String param2);
@RequestMapping
// BUG: Diagnostic contains:
A requestSecondParamViolation(@RequestBody String param, String param2);
}
""")
.doTest();
}
}

View File

@@ -9,55 +9,57 @@ final class RequestParamTypeTest {
CompilationTestHelper.newInstance(RequestParamType.class, getClass())
.addSourceLines(
"A.java",
"import com.google.common.collect.ImmutableBiMap;",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableMap;",
"import com.google.common.collect.ImmutableSet;",
"import java.util.List;",
"import java.util.Map;",
"import java.util.Set;",
"import org.jspecify.annotations.Nullable;",
"import org.springframework.web.bind.annotation.DeleteMapping;",
"import org.springframework.web.bind.annotation.GetMapping;",
"import org.springframework.web.bind.annotation.PostMapping;",
"import org.springframework.web.bind.annotation.PutMapping;",
"import org.springframework.web.bind.annotation.RequestBody;",
"import org.springframework.web.bind.annotation.RequestParam;",
"",
"interface A {",
" @PostMapping",
" A properRequestParam(@RequestBody String body);",
"",
" @GetMapping",
" A properRequestParam(@RequestParam int param);",
"",
" @GetMapping",
" A properRequestParam(@RequestParam List<String> param);",
"",
" @PostMapping",
" A properRequestParam(@RequestBody String body, @RequestParam Set<String> param);",
"",
" @PutMapping",
" A properRequestParam(@RequestBody String body, @RequestParam Map<String, String> param);",
"",
" @GetMapping",
" // BUG: Diagnostic contains:",
" A get(@RequestParam ImmutableBiMap<String, String> param);",
"",
" @PostMapping",
" // BUG: Diagnostic contains:",
" A post(@Nullable @RequestParam ImmutableList<String> param);",
"",
" @PutMapping",
" // BUG: Diagnostic contains:",
" A put(@RequestBody String body, @RequestParam ImmutableSet<String> param);",
"",
" @DeleteMapping",
" // BUG: Diagnostic contains:",
" A delete(@RequestBody String body, @RequestParam ImmutableMap<String, String> param);",
"",
" void negative(ImmutableSet<Integer> set, ImmutableMap<String, String> map);",
"}")
"""
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jspecify.annotations.Nullable;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
interface A {
@PostMapping
A properRequestParam(@RequestBody String body);
@GetMapping
A properRequestParam(@RequestParam int param);
@GetMapping
A properRequestParam(@RequestParam List<String> param);
@PostMapping
A properRequestParam(@RequestBody String body, @RequestParam Set<String> param);
@PutMapping
A properRequestParam(@RequestBody String body, @RequestParam Map<String, String> param);
@GetMapping
// BUG: Diagnostic contains:
A get(@RequestParam ImmutableBiMap<String, String> param);
@PostMapping
// BUG: Diagnostic contains:
A post(@Nullable @RequestParam ImmutableList<String> param);
@PutMapping
// BUG: Diagnostic contains:
A put(@RequestBody String body, @RequestParam ImmutableSet<String> param);
@DeleteMapping
// BUG: Diagnostic contains:
A delete(@RequestBody String body, @RequestParam ImmutableMap<String, String> param);
void negative(ImmutableSet<Integer> set, ImmutableMap<String, String> map);
}
""")
.doTest();
}
@@ -68,47 +70,49 @@ final class RequestParamTypeTest {
"-XepOpt:RequestParamType:SupportedCustomTypes=com.google.common.collect.ImmutableSet,com.google.common.collect.ImmutableSortedMultiset")
.addSourceLines(
"A.java",
"import com.google.common.collect.ImmutableBiMap;",
"import com.google.common.collect.ImmutableCollection;",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableMap;",
"import com.google.common.collect.ImmutableMultiset;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.common.collect.ImmutableSortedMultiset;",
"import com.google.common.collect.ImmutableSortedSet;",
"import org.springframework.web.bind.annotation.GetMapping;",
"import org.springframework.web.bind.annotation.RequestParam;",
"",
"interface A {",
" @GetMapping",
" // BUG: Diagnostic contains:",
" A immutableCollection(@RequestParam ImmutableCollection<String> param);",
"",
" @GetMapping",
" // BUG: Diagnostic contains:",
" A immutableList(@RequestParam ImmutableList<String> param);",
"",
" @GetMapping",
" A immutableSet(@RequestParam ImmutableSet<String> param);",
"",
" @GetMapping",
" A immutableSortedSet(@RequestParam ImmutableSortedSet<String> param);",
"",
" @GetMapping",
" // BUG: Diagnostic contains:",
" A immutableMultiset(@RequestParam ImmutableMultiset<String> param);",
"",
" @GetMapping",
" A immutableSortedMultiset(@RequestParam ImmutableSortedMultiset<String> param);",
"",
" @GetMapping",
" // BUG: Diagnostic contains:",
" A immutableMap(@RequestParam ImmutableMap<String, String> param);",
"",
" @GetMapping",
" // BUG: Diagnostic contains:",
" A immutableBiMap(@RequestParam ImmutableBiMap<String, String> param);",
"}")
"""
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMultiset;
import com.google.common.collect.ImmutableSortedSet;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
interface A {
@GetMapping
// BUG: Diagnostic contains:
A immutableCollection(@RequestParam ImmutableCollection<String> param);
@GetMapping
// BUG: Diagnostic contains:
A immutableList(@RequestParam ImmutableList<String> param);
@GetMapping
A immutableSet(@RequestParam ImmutableSet<String> param);
@GetMapping
A immutableSortedSet(@RequestParam ImmutableSortedSet<String> param);
@GetMapping
// BUG: Diagnostic contains:
A immutableMultiset(@RequestParam ImmutableMultiset<String> param);
@GetMapping
A immutableSortedMultiset(@RequestParam ImmutableSortedMultiset<String> param);
@GetMapping
// BUG: Diagnostic contains:
A immutableMap(@RequestParam ImmutableMap<String, String> param);
@GetMapping
// BUG: Diagnostic contains:
A immutableBiMap(@RequestParam ImmutableBiMap<String, String> param);
}
""")
.doTest();
}
}

View File

@@ -11,80 +11,82 @@ final class Slf4jLogStatementTest {
CompilationTestHelper.newInstance(Slf4jLogStatement.class, getClass())
.addSourceLines(
"A.java",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"import org.slf4j.Marker;",
"import org.slf4j.MarkerFactory;",
"",
"class A {",
" private static final String FMT0 = \"format-string-without-placeholders\";",
" private static final String FMT1 = \"format-string-with-{}-placeholder\";",
" private static final String FMT2 = \"format-string-with-{}-{}-placeholders\";",
" private static final String FMT_ERR = \"format-string-with-%s-placeholder\";",
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
"",
" private final Marker marker = MarkerFactory.getMarker(A.class.getName());",
" private final Object o = new Object();",
" private final String s = o.toString();",
" private final Throwable t = new Throwable();",
"",
" void m() {",
" LOG.trace(s);",
" LOG.debug(s, o);",
" LOG.info(s, t);",
" LOG.warn(s, o, t);",
" LOG.error(marker, s);",
" LOG.trace(marker, s, o);",
" LOG.debug(marker, s, t);",
" LOG.info(marker, s, o, t);",
"",
" LOG.warn(FMT0);",
" // BUG: Diagnostic contains: Log statement contains 0 placeholders, but specifies 1 matching",
" // argument(s)",
" LOG.error(FMT0, o);",
" LOG.trace(FMT0, t);",
" // BUG: Diagnostic contains:",
" LOG.debug(FMT0, o, t);",
" LOG.info(marker, FMT0);",
" // BUG: Diagnostic contains:",
" LOG.warn(marker, FMT0, o);",
" LOG.error(marker, FMT0, t);",
" // BUG: Diagnostic contains:",
" LOG.trace(marker, FMT0, o, t);",
"",
" // BUG: Diagnostic contains: Log statement contains 1 placeholders, but specifies 0 matching",
" // argument(s)",
" LOG.debug(FMT1);",
" LOG.info(FMT1, o);",
" // BUG: Diagnostic contains:",
" LOG.warn(FMT1, t);",
" LOG.error(FMT1, o, t);",
" // BUG: Diagnostic contains: Log statement contains 1 placeholders, but specifies 2 matching",
" // argument(s)",
" LOG.trace(FMT1, o, o);",
" // BUG: Diagnostic contains:",
" LOG.debug(FMT1, o, o, t);",
" // BUG: Diagnostic contains:",
" LOG.info(marker, FMT1);",
" LOG.warn(marker, FMT1, o);",
" // BUG: Diagnostic contains:",
" LOG.error(marker, FMT1, t);",
" LOG.trace(marker, FMT1, o, t);",
" // BUG: Diagnostic contains:",
" LOG.debug(marker, FMT1, o, o);",
" // BUG: Diagnostic contains:",
" LOG.info(marker, FMT1, o, o, t);",
"",
" // BUG: Diagnostic contains: SLF4J log statement placeholders are of the form `{}`, not `%s`",
" LOG.warn(FMT_ERR);",
" // BUG: Diagnostic contains:",
" LOG.error(FMT_ERR, t);",
" // BUG: Diagnostic contains:",
" LOG.trace(FMT_ERR, o);",
" // BUG: Diagnostic contains:",
" LOG.debug(FMT_ERR, o, t);",
" }",
"}")
"""
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
class A {
private static final String FMT0 = "format-string-without-placeholders";
private static final String FMT1 = "format-string-with-{}-placeholder";
private static final String FMT2 = "format-string-with-{}-{}-placeholders";
private static final String FMT_ERR = "format-string-with-%s-placeholder";
private static final Logger LOG = LoggerFactory.getLogger(A.class);
private final Marker marker = MarkerFactory.getMarker(A.class.getName());
private final Object o = new Object();
private final String s = o.toString();
private final Throwable t = new Throwable();
void m() {
LOG.trace(s);
LOG.debug(s, o);
LOG.info(s, t);
LOG.warn(s, o, t);
LOG.error(marker, s);
LOG.trace(marker, s, o);
LOG.debug(marker, s, t);
LOG.info(marker, s, o, t);
LOG.warn(FMT0);
// BUG: Diagnostic contains: Log statement contains 0 placeholders, but specifies 1 matching
// argument(s)
LOG.error(FMT0, o);
LOG.trace(FMT0, t);
// BUG: Diagnostic contains:
LOG.debug(FMT0, o, t);
LOG.info(marker, FMT0);
// BUG: Diagnostic contains:
LOG.warn(marker, FMT0, o);
LOG.error(marker, FMT0, t);
// BUG: Diagnostic contains:
LOG.trace(marker, FMT0, o, t);
// BUG: Diagnostic contains: Log statement contains 1 placeholders, but specifies 0 matching
// argument(s)
LOG.debug(FMT1);
LOG.info(FMT1, o);
// BUG: Diagnostic contains:
LOG.warn(FMT1, t);
LOG.error(FMT1, o, t);
// BUG: Diagnostic contains: Log statement contains 1 placeholders, but specifies 2 matching
// argument(s)
LOG.trace(FMT1, o, o);
// BUG: Diagnostic contains:
LOG.debug(FMT1, o, o, t);
// BUG: Diagnostic contains:
LOG.info(marker, FMT1);
LOG.warn(marker, FMT1, o);
// BUG: Diagnostic contains:
LOG.error(marker, FMT1, t);
LOG.trace(marker, FMT1, o, t);
// BUG: Diagnostic contains:
LOG.debug(marker, FMT1, o, o);
// BUG: Diagnostic contains:
LOG.info(marker, FMT1, o, o, t);
// BUG: Diagnostic contains: SLF4J log statement placeholders are of the form `{}`, not `%s`
LOG.warn(FMT_ERR);
// BUG: Diagnostic contains:
LOG.error(FMT_ERR, t);
// BUG: Diagnostic contains:
LOG.trace(FMT_ERR, o);
// BUG: Diagnostic contains:
LOG.debug(FMT_ERR, o, t);
}
}
""")
.doTest();
}
@@ -94,50 +96,54 @@ final class Slf4jLogStatementTest {
BugCheckerRefactoringTestHelper.newInstance(Slf4jLogStatement.class, getClass())
.addInputLines(
"A.java",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"import org.slf4j.Marker;",
"import org.slf4j.MarkerFactory;",
"",
"class A {",
" private static final String FMT_ERR = \"format-string-with-%s-placeholder\";",
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
"",
" private final Marker marker = MarkerFactory.getMarker(A.class.getName());",
" private final Object o = new Object();",
" private final String s = o.toString();",
" private final Throwable t = new Throwable();",
"",
" void m() {",
" LOG.error(FMT_ERR, o);",
" LOG.error(\"format-string-with-'%s'-placeholder\", o);",
" LOG.error(\"format-string-with-\\\"%s\\\"-placeholder\", o);",
" LOG.error(\"format-string-with-%s\" + \"-placeholder\", o);",
" }",
"}")
"""
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
class A {
private static final String FMT_ERR = "format-string-with-%s-placeholder";
private static final Logger LOG = LoggerFactory.getLogger(A.class);
private final Marker marker = MarkerFactory.getMarker(A.class.getName());
private final Object o = new Object();
private final String s = o.toString();
private final Throwable t = new Throwable();
void m() {
LOG.error(FMT_ERR, o);
LOG.error("format-string-with-'%s'-placeholder", o);
LOG.error("format-string-with-\\"%s\\"-placeholder", o);
LOG.error("format-string-with-%s" + "-placeholder", o);
}
}
""")
.addOutputLines(
"A.java",
"import org.slf4j.Logger;",
"import org.slf4j.LoggerFactory;",
"import org.slf4j.Marker;",
"import org.slf4j.MarkerFactory;",
"",
"class A {",
" private static final String FMT_ERR = \"format-string-with-%s-placeholder\";",
" private static final Logger LOG = LoggerFactory.getLogger(A.class);",
"",
" private final Marker marker = MarkerFactory.getMarker(A.class.getName());",
" private final Object o = new Object();",
" private final String s = o.toString();",
" private final Throwable t = new Throwable();",
"",
" void m() {",
" LOG.error(FMT_ERR, o);",
" LOG.error(\"format-string-with-'{}'-placeholder\", o);",
" LOG.error(\"format-string-with-\\\"{}\\\"-placeholder\", o);",
" LOG.error(\"format-string-with-{}\" + \"-placeholder\", o);",
" }",
"}")
"""
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
class A {
private static final String FMT_ERR = "format-string-with-%s-placeholder";
private static final Logger LOG = LoggerFactory.getLogger(A.class);
private final Marker marker = MarkerFactory.getMarker(A.class.getName());
private final Object o = new Object();
private final String s = o.toString();
private final Throwable t = new Throwable();
void m() {
LOG.error(FMT_ERR, o);
LOG.error("format-string-with-'{}'-placeholder", o);
LOG.error("format-string-with-\\"{}\\"-placeholder", o);
LOG.error("format-string-with-{}" + "-placeholder", o);
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -11,75 +11,77 @@ final class SpringMvcAnnotationTest {
CompilationTestHelper.newInstance(SpringMvcAnnotation.class, getClass())
.addSourceLines(
"A.java",
"import static org.springframework.web.bind.annotation.RequestMethod.DELETE;",
"import static org.springframework.web.bind.annotation.RequestMethod.GET;",
"import static org.springframework.web.bind.annotation.RequestMethod.HEAD;",
"import static org.springframework.web.bind.annotation.RequestMethod.PATCH;",
"import static org.springframework.web.bind.annotation.RequestMethod.POST;",
"import static org.springframework.web.bind.annotation.RequestMethod.PUT;",
"",
"import org.springframework.web.bind.annotation.DeleteMapping;",
"import org.springframework.web.bind.annotation.GetMapping;",
"import org.springframework.web.bind.annotation.PatchMapping;",
"import org.springframework.web.bind.annotation.PostMapping;",
"import org.springframework.web.bind.annotation.PutMapping;",
"import org.springframework.web.bind.annotation.RequestMapping;",
"import org.springframework.web.bind.annotation.RequestMethod;",
"",
"interface A {",
" @RequestMapping",
" A simple();",
"",
" @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();",
"",
" @RequestMapping(method = HEAD)",
" A head();",
"",
" @RequestMapping(method = RequestMethod.OPTIONS)",
" A options();",
"",
" @RequestMapping(method = {GET, POST})",
" A simpleMix();",
"",
" @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})",
" A verboseMix();",
"",
" @DeleteMapping",
" A properDelete();",
"",
" @GetMapping",
" A properGet();",
"",
" @PatchMapping",
" A properPatch();",
"",
" @PostMapping",
" A properPost();",
"",
" @PutMapping",
" A properPut();",
"}")
"""
import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.HEAD;
import static org.springframework.web.bind.annotation.RequestMethod.PATCH;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
interface A {
@RequestMapping
A simple();
@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();
@RequestMapping(method = HEAD)
A head();
@RequestMapping(method = RequestMethod.OPTIONS)
A options();
@RequestMapping(method = {GET, POST})
A simpleMix();
@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})
A verboseMix();
@DeleteMapping
A properDelete();
@GetMapping
A properGet();
@PatchMapping
A properPatch();
@PostMapping
A properPost();
@PutMapping
A properPut();
}
""")
.doTest();
}
@@ -88,66 +90,70 @@ final class SpringMvcAnnotationTest {
BugCheckerRefactoringTestHelper.newInstance(SpringMvcAnnotation.class, getClass())
.addInputLines(
"A.java",
"import static org.springframework.web.bind.annotation.RequestMethod.PATCH;",
"import static org.springframework.web.bind.annotation.RequestMethod.POST;",
"import static org.springframework.web.bind.annotation.RequestMethod.PUT;",
"",
"import org.springframework.web.bind.annotation.RequestMapping;",
"import org.springframework.web.bind.annotation.RequestMethod;",
"",
"interface A {",
" @RequestMapping(method = RequestMethod.GET)",
" A simple();",
"",
" @RequestMapping(path = \"/foo/bar\", method = POST)",
" A prefixed();",
"",
" @RequestMapping(",
" method = {RequestMethod.DELETE},",
" path = \"/foo/bar\")",
" A suffixed();",
"",
" @RequestMapping(",
" path = \"/foo/bar\",",
" method = {PUT},",
" consumes = {\"a\", \"b\"})",
" A surrounded();",
"",
" @RequestMapping(method = {PATCH})",
" A curly();",
"}")
"""
import static org.springframework.web.bind.annotation.RequestMethod.PATCH;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
interface A {
@RequestMapping(method = RequestMethod.GET)
A simple();
@RequestMapping(path = "/foo/bar", method = POST)
A prefixed();
@RequestMapping(
method = {RequestMethod.DELETE},
path = "/foo/bar")
A suffixed();
@RequestMapping(
path = "/foo/bar",
method = {PUT},
consumes = {"a", "b"})
A surrounded();
@RequestMapping(method = {PATCH})
A curly();
}
""")
.addOutputLines(
"A.java",
"import static org.springframework.web.bind.annotation.RequestMethod.PATCH;",
"import static org.springframework.web.bind.annotation.RequestMethod.POST;",
"import static org.springframework.web.bind.annotation.RequestMethod.PUT;",
"",
"import org.springframework.web.bind.annotation.DeleteMapping;",
"import org.springframework.web.bind.annotation.GetMapping;",
"import org.springframework.web.bind.annotation.PatchMapping;",
"import org.springframework.web.bind.annotation.PostMapping;",
"import org.springframework.web.bind.annotation.PutMapping;",
"import org.springframework.web.bind.annotation.RequestMapping;",
"import org.springframework.web.bind.annotation.RequestMethod;",
"",
"interface A {",
" @GetMapping()",
" A simple();",
"",
" @PostMapping(path = \"/foo/bar\")",
" A prefixed();",
"",
" @DeleteMapping(path = \"/foo/bar\")",
" A suffixed();",
"",
" @PutMapping(",
" path = \"/foo/bar\",",
" consumes = {\"a\", \"b\"})",
" A surrounded();",
"",
" @PatchMapping()",
" A curly();",
"}")
"""
import static org.springframework.web.bind.annotation.RequestMethod.PATCH;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
interface A {
@GetMapping()
A simple();
@PostMapping(path = "/foo/bar")
A prefixed();
@DeleteMapping(path = "/foo/bar")
A suffixed();
@PutMapping(
path = "/foo/bar",
consumes = {"a", "b"})
A surrounded();
@PatchMapping()
A curly();
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -34,83 +34,85 @@ final class StaticImportTest {
CompilationTestHelper.newInstance(StaticImport.class, getClass())
.addSourceLines(
"A.java",
"import static com.google.common.collect.ImmutableMap.toImmutableMap;",
"import static com.google.common.collect.ImmutableSet.toImmutableSet;",
"import static java.nio.charset.StandardCharsets.UTF_8;",
"import static java.util.function.Predicate.not;",
"import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;",
"",
"import com.google.common.base.Predicates;",
"import com.google.common.collect.ImmutableMap;",
"import com.google.common.collect.ImmutableMultiset;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.errorprone.refaster.ImportPolicy;",
"import com.google.errorprone.refaster.annotation.UseImportPolicy;",
"import java.nio.charset.StandardCharsets;",
"import java.time.ZoneOffset;",
"import java.util.Optional;",
"import java.util.UUID;",
"import java.util.function.Predicate;",
"import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;",
"import org.springframework.http.MediaType;",
"",
"class A {",
" void m() {",
" // BUG: Diagnostic contains:",
" ImmutableMap.toImmutableMap(v -> v, v -> v);",
" ImmutableMap.<String, String, String>toImmutableMap(v -> v, v -> v);",
" toImmutableMap(v -> v, v -> v);",
"",
" // BUG: Diagnostic contains:",
" ImmutableSet.toImmutableSet();",
" ImmutableSet.<String>toImmutableSet();",
" toImmutableSet();",
"",
" // Not flagged because we define `#toImmutableMultiset` below.",
" ImmutableMultiset.toImmutableMultiset();",
" ImmutableMultiset.<String>toImmutableMultiset();",
" toImmutableMultiset();",
"",
" // BUG: Diagnostic contains:",
" Predicate.not(null);",
" not(null);",
"",
" // BUG: Diagnostic contains:",
" Predicates.alwaysTrue();",
" // BUG: Diagnostic contains:",
" Predicates.alwaysFalse();",
" // Not flagged because of `java.util.function.Predicate.not` import.",
" Predicates.not(null);",
"",
" // BUG: Diagnostic contains:",
" UUID uuid = UUID.randomUUID();",
"",
" // BUG: Diagnostic contains:",
" Object o1 = StandardCharsets.UTF_8;",
" Object o2 = UTF_8;",
"",
" // BUG: Diagnostic contains:",
" Object e1 = WebEnvironment.RANDOM_PORT;",
" Object e2 = RANDOM_PORT;",
"",
" // Not flagged because `MediaType.ALL` is exempted.",
" MediaType t1 = MediaType.ALL;",
" // BUG: Diagnostic contains:",
" MediaType t2 = MediaType.APPLICATION_JSON;",
"",
" Optional.empty();",
"",
" // BUG: Diagnostic contains:",
" ZoneOffset zo1 = ZoneOffset.UTC;",
" ZoneOffset zo2 = ZoneOffset.MIN;",
" }",
"",
" // BUG: Diagnostic contains:",
" @UseImportPolicy(ImportPolicy.IMPORT_TOP_LEVEL)",
" void refasterAfterTemplate() {}",
"",
" void toImmutableMultiset() {}",
"}")
"""
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.function.Predicate.not;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.refaster.ImportPolicy;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.nio.charset.StandardCharsets;
import java.time.ZoneOffset;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Predicate;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.http.MediaType;
class A {
void m() {
// BUG: Diagnostic contains:
ImmutableMap.toImmutableMap(v -> v, v -> v);
ImmutableMap.<String, String, String>toImmutableMap(v -> v, v -> v);
toImmutableMap(v -> v, v -> v);
// BUG: Diagnostic contains:
ImmutableSet.toImmutableSet();
ImmutableSet.<String>toImmutableSet();
toImmutableSet();
// Not flagged because we define `#toImmutableMultiset` below.
ImmutableMultiset.toImmutableMultiset();
ImmutableMultiset.<String>toImmutableMultiset();
toImmutableMultiset();
// BUG: Diagnostic contains:
Predicate.not(null);
not(null);
// BUG: Diagnostic contains:
Predicates.alwaysTrue();
// BUG: Diagnostic contains:
Predicates.alwaysFalse();
// Not flagged because of `java.util.function.Predicate.not` import.
Predicates.not(null);
// BUG: Diagnostic contains:
UUID uuid = UUID.randomUUID();
// BUG: Diagnostic contains:
Object o1 = StandardCharsets.UTF_8;
Object o2 = UTF_8;
// BUG: Diagnostic contains:
Object e1 = WebEnvironment.RANDOM_PORT;
Object e2 = RANDOM_PORT;
// Not flagged because `MediaType.ALL` is exempted.
MediaType t1 = MediaType.ALL;
// BUG: Diagnostic contains:
MediaType t2 = MediaType.APPLICATION_JSON;
Optional.empty();
// BUG: Diagnostic contains:
ZoneOffset zo1 = ZoneOffset.UTC;
ZoneOffset zo2 = ZoneOffset.MIN;
}
// BUG: Diagnostic contains:
@UseImportPolicy(ImportPolicy.IMPORT_TOP_LEVEL)
void refasterAfterTemplate() {}
void toImmutableMultiset() {}
}
""")
.doTest();
}
@@ -119,154 +121,158 @@ final class StaticImportTest {
BugCheckerRefactoringTestHelper.newInstance(StaticImport.class, getClass())
.addInputLines(
"A.java",
"import static java.util.function.Predicate.not;",
"",
"import com.google.common.base.Predicates;",
"import com.google.common.collect.ImmutableMap;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.errorprone.BugPattern;",
"import com.google.errorprone.BugPattern.SeverityLevel;",
"import java.nio.charset.StandardCharsets;",
"import java.util.ArrayList;",
"import java.util.Collections;",
"import java.util.Objects;",
"import java.util.regex.Pattern;",
"import org.junit.jupiter.params.provider.Arguments;",
"import org.springframework.boot.test.context.SpringBootTest;",
"import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;",
"import org.springframework.format.annotation.DateTimeFormat;",
"import org.springframework.format.annotation.DateTimeFormat.ISO;",
"import org.springframework.http.MediaType;",
"",
"class A {",
" void m1() {",
" ImmutableMap.toImmutableMap(v -> v, v -> v);",
" ImmutableMap.<String, String, String>toImmutableMap(v -> v, v -> v);",
"",
" ImmutableSet.toImmutableSet();",
" ImmutableSet.<String>toImmutableSet();",
"",
" Collections.disjoint(ImmutableSet.of(), ImmutableSet.of());",
" Collections.reverse(new ArrayList<>());",
"",
" Predicates.not(null);",
" not(null);",
"",
" Arguments.arguments(\"foo\");",
"",
" Objects.requireNonNull(\"bar\");",
"",
" Object o = StandardCharsets.UTF_8;",
"",
" ImmutableSet.of(",
" MediaType.ALL,",
" MediaType.APPLICATION_XHTML_XML,",
" MediaType.TEXT_HTML,",
" MediaType.valueOf(\"image/webp\"));",
"",
" Pattern.compile(\"\", Pattern.CASE_INSENSITIVE);",
" }",
"",
" void m2(",
" @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) String date,",
" @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) String dateTime,",
" @DateTimeFormat(iso = DateTimeFormat.ISO.TIME) String time) {}",
"",
" void m3(",
" @DateTimeFormat(iso = ISO.DATE) String date,",
" @DateTimeFormat(iso = ISO.DATE_TIME) String dateTime,",
" @DateTimeFormat(iso = ISO.TIME) String time) {}",
"",
" @BugPattern(",
" summary = \"\",",
" linkType = BugPattern.LinkType.NONE,",
" severity = SeverityLevel.SUGGESTION,",
" tags = BugPattern.StandardTags.SIMPLIFICATION)",
" static final class TestBugPattern {}",
"",
" @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)",
" final class Test {}",
"}")
"""
import static java.util.function.Predicate.not;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.SeverityLevel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Objects;
import java.util.regex.Pattern;
import org.junit.jupiter.params.provider.Arguments;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.http.MediaType;
class A {
void m1() {
ImmutableMap.toImmutableMap(v -> v, v -> v);
ImmutableMap.<String, String, String>toImmutableMap(v -> v, v -> v);
ImmutableSet.toImmutableSet();
ImmutableSet.<String>toImmutableSet();
Collections.disjoint(ImmutableSet.of(), ImmutableSet.of());
Collections.reverse(new ArrayList<>());
Predicates.not(null);
not(null);
Arguments.arguments("foo");
Objects.requireNonNull("bar");
Object o = StandardCharsets.UTF_8;
ImmutableSet.of(
MediaType.ALL,
MediaType.APPLICATION_XHTML_XML,
MediaType.TEXT_HTML,
MediaType.valueOf("image/webp"));
Pattern.compile("", Pattern.CASE_INSENSITIVE);
}
void m2(
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) String date,
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) String dateTime,
@DateTimeFormat(iso = DateTimeFormat.ISO.TIME) String time) {}
void m3(
@DateTimeFormat(iso = ISO.DATE) String date,
@DateTimeFormat(iso = ISO.DATE_TIME) String dateTime,
@DateTimeFormat(iso = ISO.TIME) String time) {}
@BugPattern(
summary = "",
linkType = BugPattern.LinkType.NONE,
severity = SeverityLevel.SUGGESTION,
tags = BugPattern.StandardTags.SIMPLIFICATION)
static final class TestBugPattern {}
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
final class Test {}
}
""")
.addOutputLines(
"A.java",
"import static com.google.common.collect.ImmutableMap.toImmutableMap;",
"import static com.google.common.collect.ImmutableSet.toImmutableSet;",
"import static com.google.errorprone.BugPattern.LinkType.NONE;",
"import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;",
"import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;",
"import static java.nio.charset.StandardCharsets.UTF_8;",
"import static java.util.Collections.disjoint;",
"import static java.util.Collections.reverse;",
"import static java.util.Objects.requireNonNull;",
"import static java.util.function.Predicate.not;",
"import static java.util.regex.Pattern.CASE_INSENSITIVE;",
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
"import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;",
"import static org.springframework.format.annotation.DateTimeFormat.ISO.DATE;",
"import static org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIME;",
"import static org.springframework.format.annotation.DateTimeFormat.ISO.TIME;",
"import static org.springframework.http.MediaType.APPLICATION_XHTML_XML;",
"import static org.springframework.http.MediaType.TEXT_HTML;",
"",
"import com.google.common.base.Predicates;",
"import com.google.common.collect.ImmutableMap;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.errorprone.BugPattern;",
"import com.google.errorprone.BugPattern.SeverityLevel;",
"import java.nio.charset.StandardCharsets;",
"import java.util.ArrayList;",
"import java.util.Collections;",
"import java.util.Objects;",
"import java.util.regex.Pattern;",
"import org.junit.jupiter.params.provider.Arguments;",
"import org.springframework.boot.test.context.SpringBootTest;",
"import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;",
"import org.springframework.format.annotation.DateTimeFormat;",
"import org.springframework.format.annotation.DateTimeFormat.ISO;",
"import org.springframework.http.MediaType;",
"",
"class A {",
" void m1() {",
" toImmutableMap(v -> v, v -> v);",
" ImmutableMap.<String, String, String>toImmutableMap(v -> v, v -> v);",
"",
" toImmutableSet();",
" ImmutableSet.<String>toImmutableSet();",
"",
" disjoint(ImmutableSet.of(), ImmutableSet.of());",
" reverse(new ArrayList<>());",
"",
" Predicates.not(null);",
" not(null);",
"",
" arguments(\"foo\");",
"",
" requireNonNull(\"bar\");",
"",
" Object o = UTF_8;",
"",
" ImmutableSet.of(",
" MediaType.ALL, APPLICATION_XHTML_XML, TEXT_HTML, MediaType.valueOf(\"image/webp\"));",
"",
" Pattern.compile(\"\", CASE_INSENSITIVE);",
" }",
"",
" void m2(",
" @DateTimeFormat(iso = DATE) String date,",
" @DateTimeFormat(iso = DATE_TIME) String dateTime,",
" @DateTimeFormat(iso = TIME) String time) {}",
"",
" void m3(",
" @DateTimeFormat(iso = DATE) String date,",
" @DateTimeFormat(iso = DATE_TIME) String dateTime,",
" @DateTimeFormat(iso = TIME) String time) {}",
"",
" @BugPattern(summary = \"\", linkType = NONE, severity = SUGGESTION, tags = SIMPLIFICATION)",
" static final class TestBugPattern {}",
"",
" @SpringBootTest(webEnvironment = RANDOM_PORT)",
" final class Test {}",
"}")
"""
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.disjoint;
import static java.util.Collections.reverse;
import static java.util.Objects.requireNonNull;
import static java.util.function.Predicate.not;
import static java.util.regex.Pattern.CASE_INSENSITIVE;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
import static org.springframework.format.annotation.DateTimeFormat.ISO.DATE;
import static org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIME;
import static org.springframework.format.annotation.DateTimeFormat.ISO.TIME;
import static org.springframework.http.MediaType.APPLICATION_XHTML_XML;
import static org.springframework.http.MediaType.TEXT_HTML;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.SeverityLevel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Objects;
import java.util.regex.Pattern;
import org.junit.jupiter.params.provider.Arguments;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.http.MediaType;
class A {
void m1() {
toImmutableMap(v -> v, v -> v);
ImmutableMap.<String, String, String>toImmutableMap(v -> v, v -> v);
toImmutableSet();
ImmutableSet.<String>toImmutableSet();
disjoint(ImmutableSet.of(), ImmutableSet.of());
reverse(new ArrayList<>());
Predicates.not(null);
not(null);
arguments("foo");
requireNonNull("bar");
Object o = UTF_8;
ImmutableSet.of(
MediaType.ALL, APPLICATION_XHTML_XML, TEXT_HTML, MediaType.valueOf("image/webp"));
Pattern.compile("", CASE_INSENSITIVE);
}
void m2(
@DateTimeFormat(iso = DATE) String date,
@DateTimeFormat(iso = DATE_TIME) String dateTime,
@DateTimeFormat(iso = TIME) String time) {}
void m3(
@DateTimeFormat(iso = DATE) String date,
@DateTimeFormat(iso = DATE_TIME) String dateTime,
@DateTimeFormat(iso = TIME) String time) {}
@BugPattern(summary = "", linkType = NONE, severity = SUGGESTION, tags = SIMPLIFICATION)
static final class TestBugPattern {}
@SpringBootTest(webEnvironment = RANDOM_PORT)
final class Test {}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -14,43 +14,45 @@ final class StringJoinTest {
.expectErrorMessage("join", m -> m.contains("Prefer `String#join` over `String#format`"))
.addSourceLines(
"A.java",
"import java.util.Formattable;",
"import java.util.Locale;",
"",
"class A {",
" void m() {",
" String.join(\"-\", getClass().getName());",
" String.format(getClass().getName(), getClass().getName());",
" String.format(Locale.ROOT, \"%s\", getClass().getName());",
" String.format(\"%20s\", getClass().getName());",
" // BUG: Diagnostic matches: valueOf",
" String.format(\"%s\", getClass().getName());",
" // BUG: Diagnostic matches: valueOf",
" String.format(\"%s\", hashCode());",
" String.format(\"%s\", (Formattable) null);",
" String.format(\"-%s\", getClass().getName());",
" String.format(\"%s-\", getClass().getName());",
" String.format(\"-%s-\", getClass().getName());",
" // BUG: Diagnostic matches: join",
" String.format(\"%s%s\", getClass().getName(), getClass().getName());",
" // BUG: Diagnostic matches: join",
" String.format(\"%s%s\", getClass().getName(), hashCode());",
" // BUG: Diagnostic matches: join",
" String.format(\"%s%s\", hashCode(), getClass().getName());",
" String.format(\"%s%s\", getClass().getName(), (Formattable) null);",
" String.format(\"%s%s\", (Formattable) null, getClass().getName());",
" String.format(\"%s%s\", getClass().getName());",
" // BUG: Diagnostic matches: join",
" String.format(\"%s-%s\", getClass().getName(), getClass().getName());",
" // BUG: Diagnostic matches: join",
" String.format(\"%saa%s\", getClass().getName(), getClass().getName());",
" String.format(\"%s%%%s\", getClass().getName(), getClass().getName());",
" // BUG: Diagnostic matches: join",
" String.format(\"%s_%s_%s\", getClass().getName(), getClass().getName(), getClass().getName());",
" String.format(\"%s_%s_%s\", getClass().getName(), getClass().getName());",
" String.format(\"%s_%s-%s\", getClass().getName(), getClass().getName(), getClass().getName());",
" }",
"}")
"""
import java.util.Formattable;
import java.util.Locale;
class A {
void m() {
String.join("-", getClass().getName());
String.format(getClass().getName(), getClass().getName());
String.format(Locale.ROOT, "%s", getClass().getName());
String.format("%20s", getClass().getName());
// BUG: Diagnostic matches: valueOf
String.format("%s", getClass().getName());
// BUG: Diagnostic matches: valueOf
String.format("%s", hashCode());
String.format("%s", (Formattable) null);
String.format("-%s", getClass().getName());
String.format("%s-", getClass().getName());
String.format("-%s-", getClass().getName());
// BUG: Diagnostic matches: join
String.format("%s%s", getClass().getName(), getClass().getName());
// BUG: Diagnostic matches: join
String.format("%s%s", getClass().getName(), hashCode());
// BUG: Diagnostic matches: join
String.format("%s%s", hashCode(), getClass().getName());
String.format("%s%s", getClass().getName(), (Formattable) null);
String.format("%s%s", (Formattable) null, getClass().getName());
String.format("%s%s", getClass().getName());
// BUG: Diagnostic matches: join
String.format("%s-%s", getClass().getName(), getClass().getName());
// BUG: Diagnostic matches: join
String.format("%saa%s", getClass().getName(), getClass().getName());
String.format("%s%%%s", getClass().getName(), getClass().getName());
// BUG: Diagnostic matches: join
String.format("%s_%s_%s", getClass().getName(), getClass().getName(), getClass().getName());
String.format("%s_%s_%s", getClass().getName(), getClass().getName());
String.format("%s_%s-%s", getClass().getName(), getClass().getName(), getClass().getName());
}
}
""")
.doTest();
}
@@ -59,32 +61,36 @@ final class StringJoinTest {
BugCheckerRefactoringTestHelper.newInstance(StringJoin.class, getClass())
.addInputLines(
"A.java",
"class A {",
" void m() {",
" String.format(\"%s\", getClass().getName());",
" String.format(\"%s%s\", getClass().getName(), getClass().getName());",
" String.format(\"%s%s\", getClass().getName(), hashCode());",
" String.format(\"%s%s\", hashCode(), getClass().getName());",
" String.format(\"%s-%s\", getClass().getName(), getClass().getName());",
" String.format(\"%saa%s\", getClass().getName(), getClass().getName());",
" String.format(\"%s\\\"%s\", getClass().getName(), getClass().getName());",
" String.format(\"%s_%s_%s\", getClass().getName(), getClass().getName(), getClass().getName());",
" }",
"}")
"""
class A {
void m() {
String.format("%s", getClass().getName());
String.format("%s%s", getClass().getName(), getClass().getName());
String.format("%s%s", getClass().getName(), hashCode());
String.format("%s%s", hashCode(), getClass().getName());
String.format("%s-%s", getClass().getName(), getClass().getName());
String.format("%saa%s", getClass().getName(), getClass().getName());
String.format("%s\\"%s", getClass().getName(), getClass().getName());
String.format("%s_%s_%s", getClass().getName(), getClass().getName(), getClass().getName());
}
}
""")
.addOutputLines(
"A.java",
"class A {",
" void m() {",
" String.valueOf(getClass().getName());",
" String.join(\"\", getClass().getName(), getClass().getName());",
" String.join(\"\", getClass().getName(), String.valueOf(hashCode()));",
" String.join(\"\", String.valueOf(hashCode()), getClass().getName());",
" String.join(\"-\", getClass().getName(), getClass().getName());",
" String.join(\"aa\", getClass().getName(), getClass().getName());",
" String.join(\"\\\"\", getClass().getName(), getClass().getName());",
" String.join(\"_\", getClass().getName(), getClass().getName(), getClass().getName());",
" }",
"}")
"""
class A {
void m() {
String.valueOf(getClass().getName());
String.join("", getClass().getName(), getClass().getName());
String.join("", getClass().getName(), String.valueOf(hashCode()));
String.join("", String.valueOf(hashCode()), getClass().getName());
String.join("-", getClass().getName(), getClass().getName());
String.join("aa", getClass().getName(), getClass().getName());
String.join("\\"", getClass().getName(), getClass().getName());
String.join("_", getClass().getName(), getClass().getName(), getClass().getName());
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -9,110 +9,112 @@ final class TimeZoneUsageTest {
CompilationTestHelper.newInstance(TimeZoneUsage.class, getClass())
.addSourceLines(
"A.java",
"import static java.time.ZoneOffset.UTC;",
"",
"import java.time.Clock;",
"import java.time.Duration;",
"import java.time.Instant;",
"import java.time.LocalDate;",
"import java.time.LocalDateTime;",
"import java.time.LocalTime;",
"import java.time.OffsetDateTime;",
"import java.time.OffsetTime;",
"import java.time.ZoneId;",
"import java.time.ZonedDateTime;",
"",
"class A {",
" void m() {",
" Clock clock = Clock.fixed(Instant.EPOCH, UTC);",
" clock.instant();",
" clock.millis();",
" Clock.offset(clock, Duration.ZERO);",
" Clock.tick(clock, Duration.ZERO);",
"",
" // BUG: Diagnostic contains:",
" Clock.systemUTC();",
" // BUG: Diagnostic contains:",
" Clock.systemDefaultZone();",
" // BUG: Diagnostic contains:",
" Clock.system(UTC);",
" // BUG: Diagnostic contains:",
" Clock.tickMillis(UTC);",
" // BUG: Diagnostic contains:",
" Clock.tickMinutes(UTC);",
" // BUG: Diagnostic contains:",
" Clock.tickSeconds(UTC);",
" // BUG: Diagnostic contains:",
" clock.getZone();",
" // BUG: Diagnostic contains:",
" clock.withZone(UTC);",
"",
" // BUG: Diagnostic contains:",
" Instant.now();",
" // This is equivalent to `clock.instant()`, which is fine.",
" Instant.now(clock);",
"",
" // BUG: Diagnostic contains:",
" LocalDate.now();",
" // BUG: Diagnostic contains:",
" LocalDate.now(clock);",
" // BUG: Diagnostic contains:",
" LocalDate.now(UTC);",
"",
" // BUG: Diagnostic contains:",
" LocalDateTime.now();",
" // BUG: Diagnostic contains:",
" LocalDateTime.now(clock);",
" // BUG: Diagnostic contains:",
" LocalDateTime.now(UTC);",
"",
" // BUG: Diagnostic contains:",
" LocalTime.now();",
" // BUG: Diagnostic contains:",
" LocalTime.now(clock);",
" // BUG: Diagnostic contains:",
" LocalTime.now(UTC);",
"",
" // BUG: Diagnostic contains:",
" OffsetDateTime.now();",
" // BUG: Diagnostic contains:",
" OffsetDateTime.now(clock);",
" // BUG: Diagnostic contains:",
" OffsetDateTime.now(UTC);",
"",
" // BUG: Diagnostic contains:",
" OffsetTime.now();",
" // BUG: Diagnostic contains:",
" OffsetTime.now(clock);",
" // BUG: Diagnostic contains:",
" OffsetTime.now(UTC);",
"",
" // BUG: Diagnostic contains:",
" ZonedDateTime.now();",
" // BUG: Diagnostic contains:",
" ZonedDateTime.now(clock);",
" // BUG: Diagnostic contains:",
" ZonedDateTime.now(UTC);",
" }",
"",
" abstract class ForwardingClock extends Clock {",
" private final Clock clock;",
"",
" ForwardingClock(Clock clock) {",
" this.clock = clock;",
" }",
"",
" @Override",
" public ZoneId getZone() {",
" return clock.getZone();",
" }",
"",
" @Override",
" public Clock withZone(ZoneId zone) {",
" return clock.withZone(zone);",
" }",
" }",
"}")
"""
import static java.time.ZoneOffset.UTC;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
class A {
void m() {
Clock clock = Clock.fixed(Instant.EPOCH, UTC);
clock.instant();
clock.millis();
Clock.offset(clock, Duration.ZERO);
Clock.tick(clock, Duration.ZERO);
// BUG: Diagnostic contains:
Clock.systemUTC();
// BUG: Diagnostic contains:
Clock.systemDefaultZone();
// BUG: Diagnostic contains:
Clock.system(UTC);
// BUG: Diagnostic contains:
Clock.tickMillis(UTC);
// BUG: Diagnostic contains:
Clock.tickMinutes(UTC);
// BUG: Diagnostic contains:
Clock.tickSeconds(UTC);
// BUG: Diagnostic contains:
clock.getZone();
// BUG: Diagnostic contains:
clock.withZone(UTC);
// BUG: Diagnostic contains:
Instant.now();
// This is equivalent to `clock.instant()`, which is fine.
Instant.now(clock);
// BUG: Diagnostic contains:
LocalDate.now();
// BUG: Diagnostic contains:
LocalDate.now(clock);
// BUG: Diagnostic contains:
LocalDate.now(UTC);
// BUG: Diagnostic contains:
LocalDateTime.now();
// BUG: Diagnostic contains:
LocalDateTime.now(clock);
// BUG: Diagnostic contains:
LocalDateTime.now(UTC);
// BUG: Diagnostic contains:
LocalTime.now();
// BUG: Diagnostic contains:
LocalTime.now(clock);
// BUG: Diagnostic contains:
LocalTime.now(UTC);
// BUG: Diagnostic contains:
OffsetDateTime.now();
// BUG: Diagnostic contains:
OffsetDateTime.now(clock);
// BUG: Diagnostic contains:
OffsetDateTime.now(UTC);
// BUG: Diagnostic contains:
OffsetTime.now();
// BUG: Diagnostic contains:
OffsetTime.now(clock);
// BUG: Diagnostic contains:
OffsetTime.now(UTC);
// BUG: Diagnostic contains:
ZonedDateTime.now();
// BUG: Diagnostic contains:
ZonedDateTime.now(clock);
// BUG: Diagnostic contains:
ZonedDateTime.now(UTC);
}
abstract class ForwardingClock extends Clock {
private final Clock clock;
ForwardingClock(Clock clock) {
this.clock = clock;
}
@Override
public ZoneId getZone() {
return clock.getZone();
}
@Override
public Clock withZone(ZoneId zone) {
return clock.withZone(zone);
}
}
}
""")
.doTest();
}
}

View File

@@ -19,38 +19,40 @@ final class ConflictDetectionTest {
CompilationTestHelper.newInstance(RenameBlockerFlagger.class, getClass())
.addSourceLines(
"pkg/A.java",
"package pkg;",
"",
"import static pkg.A.B.method3t;",
"",
"import pkg.A.method4t;",
"",
"class A {",
" void method1() {",
" method3t();",
" method4(method4t.class);",
" }",
"",
" // BUG: Diagnostic contains: a method named `method2t` is already defined in this class or a",
" // supertype",
" void method2() {}",
"",
" void method2t() {}",
"",
" // BUG: Diagnostic contains: `method3t` is already statically imported",
" void method3() {}",
"",
" void method4(Object o) {}",
"",
" // BUG: Diagnostic contains: `int` is not a valid identifier",
" void in() {}",
"",
" static class B {",
" static void method3t() {}",
" }",
"",
" class method4t {}",
"}")
"""
package pkg;
import static pkg.A.B.method3t;
import pkg.A.method4t;
class A {
void method1() {
method3t();
method4(method4t.class);
}
// BUG: Diagnostic contains: a method named `method2t` is already defined in this class or a
// supertype
void method2() {}
void method2t() {}
// BUG: Diagnostic contains: `method3t` is already statically imported
void method3() {}
void method4(Object o) {}
// BUG: Diagnostic contains: `int` is not a valid identifier
void in() {}
static class B {
static void method3t() {}
}
class method4t {}
}
""")
.doTest();
}

View File

@@ -51,153 +51,165 @@ final class MethodMatcherFactoryTest {
CompilationTestHelper.newInstance(MatchedMethodsFlagger.class, getClass())
.addSourceLines(
"com/example/A.java",
"package com.example;",
"",
"public class A {",
" public void m1() {}",
"",
" public void m1(String s) {}",
"",
" public void m1(int i, int j) {}",
"",
" public void m2() {}",
"",
" public void m2(String s) {}",
"",
" public void m2(int i, int j) {}",
"",
" public void m3() {}",
"",
" public void m3(String s) {}",
"",
" public void m3(int i, int j) {}",
"}")
"""
package com.example;
public class A {
public void m1() {}
public void m1(String s) {}
public void m1(int i, int j) {}
public void m2() {}
public void m2(String s) {}
public void m2(int i, int j) {}
public void m3() {}
public void m3(String s) {}
public void m3(int i, int j) {}
}
""")
.addSourceLines(
"com/example/B.java",
"package com.example;",
"",
"public class B {",
" public void m1() {}",
"",
" public void m1(String s) {}",
"",
" public void m1(int i, int j) {}",
"",
" public void m2() {}",
"",
" public void m2(String s) {}",
"",
" public void m2(int i, int j) {}",
"",
" public void m3() {}",
"",
" public void m3(String s) {}",
"",
" public void m3(int i, int j) {}",
"}")
"""
package com.example;
public class B {
public void m1() {}
public void m1(String s) {}
public void m1(int i, int j) {}
public void m2() {}
public void m2(String s) {}
public void m2(int i, int j) {}
public void m3() {}
public void m3(String s) {}
public void m3(int i, int j) {}
}
""")
.addSourceLines(
"com/example/sub/A.java",
"package com.example.sub;",
"",
"public class A {",
" public static void m1() {}",
"",
" public static void m1(String s) {}",
"",
" public static void m1(int i, int j) {}",
"",
" public static void m2() {}",
"",
" public static void m2(String s) {}",
"",
" public static void m2(int i, int j) {}",
"",
" public static void m3() {}",
"",
" public static void m3(String s) {}",
"",
" public static void m3(int i, int j) {}",
"}")
"""
package com.example.sub;
public class A {
public static void m1() {}
public static void m1(String s) {}
public static void m1(int i, int j) {}
public static void m2() {}
public static void m2(String s) {}
public static void m2(int i, int j) {}
public static void m3() {}
public static void m3(String s) {}
public static void m3(int i, int j) {}
}
""")
.addSourceLines(
"com/example/sub/B.java",
"package com.example.sub;",
"",
"public class B {",
" public static void m1() {}",
"",
" public static void m1(String s) {}",
"",
" public static void m1(int i, int j) {}",
"",
" public static void m2() {}",
"",
" public static void m2(String s) {}",
"",
" public static void m2(int i, int j) {}",
"",
" public static void m3() {}",
"",
" public static void m3(String s) {}",
"",
" public static void m3(int i, int j) {}",
"}")
"""
package com.example.sub;
public class B {
public static void m1() {}
public static void m1(String s) {}
public static void m1(int i, int j) {}
public static void m2() {}
public static void m2(String s) {}
public static void m2(int i, int j) {}
public static void m3() {}
public static void m3(String s) {}
public static void m3(int i, int j) {}
}
""")
.addSourceLines(
"External.java",
"import com.example.A;",
"import com.example.sub.B;",
"",
"public class External {",
" void invocations() {",
" // BUG: Diagnostic contains:",
" new A().m1();",
" new A().m1(\"\");",
" new A().m1(0, 0);",
" new A().m2();",
" // BUG: Diagnostic contains:",
" new A().m2(\"\");",
" new A().m2(0, 0);",
" new A().m3();",
" new A().m3(\"\");",
" new A().m3(0, 0);",
" B.m1();",
" B.m1(\"\");",
" B.m1(0, 0);",
" B.m2();",
" B.m2(\"\");",
" B.m2(0, 0);",
" B.m3();",
" B.m3(\"\");",
" // BUG: Diagnostic contains:",
" B.m3(0, 0);",
" }",
"}")
"""
import com.example.A;
import com.example.sub.B;
public class External {
void invocations() {
// BUG: Diagnostic contains:
new A().m1();
new A().m1("");
new A().m1(0, 0);
new A().m2();
// BUG: Diagnostic contains:
new A().m2("");
new A().m2(0, 0);
new A().m3();
new A().m3("");
new A().m3(0, 0);
B.m1();
B.m1("");
B.m1(0, 0);
B.m2();
B.m2("");
B.m2(0, 0);
B.m3();
B.m3("");
// BUG: Diagnostic contains:
B.m3(0, 0);
}
}
""")
.addSourceLines(
"ExternalWithDifferentPackages.java",
"import com.example.B;",
"import com.example.sub.A;",
"",
"public class ExternalWithDifferentPackages {",
" void invocations() {",
" A.m1();",
" A.m1(\"\");",
" A.m1(0, 0);",
" A.m2();",
" A.m2(\"\");",
" A.m2(0, 0);",
" A.m3();",
" A.m3(\"\");",
" A.m3(0, 0);",
" new B().m1();",
" new B().m1(\"\");",
" new B().m1(0, 0);",
" new B().m2();",
" new B().m2(\"\");",
" new B().m2(0, 0);",
" new B().m3();",
" new B().m3(\"\");",
" new B().m3(0, 0);",
" }",
"}")
"""
import com.example.B;
import com.example.sub.A;
public class ExternalWithDifferentPackages {
void invocations() {
A.m1();
A.m1("");
A.m1(0, 0);
A.m2();
A.m2("");
A.m2(0, 0);
A.m3();
A.m3("");
A.m3(0, 0);
new B().m1();
new B().m1("");
new B().m1(0, 0);
new B().m2();
new B().m2("");
new B().m2(0, 0);
new B().m3();
new B().m3("");
new B().m3(0, 0);
}
}
""")
.doTest();
}

View File

@@ -27,24 +27,26 @@ final class MoreASTHelpersTest {
CompilationTestHelper.newInstance(FindMethodsTestChecker.class, getClass())
.addSourceLines(
"A.java",
"class A {",
" // BUG: Diagnostic contains: {foo=1, bar=2, baz=0}",
" void foo() {}",
"",
" // BUG: Diagnostic contains: {foo=1, bar=2, baz=0}",
" void bar() {}",
"",
" // BUG: Diagnostic contains: {foo=1, bar=2, baz=0}",
" void bar(int i) {}",
"",
" static class B {",
" // BUG: Diagnostic contains: {foo=0, bar=1, baz=1}",
" void bar() {}",
"",
" // BUG: Diagnostic contains: {foo=0, bar=1, baz=1}",
" void baz() {}",
" }",
"}")
"""
class A {
// BUG: Diagnostic contains: {foo=1, bar=2, baz=0}
void foo() {}
// BUG: Diagnostic contains: {foo=1, bar=2, baz=0}
void bar() {}
// BUG: Diagnostic contains: {foo=1, bar=2, baz=0}
void bar(int i) {}
static class B {
// BUG: Diagnostic contains: {foo=0, bar=1, baz=1}
void bar() {}
// BUG: Diagnostic contains: {foo=0, bar=1, baz=1}
void baz() {}
}
}
""")
.doTest();
}
@@ -53,24 +55,26 @@ final class MoreASTHelpersTest {
CompilationTestHelper.newInstance(MethodExistsTestChecker.class, getClass())
.addSourceLines(
"A.java",
"class A {",
" // BUG: Diagnostic contains: {foo=true, bar=true, baz=false}",
" void foo() {}",
"",
" // BUG: Diagnostic contains: {foo=true, bar=true, baz=false}",
" void bar() {}",
"",
" // BUG: Diagnostic contains: {foo=true, bar=true, baz=false}",
" void bar(int i) {}",
"",
" static class B {",
" // BUG: Diagnostic contains: {foo=false, bar=true, baz=true}",
" void bar() {}",
"",
" // BUG: Diagnostic contains: {foo=false, bar=true, baz=true}",
" void baz() {}",
" }",
"}")
"""
class A {
// BUG: Diagnostic contains: {foo=true, bar=true, baz=false}
void foo() {}
// BUG: Diagnostic contains: {foo=true, bar=true, baz=false}
void bar() {}
// BUG: Diagnostic contains: {foo=true, bar=true, baz=false}
void bar(int i) {}
static class B {
// BUG: Diagnostic contains: {foo=false, bar=true, baz=true}
void bar() {}
// BUG: Diagnostic contains: {foo=false, bar=true, baz=true}
void baz() {}
}
}
""")
.doTest();
}
@@ -79,42 +83,44 @@ final class MoreASTHelpersTest {
CompilationTestHelper.newInstance(FindMethodReturnTestChecker.class, getClass())
.addSourceLines(
"A.java",
"import java.util.stream.Stream;",
"",
"class A {",
" {",
" toString();",
" }",
"",
" String topLevelMethod() {",
" // BUG: Diagnostic contains: topLevelMethod",
" toString();",
" // BUG: Diagnostic contains: topLevelMethod",
" return toString();",
" }",
"",
" Stream<String> anotherMethod() {",
" // BUG: Diagnostic contains: anotherMethod",
" return Stream.of(1)",
" .map(",
" n -> {",
" toString();",
" return toString();",
" });",
" }",
"",
" void recursiveMethod(Runnable r) {",
" // BUG: Diagnostic contains: recursiveMethod",
" recursiveMethod(",
" new Runnable() {",
" @Override",
" public void run() {",
" // BUG: Diagnostic contains: run",
" toString();",
" }",
" });",
" }",
"}")
"""
import java.util.stream.Stream;
class A {
{
toString();
}
String topLevelMethod() {
// BUG: Diagnostic contains: topLevelMethod
toString();
// BUG: Diagnostic contains: topLevelMethod
return toString();
}
Stream<String> anotherMethod() {
// BUG: Diagnostic contains: anotherMethod
return Stream.of(1)
.map(
n -> {
toString();
return toString();
});
}
void recursiveMethod(Runnable r) {
// BUG: Diagnostic contains: recursiveMethod
recursiveMethod(
new Runnable() {
@Override
public void run() {
// BUG: Diagnostic contains: run
toString();
}
});
}
}
""")
.doTest();
}
@@ -123,17 +129,19 @@ final class MoreASTHelpersTest {
CompilationTestHelper.newInstance(AreSameTypeTestChecker.class, getClass())
.addSourceLines(
"A.java",
"class A {",
" void negative1(String a, Integer b) {}",
"",
" void negative2(Integer a, Number b) {}",
"",
" // BUG: Diagnostic contains:",
" void positive1(String a, String b) {}",
"",
" // BUG: Diagnostic contains:",
" void positive2(Iterable<String> a, Iterable<Integer> b) {}",
"}")
"""
class A {
void negative1(String a, Integer b) {}
void negative2(Integer a, Number b) {}
// BUG: Diagnostic contains:
void positive1(String a, String b) {}
// BUG: Diagnostic contains:
void positive2(Iterable<String> a, Iterable<Integer> b) {}
}
""")
.doTest();
}

View File

@@ -27,56 +27,58 @@ final class MoreJUnitMatchersTest {
CompilationTestHelper.newInstance(MethodMatchersTestChecker.class, getClass())
.addSourceLines(
"A.java",
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
"",
"import java.util.stream.Stream;",
"import org.junit.jupiter.api.AfterAll;",
"import org.junit.jupiter.api.AfterEach;",
"import org.junit.jupiter.api.BeforeAll;",
"import org.junit.jupiter.api.BeforeEach;",
"import org.junit.jupiter.api.RepeatedTest;",
"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;",
"",
"class A {",
" @BeforeAll",
" // BUG: Diagnostic contains: SETUP_OR_TEARDOWN_METHOD",
" public void beforeAll() {}",
"",
" @BeforeEach",
" @Test",
" // BUG: Diagnostic contains: TEST_METHOD, SETUP_OR_TEARDOWN_METHOD",
" protected void beforeEachAndTest() {}",
"",
" @AfterEach",
" // BUG: Diagnostic contains: SETUP_OR_TEARDOWN_METHOD",
" private void afterEach() {}",
"",
" @AfterAll",
" // BUG: Diagnostic contains: SETUP_OR_TEARDOWN_METHOD",
" private void afterAll() {}",
"",
" @Test",
" // BUG: Diagnostic contains: TEST_METHOD",
" void test() {}",
"",
" private static Stream<Arguments> booleanArgs() {",
" return Stream.of(arguments(false), arguments(true));",
" }",
"",
" @ParameterizedTest",
" @MethodSource(\"booleanArgs\")",
" // BUG: Diagnostic contains: TEST_METHOD, HAS_METHOD_SOURCE",
" void parameterizedTest(boolean b) {}",
"",
" @RepeatedTest(2)",
" // BUG: Diagnostic contains: TEST_METHOD",
" private void repeatedTest() {}",
"",
" private void unannotatedMethod() {}",
"}")
"""
import static org.junit.jupiter.params.provider.Arguments.arguments;
import java.util.stream.Stream;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.RepeatedTest;
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;
class A {
@BeforeAll
// BUG: Diagnostic contains: SETUP_OR_TEARDOWN_METHOD
public void beforeAll() {}
@BeforeEach
@Test
// BUG: Diagnostic contains: TEST_METHOD, SETUP_OR_TEARDOWN_METHOD
protected void beforeEachAndTest() {}
@AfterEach
// BUG: Diagnostic contains: SETUP_OR_TEARDOWN_METHOD
private void afterEach() {}
@AfterAll
// BUG: Diagnostic contains: SETUP_OR_TEARDOWN_METHOD
private void afterAll() {}
@Test
// BUG: Diagnostic contains: TEST_METHOD
void test() {}
private static Stream<Arguments> booleanArgs() {
return Stream.of(arguments(false), arguments(true));
}
@ParameterizedTest
@MethodSource("booleanArgs")
// BUG: Diagnostic contains: TEST_METHOD, HAS_METHOD_SOURCE
void parameterizedTest(boolean b) {}
@RepeatedTest(2)
// BUG: Diagnostic contains: TEST_METHOD
private void repeatedTest() {}
private void unannotatedMethod() {}
}
""")
.doTest();
}
@@ -85,32 +87,34 @@ final class MoreJUnitMatchersTest {
CompilationTestHelper.newInstance(MethodSourceFactoryNamesTestChecker.class, getClass())
.addSourceLines(
"A.java",
"import org.junit.jupiter.params.provider.MethodSource;",
"",
"class A {",
" @MethodSource",
" // BUG: Diagnostic contains: [matchingMethodSource]",
" void matchingMethodSource(boolean b) {}",
"",
" @MethodSource(\"myValueFactory\")",
" // BUG: Diagnostic contains: [myValueFactory]",
" void singleCustomMethodSource(boolean b) {}",
"",
" @MethodSource({",
" \"nullary()\",",
" \"nullary()\",",
" \"\",",
" \"withStringParam(java.lang.String)\",",
" \"paramsUnspecified\"",
" })",
" // BUG: Diagnostic contains: [nullary, nullary, multipleMethodSources, withStringParam,",
" // paramsUnspecified]",
" void multipleMethodSources(boolean b) {}",
"",
" @MethodSource({\"foo\", \"()\", \"bar\"})",
" // BUG: Diagnostic contains: [foo, , bar]",
" void methodSourceWithoutName(boolean b) {}",
"}")
"""
import org.junit.jupiter.params.provider.MethodSource;
class A {
@MethodSource
// BUG: Diagnostic contains: [matchingMethodSource]
void matchingMethodSource(boolean b) {}
@MethodSource("myValueFactory")
// BUG: Diagnostic contains: [myValueFactory]
void singleCustomMethodSource(boolean b) {}
@MethodSource({
"nullary()",
"nullary()",
"",
"withStringParam(java.lang.String)",
"paramsUnspecified"
})
// BUG: Diagnostic contains: [nullary, nullary, multipleMethodSources, withStringParam,
// paramsUnspecified]
void multipleMethodSources(boolean b) {}
@MethodSource({"foo", "()", "bar"})
// BUG: Diagnostic contains: [foo, , bar]
void methodSourceWithoutName(boolean b) {}
}
""")
.doTest();
}
@@ -119,49 +123,51 @@ final class MoreJUnitMatchersTest {
CompilationTestHelper.newInstance(MethodSourceFactoryDescriptorsTestChecker.class, getClass())
.addSourceLines(
"A.java",
"import org.junit.jupiter.params.provider.MethodSource;",
"",
"class A {",
" @MethodSource",
" // BUG: Diagnostic contains: [matchingMethodSource]",
" void matchingMethodSource(boolean b) {}",
"",
" @MethodSource()",
" // BUG: Diagnostic contains: [matchingMethodSourceWithParens]",
" void matchingMethodSourceWithParens(boolean b) {}",
"",
" @MethodSource(\"\")",
" // BUG: Diagnostic contains: [matchingMethodSourceMadeExplicit]",
" void matchingMethodSourceMadeExplicit(boolean b) {}",
"",
" @MethodSource({\"\"})",
" // BUG: Diagnostic contains: [matchingMethodSourceMadeExplicitWithParens]",
" void matchingMethodSourceMadeExplicitWithParens(boolean b) {}",
"",
" @MethodSource({})",
" // BUG: Diagnostic contains: []",
" void noMethodSources(boolean b) {}",
"",
" @MethodSource(\"myValueFactory\")",
" // BUG: Diagnostic contains: [myValueFactory]",
" void singleCustomMethodSource(boolean b) {}",
"",
" @MethodSource({\"firstValueFactory\", \"secondValueFactory\"})",
" // BUG: Diagnostic contains: [firstValueFactory, secondValueFactory]",
" void twoCustomMethodSources(boolean b) {}",
"",
" @MethodSource({\"myValueFactory\", \"\"})",
" // BUG: Diagnostic contains: [myValueFactory, customAndMatchingMethodSources]",
" void customAndMatchingMethodSources(boolean b) {}",
"",
" @MethodSource({\"factory\", \"\", \"factory\", \"\"})",
" // BUG: Diagnostic contains: [factory, repeatedMethodSources, factory, repeatedMethodSources]",
" void repeatedMethodSources(boolean b) {}",
"",
" @MethodSource({\"nullary()\", \"withStringParam(java.lang.String)\"})",
" // BUG: Diagnostic contains: [nullary(), withStringParam(java.lang.String)]",
" void methodSourcesWithParameterSpecification(boolean b) {}",
"}")
"""
import org.junit.jupiter.params.provider.MethodSource;
class A {
@MethodSource
// BUG: Diagnostic contains: [matchingMethodSource]
void matchingMethodSource(boolean b) {}
@MethodSource()
// BUG: Diagnostic contains: [matchingMethodSourceWithParens]
void matchingMethodSourceWithParens(boolean b) {}
@MethodSource("")
// BUG: Diagnostic contains: [matchingMethodSourceMadeExplicit]
void matchingMethodSourceMadeExplicit(boolean b) {}
@MethodSource({""})
// BUG: Diagnostic contains: [matchingMethodSourceMadeExplicitWithParens]
void matchingMethodSourceMadeExplicitWithParens(boolean b) {}
@MethodSource({})
// BUG: Diagnostic contains: []
void noMethodSources(boolean b) {}
@MethodSource("myValueFactory")
// BUG: Diagnostic contains: [myValueFactory]
void singleCustomMethodSource(boolean b) {}
@MethodSource({"firstValueFactory", "secondValueFactory"})
// BUG: Diagnostic contains: [firstValueFactory, secondValueFactory]
void twoCustomMethodSources(boolean b) {}
@MethodSource({"myValueFactory", ""})
// BUG: Diagnostic contains: [myValueFactory, customAndMatchingMethodSources]
void customAndMatchingMethodSources(boolean b) {}
@MethodSource({"factory", "", "factory", ""})
// BUG: Diagnostic contains: [factory, repeatedMethodSources, factory, repeatedMethodSources]
void repeatedMethodSources(boolean b) {}
@MethodSource({"nullary()", "withStringParam(java.lang.String)"})
// BUG: Diagnostic contains: [nullary(), withStringParam(java.lang.String)]
void methodSourcesWithParameterSpecification(boolean b) {}
}
""")
.doTest();
}

View File

@@ -26,32 +26,34 @@ final class MoreMatchersTest {
CompilationTestHelper.newInstance(HasMetaAnnotationTestChecker.class, getClass())
.addSourceLines(
"A.java",
"import org.junit.jupiter.api.AfterAll;",
"import org.junit.jupiter.api.RepeatedTest;",
"import org.junit.jupiter.api.Test;",
"import org.junit.jupiter.api.TestTemplate;",
"import org.junit.jupiter.params.ParameterizedTest;",
"",
"class A {",
" void negative1() {}",
"",
" @Test",
" void negative2() {}",
"",
" @AfterAll",
" void negative3() {}",
"",
" @TestTemplate",
" void negative4() {}",
"",
" // BUG: Diagnostic contains:",
" @ParameterizedTest",
" void positive1() {}",
"",
" // BUG: Diagnostic contains:",
" @RepeatedTest(2)",
" void positive2() {}",
"}")
"""
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.params.ParameterizedTest;
class A {
void negative1() {}
@Test
void negative2() {}
@AfterAll
void negative3() {}
@TestTemplate
void negative4() {}
// BUG: Diagnostic contains:
@ParameterizedTest
void positive1() {}
// BUG: Diagnostic contains:
@RepeatedTest(2)
void positive2() {}
}
""")
.doTest();
}
@@ -60,33 +62,35 @@ final class MoreMatchersTest {
CompilationTestHelper.newInstance(IsSubTypeOfTestChecker.class, getClass())
.addSourceLines(
"A.java",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"import com.google.common.collect.ImmutableSortedSet;",
"",
"class A {",
" void m() {",
" ImmutableSet.of(\"foo\");",
" ImmutableSortedSet.of(\"foo\");",
" ImmutableList.of(\"foo\");",
" ImmutableList.of(1);",
" ImmutableList.of(1.0);",
" ImmutableList.of((Number) 1);",
"",
" // BUG: Diagnostic contains:",
" ImmutableSet.of(1);",
" // BUG: Diagnostic contains:",
" ImmutableSet.of(1.0);",
" // BUG: Diagnostic contains:",
" ImmutableSet.of((Number) 1);",
" // BUG: Diagnostic contains:",
" ImmutableSortedSet.of(1);",
" // BUG: Diagnostic contains:",
" ImmutableSortedSet.of(1.0);",
" // BUG: Diagnostic contains:",
" ImmutableSortedSet.of((Number) 1);",
" }",
"}")
"""
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
class A {
void m() {
ImmutableSet.of("foo");
ImmutableSortedSet.of("foo");
ImmutableList.of("foo");
ImmutableList.of(1);
ImmutableList.of(1.0);
ImmutableList.of((Number) 1);
// BUG: Diagnostic contains:
ImmutableSet.of(1);
// BUG: Diagnostic contains:
ImmutableSet.of(1.0);
// BUG: Diagnostic contains:
ImmutableSet.of((Number) 1);
// BUG: Diagnostic contains:
ImmutableSortedSet.of(1);
// BUG: Diagnostic contains:
ImmutableSortedSet.of(1.0);
// BUG: Diagnostic contains:
ImmutableSortedSet.of((Number) 1);
}
}
""")
.doTest();
}
@@ -96,11 +100,13 @@ final class MoreMatchersTest {
.withClasspath()
.addSourceLines(
"A.java",
"class A {",
" void m() {",
" System.out.println(toString());",
" }",
"}")
"""
class A {
void m() {
System.out.println(toString());
}
}
""")
.doTest();
}

View File

@@ -30,91 +30,93 @@ final class MoreTypesTest {
CompilationTestHelper.newInstance(SubtypeFlagger.class, getClass())
.addSourceLines(
"/A.java",
"import java.util.Collection;",
"import java.util.List;",
"import java.util.Map;",
"import java.util.Optional;",
"import java.util.Set;",
"",
"class A<S, T> {",
" void m() {",
" Object object = factory();",
" A a = factory();",
"",
" // BUG: Diagnostic contains: [Number, ? super Number, Integer, ? super Integer]",
" int integer = factory();",
"",
" // BUG: Diagnostic contains: [String]",
" String string = factory();",
"",
" // BUG: Diagnostic contains: [Optional]",
" Optional rawOptional = factory();",
" // BUG: Diagnostic contains: [Optional, Optional<?>]",
" Optional<S> optionalOfS = factory();",
" // BUG: Diagnostic contains: [Optional, Optional<?>]",
" Optional<T> optionalOfT = factory();",
" // BUG: Diagnostic contains: [Optional, Optional<?>, Optional<Number>]",
" Optional<Number> optionalOfNumber = factory();",
" // BUG: Diagnostic contains: [Optional, Optional<?>]",
" Optional<Integer> optionalOfInteger = factory();",
"",
" // BUG: Diagnostic contains: [Collection]",
" Collection rawCollection = factory();",
" // BUG: Diagnostic contains: [Collection, Collection<?>, Collection<Number>, Collection<? super",
" // Number>, Collection<? extends Number>, Collection<? super Integer>]",
" Collection<Number> collectionOfNumber = factory();",
" // BUG: Diagnostic contains: [Collection, Collection<?>, Collection<? extends Number>,",
" // Collection<Integer>, Collection<? super Integer>, Collection<? extends Integer>]",
" Collection<Integer> collectionOfInteger = factory();",
" // BUG: Diagnostic contains: [Collection, Collection<?>, Collection<? extends Number>]",
" Collection<Short> collectionOfShort = factory();",
"",
" // BUG: Diagnostic contains: [Collection, List]",
" List rawList = factory();",
" // BUG: Diagnostic contains: [Collection, Collection<?>, Collection<Number>, Collection<? super",
" // Number>, Collection<? extends Number>, Collection<? super Integer>, List, List<?>,",
" // List<Number>, List<? super Number>, List<? extends Number>, List<? super Integer>]",
" List<Number> listOfNumber = factory();",
" // BUG: Diagnostic contains: [Collection, Collection<?>, Collection<? extends Number>,",
" // Collection<Integer>, Collection<? super Integer>, Collection<? extends Integer>, List,",
" // List<?>, List<? extends Number>, List<Integer>, List<? super Integer>, List<? extends",
" // Integer>]",
" List<Integer> listOfInteger = factory();",
" // BUG: Diagnostic contains: [Collection, Collection<?>, Collection<? extends Number>, List,",
" // List<?>, List<? extends Number>]",
" List<Short> listOfShort = factory();",
"",
" // BUG: Diagnostic contains: [Collection]",
" Set rawSet = factory();",
" // BUG: Diagnostic contains: [Collection, Collection<?>, Collection<Number>, Collection<? super",
" // Number>, Collection<? extends Number>, Collection<? super Integer>]",
" Set<Number> setOfNumber = factory();",
" // BUG: Diagnostic contains: [Collection, Collection<?>, Collection<? extends Number>,",
" // Collection<Integer>, Collection<? super Integer>, Collection<? extends Integer>]",
" Set<Integer> setOfInteger = factory();",
" // BUG: Diagnostic contains: [Collection, Collection<?>, Collection<? extends Number>]",
" Set<Short> setOfShort = factory();",
"",
" Map rawMap = factory();",
" Map<Number, Collection<Number>> mapFromNumberToCollectionOfNumber = factory();",
" Map<Number, Collection<Short>> mapFromNumberToCollectionOfShort = factory();",
" Map<Number, Collection<Integer>> mapFromNumberToCollectionOfInteger = factory();",
" // BUG: Diagnostic contains: [Map<String, ? extends Collection<? super Short>>]",
" Map<String, Collection<Number>> mapFromStringToCollectionOfNumber = factory();",
" // BUG: Diagnostic contains: [Map<String, ? extends Collection<? super Short>>]",
" Map<String, Collection<Short>> mapFromStringToCollectionOfShort = factory();",
" Map<String, Collection<Integer>> mapFromStringToCollectionOfInteger = factory();",
" // BUG: Diagnostic contains: [Map<String, ? extends Collection<? super Short>>]",
" Map<String, List<Number>> mapFromStringToListOfNumber = factory();",
" // BUG: Diagnostic contains: [Map<String, ? extends Collection<? super Short>>]",
" Map<String, List<Short>> mapFromStringToListOfShort = factory();",
" Map<String, List<Integer>> mapFromStringToListOfInteger = factory();",
" }",
"",
" private <T> T factory() {",
" return null;",
" }",
"}")
"""
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
class A<S, T> {
void m() {
Object object = factory();
A a = factory();
// BUG: Diagnostic contains: [Number, ? super Number, Integer, ? super Integer]
int integer = factory();
// BUG: Diagnostic contains: [String]
String string = factory();
// BUG: Diagnostic contains: [Optional]
Optional rawOptional = factory();
// BUG: Diagnostic contains: [Optional, Optional<?>]
Optional<S> optionalOfS = factory();
// BUG: Diagnostic contains: [Optional, Optional<?>]
Optional<T> optionalOfT = factory();
// BUG: Diagnostic contains: [Optional, Optional<?>, Optional<Number>]
Optional<Number> optionalOfNumber = factory();
// BUG: Diagnostic contains: [Optional, Optional<?>]
Optional<Integer> optionalOfInteger = factory();
// BUG: Diagnostic contains: [Collection]
Collection rawCollection = factory();
// BUG: Diagnostic contains: [Collection, Collection<?>, Collection<Number>, Collection<? super
// Number>, Collection<? extends Number>, Collection<? super Integer>]
Collection<Number> collectionOfNumber = factory();
// BUG: Diagnostic contains: [Collection, Collection<?>, Collection<? extends Number>,
// Collection<Integer>, Collection<? super Integer>, Collection<? extends Integer>]
Collection<Integer> collectionOfInteger = factory();
// BUG: Diagnostic contains: [Collection, Collection<?>, Collection<? extends Number>]
Collection<Short> collectionOfShort = factory();
// BUG: Diagnostic contains: [Collection, List]
List rawList = factory();
// BUG: Diagnostic contains: [Collection, Collection<?>, Collection<Number>, Collection<? super
// Number>, Collection<? extends Number>, Collection<? super Integer>, List, List<?>,
// List<Number>, List<? super Number>, List<? extends Number>, List<? super Integer>]
List<Number> listOfNumber = factory();
// BUG: Diagnostic contains: [Collection, Collection<?>, Collection<? extends Number>,
// Collection<Integer>, Collection<? super Integer>, Collection<? extends Integer>, List,
// List<?>, List<? extends Number>, List<Integer>, List<? super Integer>, List<? extends
// Integer>]
List<Integer> listOfInteger = factory();
// BUG: Diagnostic contains: [Collection, Collection<?>, Collection<? extends Number>, List,
// List<?>, List<? extends Number>]
List<Short> listOfShort = factory();
// BUG: Diagnostic contains: [Collection]
Set rawSet = factory();
// BUG: Diagnostic contains: [Collection, Collection<?>, Collection<Number>, Collection<? super
// Number>, Collection<? extends Number>, Collection<? super Integer>]
Set<Number> setOfNumber = factory();
// BUG: Diagnostic contains: [Collection, Collection<?>, Collection<? extends Number>,
// Collection<Integer>, Collection<? super Integer>, Collection<? extends Integer>]
Set<Integer> setOfInteger = factory();
// BUG: Diagnostic contains: [Collection, Collection<?>, Collection<? extends Number>]
Set<Short> setOfShort = factory();
Map rawMap = factory();
Map<Number, Collection<Number>> mapFromNumberToCollectionOfNumber = factory();
Map<Number, Collection<Short>> mapFromNumberToCollectionOfShort = factory();
Map<Number, Collection<Integer>> mapFromNumberToCollectionOfInteger = factory();
// BUG: Diagnostic contains: [Map<String, ? extends Collection<? super Short>>]
Map<String, Collection<Number>> mapFromStringToCollectionOfNumber = factory();
// BUG: Diagnostic contains: [Map<String, ? extends Collection<? super Short>>]
Map<String, Collection<Short>> mapFromStringToCollectionOfShort = factory();
Map<String, Collection<Integer>> mapFromStringToCollectionOfInteger = factory();
// BUG: Diagnostic contains: [Map<String, ? extends Collection<? super Short>>]
Map<String, List<Number>> mapFromStringToListOfNumber = factory();
// BUG: Diagnostic contains: [Map<String, ? extends Collection<? super Short>>]
Map<String, List<Short>> mapFromStringToListOfShort = factory();
Map<String, List<Integer>> mapFromStringToListOfInteger = factory();
}
private <T> T factory() {
return null;
}
}
""")
.doTest();
}

View File

@@ -5,92 +5,136 @@ import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.BugPattern;
import com.google.errorprone.CompilationTestHelper;
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.bugpatterns.BugChecker.ReturnTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ReturnTree;
import com.sun.source.tree.Tree;
import javax.lang.model.element.Name;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
final class SourceCodeTest {
@DisabledForJreRange(max = JRE.JAVA_14)
@Test
void isTextBlock() {
CompilationTestHelper.newInstance(TextBlockFlagger.class, getClass())
.addSourceLines(
"A.java",
"""
class A {
String negative1() {
return toString();
}
String negative2() {
return "foo";
}
String positive1() {
// BUG: Diagnostic contains:
return ""\"
foo
""\";
}
}
""")
.doTest();
}
@Test
void deleteWithTrailingWhitespaceAnnotations() {
BugCheckerRefactoringTestHelper.newInstance(
DeleteWithTrailingWhitespaceTestChecker.class, getClass())
.addInputLines("AnnotationToBeDeleted.java", "@interface AnnotationToBeDeleted {}")
.addInputLines(
"AnnotationToBeDeleted.java",
"""
@interface AnnotationToBeDeleted {}
""")
.expectUnchanged()
.addInputLines(
"AnotherAnnotationToBeDeleted.java", "@interface AnotherAnnotationToBeDeleted {}")
"AnotherAnnotationToBeDeleted.java",
"""
@interface AnotherAnnotationToBeDeleted {}
""")
.expectUnchanged()
.addInputLines(
"AnnotationDeletions.java",
"interface AnnotationDeletions {",
" class SoleAnnotation {",
" @AnnotationToBeDeleted",
" void m() {}",
" }",
"",
" class FirstAnnotation {",
" @AnnotationToBeDeleted",
" @Deprecated",
" void m() {}",
" }",
"",
" class MiddleAnnotation {",
" @Deprecated",
" @AnnotationToBeDeleted",
" @SuppressWarnings(\"foo\")",
" void m() {}",
" }",
"",
" class LastAnnotation {",
" @Deprecated",
" @AnnotationToBeDeleted",
" void m() {}",
" }",
"",
" class MultipleAnnotations {",
" @AnnotationToBeDeleted",
" @AnotherAnnotationToBeDeleted",
" @Deprecated",
" void m() {}",
" }",
"}")
"""
interface AnnotationDeletions {
class SoleAnnotation {
@AnnotationToBeDeleted
void m() {}
}
class FirstAnnotation {
@AnnotationToBeDeleted
@Deprecated
void m() {}
}
class MiddleAnnotation {
@Deprecated
@AnnotationToBeDeleted
@SuppressWarnings("foo")
void m() {}
}
class LastAnnotation {
@Deprecated
@AnnotationToBeDeleted
void m() {}
}
class MultipleAnnotations {
@AnnotationToBeDeleted
@AnotherAnnotationToBeDeleted
@Deprecated
void m() {}
}
}
""")
.addOutputLines(
"AnnotationDeletions.java",
"interface AnnotationDeletions {",
" class SoleAnnotation {",
" void m() {}",
" }",
"",
" class FirstAnnotation {",
" @Deprecated",
" void m() {}",
" }",
"",
" class MiddleAnnotation {",
" @Deprecated",
" @SuppressWarnings(\"foo\")",
" void m() {}",
" }",
"",
" class LastAnnotation {",
" @Deprecated",
" void m() {}",
" }",
"",
" class MultipleAnnotations {",
" @Deprecated",
" void m() {}",
" }",
"}")
"""
interface AnnotationDeletions {
class SoleAnnotation {
void m() {}
}
class FirstAnnotation {
@Deprecated
void m() {}
}
class MiddleAnnotation {
@Deprecated
@SuppressWarnings("foo")
void m() {}
}
class LastAnnotation {
@Deprecated
void m() {}
}
class MultipleAnnotations {
@Deprecated
void m() {}
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
@@ -100,66 +144,70 @@ final class SourceCodeTest {
DeleteWithTrailingWhitespaceTestChecker.class, getClass())
.addInputLines(
"MethodDeletions.java",
"interface MethodDeletions {",
" class SoleMethod {",
" void methodToBeDeleted() {}",
" }",
"",
" class FirstMethod {",
" void methodToBeDeleted() {}",
"",
" void finalMethod() {}",
" }",
"",
" class MiddleMethod {",
" void initialMethod() {}",
"",
" void methodToBeDeleted() {}",
"",
" void finalMethod() {}",
" }",
"",
" class LastMethod {",
" void initialMethod() {}",
"",
" void methodToBeDeleted() {}",
" }",
"",
" class MultipleMethods {",
" void method1ToBeDeleted() {}",
"",
" void method2ToBeDeleted() {}",
"",
" void middleMethod() {}",
"",
" void method3ToBeDeleted() {}",
"",
" void method4ToBeDeleted() {}",
" }",
"}")
"""
interface MethodDeletions {
class SoleMethod {
void methodToBeDeleted() {}
}
class FirstMethod {
void methodToBeDeleted() {}
void finalMethod() {}
}
class MiddleMethod {
void initialMethod() {}
void methodToBeDeleted() {}
void finalMethod() {}
}
class LastMethod {
void initialMethod() {}
void methodToBeDeleted() {}
}
class MultipleMethods {
void method1ToBeDeleted() {}
void method2ToBeDeleted() {}
void middleMethod() {}
void method3ToBeDeleted() {}
void method4ToBeDeleted() {}
}
}
""")
.addOutputLines(
"MethodDeletions.java",
"interface MethodDeletions {",
" class SoleMethod {}",
"",
" class FirstMethod {",
" void finalMethod() {}",
" }",
"",
" class MiddleMethod {",
" void initialMethod() {}",
"",
" void finalMethod() {}",
" }",
"",
" class LastMethod {",
" void initialMethod() {}",
" }",
"",
" class MultipleMethods {",
" void middleMethod() {}",
" }",
"}")
"""
interface MethodDeletions {
class SoleMethod {}
class FirstMethod {
void finalMethod() {}
}
class MiddleMethod {
void initialMethod() {}
void finalMethod() {}
}
class LastMethod {
void initialMethod() {}
}
class MultipleMethods {
void middleMethod() {}
}
}
""")
.doTest(TestMode.TEXT_MATCH);
}
@@ -168,30 +216,34 @@ final class SourceCodeTest {
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*/",
" }",
" };",
" }",
"}")
"""
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*/}};",
" }",
"}")
"""
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);
}
@@ -201,33 +253,53 @@ final class SourceCodeTest {
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*/",
" }",
" };",
" }",
"}")
"""
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*/}};",
" }",
"}")
"""
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 delegates to {@link SourceCode#isTextBlock(ExpressionTree,
* VisitorState)}.
*/
@BugPattern(summary = "Interacts with `SourceCode` for testing purposes", severity = ERROR)
public static final class TextBlockFlagger extends BugChecker implements ReturnTreeMatcher {
private static final long serialVersionUID = 1L;
@Override
public Description matchReturn(ReturnTree tree, VisitorState state) {
return SourceCode.isTextBlock(tree.getExpression(), state)
? describeMatch(tree)
: Description.NO_MATCH;
}
}
/**
* A {@link BugChecker} that uses {@link SourceCode#deleteWithTrailingWhitespace(Tree,
* VisitorState)} to suggest the deletion of annotations and methods with a name containing

View File

@@ -23,8 +23,10 @@ final class ThirdPartyLibraryTest {
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.addSourceLines(
"A.java",
"// BUG: Diagnostic contains: ASSERTJ: true, GUAVA: true, REACTOR: true",
"class A {}")
"""
// BUG: Diagnostic contains: ASSERTJ: true, GUAVA: true, REACTOR: true
class A {}
""")
.doTest();
}
@@ -33,18 +35,20 @@ final class ThirdPartyLibraryTest {
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.addSourceLines(
"A.java",
"import com.google.common.collect.ImmutableList;",
"import org.assertj.core.api.Assertions;",
"import reactor.core.publisher.Flux;",
"",
"// BUG: Diagnostic contains: ASSERTJ: true, GUAVA: true, REACTOR: true",
"class A {",
" void m(Class<?> clazz) {",
" m(Assertions.class);",
" m(ImmutableList.class);",
" m(Flux.class);",
" }",
"}")
"""
import com.google.common.collect.ImmutableList;
import org.assertj.core.api.Assertions;
import reactor.core.publisher.Flux;
// BUG: Diagnostic contains: ASSERTJ: true, GUAVA: true, REACTOR: true
class A {
void m(Class<?> clazz) {
m(Assertions.class);
m(ImmutableList.class);
m(Flux.class);
}
}
""")
.doTest();
}
@@ -54,8 +58,10 @@ final class ThirdPartyLibraryTest {
.withClasspath(ImmutableList.class, Flux.class)
.addSourceLines(
"A.java",
"// BUG: Diagnostic contains: ASSERTJ: false, GUAVA: true, REACTOR: true",
"class A {}")
"""
// BUG: Diagnostic contains: ASSERTJ: false, GUAVA: true, REACTOR: true
class A {}
""")
.doTest();
}
@@ -65,9 +71,11 @@ final class ThirdPartyLibraryTest {
.withClasspath()
.addSourceLines(
"A.java",
"// BUG: Diagnostic contains: ASSERTJ: false, GUAVA: false, REACTOR:",
"// false",
"class A {}")
"""
// BUG: Diagnostic contains: ASSERTJ: false, GUAVA: false, REACTOR:
// false
class A {}
""")
.doTest();
}

View File

@@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import java.util.OptionalInt;
import java.util.function.IntPredicate;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;

140
openai-coder/README.md Normal file
View File

@@ -0,0 +1,140 @@
# OpenAI Coder
A command line utility that queries OpenAI for code fixes based on error and
warning messages extracted from Maven build output.
## Open topics and ideas.
* Apply suggstions from https://clig.dev/.
* Review ideas from
http://catb.org/~esr/writings/taoup/html/ch10s05.html#id2948149 (e.g. `-b`).
* Add support for an interactive mode, in which the user is prompted to
confirm an identified issue before sending it to OpenAI.
* In case multiple issues are reported against a single file, the user
should be able to (a) select which issue(s) to send to OpenAI and (b)
whether to batch the requests.
* The user should be able to accept or reject the OpenAI response.
* The user should be able to (re)try with a higher or lower temperature.
* If a change is accepted, the user may opt to resubmit the modified file
with a (new) subset of the reported issues.
* The user should be able to modify the OpenAI request payload before
sending it.
(Check whether this can be done with Picocli (see below); otherwise check
out [JLine](https://jline.github.io/jline3/).)
* Integrate with [Picocli](https://picocli.info/) (or similar) to make the
command line interface more user-friendly.
* Add a `--dry-run` flag that only prints the issues that would be reported
to OpenAI.
* Add a `--verbose` flag that prints the OpenAI request and response
payloads, or some abstraction thereof.
* Add a `--no-interactive` flag that does not prompt the user to confirm
the issue before sending it to OpenAI.
* Add flags to include/exclude certain types of issues and files from being
processed.
* Add a `--help` flag that prints a help message.
* Add a `--run-to-fix <command>` (name TBD) flag that repeatedly runs the
given command in a sub-process and processes the output until either no
further issues are reported, or no further fixes are found. (For this we
could use `ProcessBuilder`.)
* Add a `--git-safe` (name TBD) flag that only processes files that are
tracked by Git and that have not been modified.
* Add a `--format <format>` flag that allows the user to specify the
format of the input. E.g. `--format maven` (default), `--format
errorprone`, `--format sarif`, etc.
* Add a `--patch-context <lines>` flag that allows the user to specify the
number of lines of context to include in presented unified patches.
* Create a binary image using [GraalVM](https://www.graalvm.org/).
* Add support for sending a suitable subset of the code to OpenAI, so as (a) to
better deal with the token limit and (b) potentially reduce cost. This might
take the form of using the line numbers of the issue to extract code section,
as well as any relevant context. E.g. by replacing the source of irrelevant
members with a unique placeholder. When the response is received, the
placeholders are replaced with the original code.
* Add support for parsing other formats (next to Maven build output). E.g.
Sarif.
* Add support for a mode in which a file (or if we can pull it off: a group of
files) is explicitly specified to be processed using instructions entered
interactively or non-interactively.
* Introduce an `IssueExtractor` for Error Prone test compiler output.
* Write an `IssueExtractor` for the output of the `dependency:analyze` Maven
goal:
```
[INFO] --- dependency:3.5.0:analyze-only (analyze-dependencies) @ openai-coder ---
[WARNING] Used undeclared dependencies found:
[WARNING] com.google.code.findbugs:jsr305:jar:3.0.2:compile
[WARNING] com.theokanning.openai-gpt3-java:api:jar:0.12.0:compile
[WARNING] org.junit.jupiter:junit-jupiter-api:jar:5.9.2:test
[WARNING] io.github.java-diff-utils:java-diff-utils:jar:4.0:compile
[WARNING] Unused declared dependencies found:
[WARNING] com.fasterxml.jackson.core:jackson-databind:jar:2.14.2:compile
[WARNING] tech.picnic.error-prone-support:refaster-runner:jar:0.9.1-SNAPSHOT:compile
[WARNING] com.google.errorprone:error_prone_check_api:jar:2.18.0:compile
[WARNING] com.google.errorprone:error_prone_annotation:jar:2.18.0:compile
[WARNING] com.google.errorprone:error_prone_test_helpers:jar:2.18.0:compile
[WARNING] org.springframework:spring-core:jar:5.3.26:compile
```
This could e.g. be transformed to:
```
[INFO] --- dependency:3.5.0:analyze-only (analyze-dependencies) @ openai-coder ---
[WARNING] Used undeclared dependencies found:
[WARNING] com.google.code.findbugs:jsr305:jar:3.0.2:compile
[WARNING] com.theokanning.openai-gpt3-java:api:jar:0.12.0:compile
[WARNING] org.junit.jupiter:junit-jupiter-api:jar:5.9.2:test
[WARNING] io.github.java-diff-utils:java-diff-utils:jar:4.0:compile
[WARNING] Unused declared dependencies found:
[WARNING] com.fasterxml.jackson.core:jackson-databind:jar:2.14.2:compile
[WARNING] tech.picnic.error-prone-support:refaster-runner:jar:0.9.1-SNAPSHOT:compile
[WARNING] com.google.errorprone:error_prone_check_api:jar:2.18.0:compile
[WARNING] com.google.errorprone:error_prone_annotation:jar:2.18.0:compile
[WARNING] com.google.errorprone:error_prone_test_helpers:jar:2.18.0:compile
[WARNING] org.springframework:spring-core:jar:5.3.26:compile
```
(Though `type` and `version` can then conditionally be omitted.)
The problem: the relevant `pom.xml` file is not available in the Maven
output. It could be inferred from the preceding `dependency:analyze` line,
but that line is logged at the `INFO` level, which is not extracted by
the `LogLineExtractor`.
# Interactive mode API ideation
For each file, the user is presented with a list of issues:
```
Issues in `/path/to/File.java`:
1. Issue 1.
2. Issue 2.
3. Issue 3.
Action [submit]: <prompt>
```
Upon submitting the issues to OpenAI, the user is presented with a unified
patch of the suggested changes:
```
Suggested changes:
<diff>
Action [apply]: <prompt>
```
Commands:
- If empty, then apply the appropriate default, depending on context:
- If the most recent prompt listed the issues, then submit all issues to
OpenAI (`submit`).
- If the most recent prompt listed the suggested fixes, then apply the
suggested fixes (`apply`).
- `h`/`help`: Print help message.
- `i`/`issues`: Print list of issues.
- `s`/`submit [<issue>, ...]`: Submit all or the given issues to OpenAI.
- `a`/`apply`: Apply the suggested changes:
- If no changes have yet been suggested, then print a message and prompt
again.
- If changes have been suggested, then apply them, print a message and move
on to the next file.
- `q`/`quit`: Quit the program.
- `n`/`next`: Skip the current file.
- `p`/`prev`/`previous`: Go back to the previous file.

110
openai-coder/pom.xml Normal file
View File

@@ -0,0 +1,110 @@
<?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.9.1-SNAPSHOT</version>
</parent>
<artifactId>openai-coder</artifactId>
<name>Picnic :: Error Prone Support :: OpenAI Coder</name>
<description>Experimental tools that integrate with the OpenAI API.</description>
<dependencies>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotation</artifactId>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_check_api</artifactId>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_test_helpers</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-runner</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.google.jimfs</groupId>
<artifactId>jimfs</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.theokanning.openai-gpt3-java</groupId>
<artifactId>service</artifactId>
</dependency>
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
</dependency>
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli-shell-jline3</artifactId>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
</dependency>
<dependency>
<groupId>org.fusesource.jansi</groupId>
<artifactId>jansi</artifactId>
</dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline</artifactId>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<scope>provided</scope>
</dependency>
<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>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,160 @@
package tech.picnic.errorprone.openai;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.function.Supplier;
import javax.annotation.WillClose;
import javax.annotation.WillNotClose;
import org.fusesource.jansi.AnsiConsole;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import tech.picnic.errorprone.openai.IssueExtractor.Issue;
@Command(name = "", mixinStandardHelpOptions = true, description = "OpenAI Coder CLI.")
public final class AiCoder {
/** The name of the environment variable containing the OpenAI token. */
private static final String OPENAI_TOKEN_VARIABLE = "OPENAI_TOKEN";
private final OpenAi openAi;
private final PrintStream out;
private final PrintStream err;
private AiCoder(OpenAi openAi, @WillNotClose PrintStream out, @WillNotClose PrintStream err) {
this.openAi = openAi;
this.out = out;
this.err = err;
}
@Command(
name = "process-build-output",
mixinStandardHelpOptions = true,
showEndOfOptionsDelimiterInUsageHelp = true,
description = "Attempts to resolve issues extracted from build output.")
void processBuildOutput(
@Option(
names = {"-a", "--auto-fix"},
description = "Submit all issues to OpenAI and accept the results.")
boolean autoFix,
@Option(
names = {"-c", "--command"},
description =
"Interpret the positional arguments as a command to be executed, rather than output files.")
boolean command,
@Parameters(description = "The files containing the build output or the command to run.")
List<String> filesOrCommand) {
// XXX: Replace this code.
if (autoFix) {
// XXX: Implement auto-fixing.
System.out.println("Auto-fixing issues in " + filesOrCommand);
} else {
// XXX: Implement analyzing.
System.out.println("Analyzing issues in " + filesOrCommand);
}
Supplier<ImmutableSet<Issue<Path>>> issueSupplier =
command
? () -> extractIssues(ImmutableList.copyOf(filesOrCommand), out, err)
: () ->
filesOrCommand.stream()
.flatMap(f -> extractIssues(Path.of(f)).stream())
.collect(toImmutableSet());
InteractiveBuildOutputProcessor.run(openAi, issueSupplier);
}
private static ImmutableSet<Issue<Path>> extractIssues(
ImmutableList<String> command, PrintStream out, PrintStream err) {
try {
// XXX: If `ctrl-c` is pressed while the command is running, then seemingly JLine does't
// intercept it. Investigate.
Process process = new ProcessBuilder(command).start();
StringBuilder collectedOutput = new StringBuilder();
try (InputStream processOutput = process.getInputStream();
BufferedReader output = new BufferedReader(new InputStreamReader(processOutput, UTF_8))) {
String line = null;
for (line = output.readLine(); line != null; line = output.readLine()) {
out.println(line);
collectedOutput.append(line).append('\n');
}
process.waitFor();
// XXX: Report the exit code?
// XXX: Process the output.
} catch (InterruptedException e) {
err.println("Interrupted while waiting for process to finish.");
Thread.currentThread().interrupt();
}
// XXX: This `ByteArrayInputStream` usage is dodgy. Review.
return extractIssues(new ByteArrayInputStream(collectedOutput.toString().getBytes(UTF_8)));
} catch (IOException e) {
throw new UncheckedIOException(
String.format(
"Failed to execute or parse result of command '%s'", String.join(" ", command)),
e);
}
}
private static ImmutableSet<Issue<Path>> extractIssues(Path file) {
try (InputStream is = Files.newInputStream(file)) {
return extractIssues(is);
} catch (IOException e) {
throw new UncheckedIOException(String.format("Failed to parse file '%s'", file), e);
}
}
private static ImmutableSet<Issue<Path>> extractIssues(@WillClose InputStream inputStream)
throws IOException {
// XXX: Here and elsewhere: should we allow the cwd to be changed?
IssueExtractor<Path> issueExtractor =
new PathResolvingIssueExtractor(
new PathFinder(FileSystems.getDefault(), Path.of("")),
new SelectFirstIssueExtractor<>(
ImmutableSet.of(
new MavenCheckstyleIssueExtractor(), new PlexusCompilerIssueExtractor())));
return LogLineExtractor.mavenErrorAndWarningExtractor().extract(inputStream).stream()
.flatMap(issueExtractor::extract)
.collect(toImmutableSet());
}
public static void main(String... args) {
AnsiConsole.systemInstall();
try {
String openAiToken = System.getenv(OPENAI_TOKEN_VARIABLE);
if (openAiToken == null) {
AnsiConsole.err().printf("Environment variable %s not set.%n", OPENAI_TOKEN_VARIABLE);
System.exit(1);
}
try (OpenAi openAi = OpenAi.create(openAiToken)) {
System.exit(createCommandLine(openAi).execute(args));
}
} finally {
AnsiConsole.systemUninstall();
}
}
private static CommandLine createCommandLine(OpenAi openAi) {
CommandLine commandLine =
new CommandLine(new AiCoder(openAi, AnsiConsole.out(), AnsiConsole.err()));
commandLine.getCommandSpec().version(AiCoder.class.getPackage().getImplementationVersion());
return commandLine;
}
}

View File

@@ -0,0 +1,102 @@
package tech.picnic.errorprone.openai;
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
import static java.util.stream.Collectors.joining;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Streams;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.openai.IssueExtractor.Issue;
public final class AiPatcher {
private static final Pattern FILE_LOCATION_MARKER =
Pattern.compile("^(.*?\\.java):\\[(\\d+)(?:,(\\d+))?\\] ");
// XXX: Rename
private static final String OPENAI_TOKEN_VARIABLE = "openapi_token";
@Nullable private static final String OPENAI_TOKEN = System.getenv(OPENAI_TOKEN_VARIABLE);
// Allow a custom source lookup directory to be specified.
// Group by file.
public static void main(String... args) {
if (OPENAI_TOKEN == null) {
System.err.printf(
"OpenAI API token not found in environment variable '%s'.%n", OPENAI_TOKEN_VARIABLE);
System.exit(1);
}
try {
suggestFixes(
getIssuesByFile(LogLineExtractor.mavenErrorAndWarningExtractor().extract(System.in)));
} catch (IOException e) {
// XXX: Fix
throw new RuntimeException(e);
}
// Explicitly exit to prevent `mvn exec:java` from handing due to long-lived OkHTTP threads.
System.exit(0);
}
private static void suggestFixes(ImmutableSetMultimap<Path, String> issuesByFile)
throws IOException {
try (OpenAi openAi = OpenAi.create(OPENAI_TOKEN)) {
for (Map.Entry<Path, Set<String>> e : Multimaps.asMap(issuesByFile).entrySet()) {
suggestFixes(e.getKey(), e.getValue(), openAi);
}
}
}
private static void suggestFixes(Path file, Set<String> issueDescriptions, OpenAi openAi)
throws IOException {
// XXX: Cleanup
String originalCode = Files.readString(file);
if (file.toString().contains("RefasterRuleCollection")) {
return;
}
String instruction =
Streams.mapWithIndex(
issueDescriptions.stream(),
(description, index) -> String.format("%s. %s", index + 1, description))
.collect(joining("\n", "Resolve the following issues:\n", "\n"));
System.out.println("Instruction: " + instruction);
if (true) {
// return;
}
// XXX: Handle case with too much input/output (tokens).
// XXX: Handle error messages.
String result = openAi.requestEdit(originalCode, instruction);
// XXX: !!! Don't create diff in patch mode; just apply the patch.
System.out.printf("Fix for %s:%n", Diffs.unifiedDiff(originalCode, result, file.toString()));
}
private static ImmutableSetMultimap<Path, String> getIssuesByFile(List<String> logMessages) {
// XXX: Allow the path to be specified.
IssueExtractor<Path> issueExtractor =
new PathResolvingIssueExtractor(
new PathFinder(FileSystems.getDefault(), Path.of("")),
new SelectFirstIssueExtractor<>(
ImmutableSet.of(
new MavenCheckstyleIssueExtractor(), new PlexusCompilerIssueExtractor())));
return logMessages.stream()
.flatMap(issueExtractor::extract)
.collect(toImmutableSetMultimap(Issue::file, Issue::description));
}
}

View File

@@ -0,0 +1,52 @@
package tech.picnic.errorprone.openai;
import static org.fusesource.jansi.Ansi.ansi;
import com.github.difflib.DiffUtils;
import com.github.difflib.UnifiedDiffUtils;
import com.github.difflib.algorithm.DiffException;
import com.github.difflib.patch.Patch;
import com.google.common.base.Splitter;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.util.List;
final class Diffs {
private Diffs() {}
// XXX: Review cross-platform newline usage.
static String unifiedDiff(String before, String after, String path) {
return String.join("\n", generateUnifiedDiffLines(before, after, path));
}
// XXX: Review cross-platform newline usage.
// XXX: Test!
// XXX: Name?
static void printUnifiedDiff(String before, String after, Path path, PrintWriter out) {
for (String line : generateUnifiedDiffLines(before, after, path.toString())) {
if (line.startsWith("+")) {
out.println(ansi().fgGreen().a(line).reset());
} else if (line.startsWith("-")) {
out.println(ansi().fgRed().a(line).reset());
} else if (line.startsWith("@@")) {
out.println(ansi().fgYellow().a(line).reset());
} else {
out.println(line);
}
}
}
private static List<String> generateUnifiedDiffLines(String before, String after, String path) {
List<String> originalLines = Splitter.on('\n').splitToList(before);
List<String> replacementLines = Splitter.on('\n').splitToList(after);
Patch<String> diff;
try {
diff = DiffUtils.diff(originalLines, replacementLines);
} catch (DiffException e) {
throw new IllegalStateException("Failed to create diff", e);
}
return UnifiedDiffUtils.generateUnifiedDiff(path, path, originalLines, diff, 3);
}
}

View File

@@ -0,0 +1,18 @@
package tech.picnic.errorprone.openai;
import com.google.common.collect.ImmutableList;
// XXX: Use or drop.
final class FileEditSuggester {
private final OpenAi openAi;
FileEditSuggester(OpenAi openAi) {
this.openAi = openAi;
}
ImmutableList<EditSuggestion> suggestEdits(String fileContent) {
return ImmutableList.of();
}
record EditSuggestion(String replacement, String unifiedPatch) {}
}

View File

@@ -0,0 +1,390 @@
package tech.picnic.errorprone.openai;
import static com.google.common.base.Preconditions.checkArgument;
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.nio.charset.StandardCharsets.UTF_8;
import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.joining;
import static org.fusesource.jansi.Ansi.ansi;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Streams;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.IntStream;
import javax.annotation.concurrent.NotThreadSafe;
import org.fusesource.jansi.Ansi;
import org.jline.console.SystemRegistry;
import org.jline.console.impl.SystemRegistryImpl;
import org.jline.keymap.KeyMap;
import org.jline.reader.EndOfFileException;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.MaskingCallback;
import org.jline.reader.Parser;
import org.jline.reader.Reference;
import org.jline.reader.UserInterruptException;
import org.jline.reader.impl.DefaultParser;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import org.jline.widget.TailTipWidgets;
import org.jspecify.annotations.Nullable;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.HelpCommand;
import picocli.CommandLine.Parameters;
import picocli.shell.jline3.PicocliCommands;
import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
import tech.picnic.errorprone.openai.IssueExtractor.Issue;
// XXX: Review whether to enable a *subset* of JLine's built-ins. See
// https://github.com/remkop/picocli/tree/main/picocli-shell-jline3#jline-316-and-picocli-44-example.
// XXX: Consider utilizing `less` for paging. See
// https://github.com/jline/jline3/wiki/Nano-and-Less-Customization.
// XXX: Should we even support those `files.isEmpty()` cases?
// XXX: Allow issues to be dropped?
// XXX: Mark files modified/track modification count?
// XXX: List full diff over multiple rounds?
// XXX: Allow submission of a custom instruction.
@Command(name = "")
@NotThreadSafe
final class InteractiveBuildOutputProcessor {
private final OpenAi openAi;
private final PrintWriter out;
private final Supplier<ImmutableSet<Issue<Path>>> issueSupplier;
private ImmutableList<FileIssues> files = ImmutableList.of();
private int currentIndex = 0;
InteractiveBuildOutputProcessor(
OpenAi openAi, PrintWriter output, Supplier<ImmutableSet<Issue<Path>>> issueSupplier) {
this.openAi = openAi;
this.out = output;
this.issueSupplier = issueSupplier;
}
// XXX: Replace `Supplier<ImmutableSet<Issue<Path>>>` with a custom type that exposes the issue
// source and can be configured?
public static void run(OpenAi openAi, Supplier<ImmutableSet<Issue<Path>>> issueSupplier) {
try (Terminal terminal = TerminalBuilder.terminal()) {
InteractiveBuildOutputProcessor processor =
new InteractiveBuildOutputProcessor(openAi, terminal.writer(), issueSupplier);
PicocliCommandsFactory factory = new PicocliCommandsFactory();
factory.setTerminal(terminal);
CommandLine commandLine = new CommandLine(processor, factory);
PicocliCommands commands = new PicocliCommands(commandLine);
Parser parser = new DefaultParser();
SystemRegistry systemRegistry =
new SystemRegistryImpl(parser, terminal, () -> Path.of("").toAbsolutePath(), null);
systemRegistry.setCommandRegistries(commands);
systemRegistry.register("help", commands);
LineReader reader =
LineReaderBuilder.builder()
.terminal(terminal)
.completer(systemRegistry.completer())
.parser(parser)
.build();
new TailTipWidgets(
reader, systemRegistry::commandDescription, 5, TailTipWidgets.TipType.COMPLETER)
.enable();
reader.getKeyMaps().get("main").bind(new Reference("tailtip-toggle"), KeyMap.ctrl('t'));
processor.restart();
while (true) {
try {
systemRegistry.cleanUp();
systemRegistry.execute(
reader.readLine(processor.prompt(), null, (MaskingCallback) null, null));
} catch (UserInterruptException e) {
/* User pressed Ctrl+C. */
} catch (EndOfFileException e) {
/* User pressed Ctrl+D. */
return;
} catch (Exception e) {
systemRegistry.trace(e);
}
}
} catch (IOException e) {
throw new UncheckedIOException("Failed to create terminal", e);
}
}
@Command(aliases = "r", subcommands = HelpCommand.class, description = "Restarts issue analysis.")
void restart() {
files =
Multimaps.asMap(Multimaps.index(issueSupplier.get(), Issue::file)).values().stream()
.map(fileIssues -> FileIssues.of(ImmutableList.copyOf(fileIssues)))
.collect(toImmutableList());
currentIndex = 0;
issues();
}
@Command(aliases = "i", subcommands = HelpCommand.class, description = "List issues.")
void issues() {
if (files.isEmpty()) {
out.println(ansi().fgRed().a("No issues.").reset());
return;
}
renderIssueDetails(files.get(currentIndex));
}
// XXX: Review `throws` clause.
// XXX: Allow to submit a custom instruction.
@Command(aliases = "s", subcommands = HelpCommand.class, description = "Submit issues to OpenAI.")
void submit(
@Parameters(description = "The subset of issues to submit (default: all)") @Nullable
Set<Integer> issueNumbers)
throws IOException {
if (files.isEmpty()) {
out.println(ansi().fgRed().a("No issues.").reset());
return;
}
FileIssues fileIssues = files.get(currentIndex);
if (issueNumbers != null) {
for (int i : issueNumbers) {
if (i <= 0 || i > fileIssues.issues().size()) {
out.println(ansi().fgRed().a("Invalid issue number: " + i).reset());
return;
}
}
}
ImmutableList<Issue<Path>> allIssues = fileIssues.issues();
ImmutableList<Issue<Path>> selectedIssues =
issueNumbers == null
? allIssues
: issueNumbers.stream().map(n -> allIssues.get(n - 1)).collect(toImmutableList());
// XXX: Use `ansi()` and a separate thread to show a spinner.
out.println("Submitting issue(s) OpenAI...");
String originalCode = Files.readString(fileIssues.file());
String instruction =
Streams.mapWithIndex(
selectedIssues.stream(),
(description, index) -> String.format("%s. %s", index + 1, description))
.collect(joining("\n", "Resolve the following issues:\n", "\n"));
String result = openAi.requestEdit(originalCode, instruction);
fileIssues.setProposal(result);
Diffs.printUnifiedDiff(originalCode, result, fileIssues.relativeFile(), out);
}
@Command(
aliases = "a",
subcommands = HelpCommand.class,
description = "Apply the changes suggested by OpenAI")
void apply() {
if (files.isEmpty()) {
out.println(ansi().fgRed().a("No issues.").reset());
return;
}
FileIssues fileIssues = files.get(currentIndex);
fileIssues
.proposal()
.ifPresentOrElse(
proposal -> {
try {
// XXX: Apply the result only if it applies cleanly! Consider using the diff.
Files.writeString(fileIssues.file(), proposal);
out.println(ansi().fgGreen().a("Applied changes.").reset());
} catch (IOException e) {
out.println(ansi().fgRed().a("Failed to apply changes"));
e.printStackTrace(out);
out.print(ansi().reset());
}
},
() ->
out.println(
ansi().fgRed().a("No changes generated yet; run `submit` first.").reset()));
next();
}
@Command(aliases = "n", subcommands = HelpCommand.class, description = "Move to the next issue.")
void next() {
if (currentIndex < files.size() - 1) {
currentIndex++;
issues();
} else {
out.println("No next issue.");
}
}
@Command(
aliases = "p",
subcommands = HelpCommand.class,
description = "Move to the previous issue.")
void previous() {
if (currentIndex > 0) {
currentIndex--;
issues();
} else {
out.println("No previous issue.");
}
}
String prompt() {
if (files.isEmpty()) {
return ansi().fgRed().a("No issues").reset().a('>').toString();
}
return ansi()
.fgCyan()
.a(files.get(currentIndex).relativeFile())
.reset()
.a(" (")
.bold()
.a(currentIndex + 1)
.a('/')
.a(files.size())
.boldOff()
.a(")>")
.toString();
}
private void renderIssueDetails(FileIssues fileIssues) {
out.println(ansi().a("Issues for ").fgCyan().a(fileIssues.relativeFile()).reset().a(':'));
renderIssueContext(fileIssues);
renderIssues(fileIssues);
// XXX: Here, also list the currently suggested patch, if already generated.
// (...and not yet submitted?)
}
private void renderIssueContext(FileIssues fileIssues) {
ImmutableMap<Integer, ImmutableSet<Integer>> issueLines =
fileIssues.issues().stream()
.filter(issue -> issue.line().isPresent())
.collect(
toImmutableMap(
issue -> issue.line().getAsInt(),
issue ->
issue.column().isPresent()
? ImmutableSet.of(issue.column().getAsInt())
: ImmutableSet.of(),
(a, b) -> ImmutableSet.<Integer>builder().addAll(a).addAll(b).build()));
// XXX: Make context configurable.
// XXX: This would be nicer with a `RangeSet`, but then we'd hit
// https://github.com/google/guava/issues/3033.
ImmutableSet<Integer> ranges =
issueLines.keySet().stream()
.flatMap(line -> IntStream.range(line - 3, line + 4).boxed())
.collect(toImmutableSet());
boolean printedCode = false;
try {
List<String> lines = Files.readAllLines(fileIssues.file(), UTF_8);
for (int i = 1; i <= lines.size(); i++) {
int salience =
(ranges.contains(i - 1) ? 1 : 0)
+ (ranges.contains(i) ? 1 : 0)
+ (ranges.contains(i + 1) ? 1 : 0);
if (salience > 1) {
String line = lines.get(i - 1);
out.print(ansi().fgYellow().a(String.format(Locale.ROOT, "%4d: ", i)).reset());
out.println(
issueLines.containsKey(i) ? highlightIssueLine(line, issueLines.get(i)) : line);
printedCode = true;
} else if (salience > 0 && printedCode) {
out.println(ansi().fgBlue().a(".....").reset());
printedCode = false;
}
}
} catch (IOException e) {
// XXX: Review.
throw new UncheckedIOException("Failed to read file", e);
}
}
private static Ansi highlightIssueLine(String line, ImmutableSet<Integer> positions) {
Ansi ansi = ansi().fgRed();
for (int i = 0; i < line.length(); i++) {
if (positions.contains(i + 1)) {
ansi.bold().a(line.charAt(i)).boldOff();
} else {
ansi.a(line.charAt(i));
}
}
return ansi.reset();
}
private void renderIssues(FileIssues fileIssues) {
ImmutableList<Issue<Path>> issues = fileIssues.issues();
for (int i = 0; i < issues.size(); i++) {
out.println(
ansi()
.fgBlue()
.format(String.format(Locale.ROOT, "%4d. ", i + 1))
.reset()
.a(issues.get(i).description()));
}
}
private static final class FileIssues {
private final Path file;
private final ImmutableList<Issue<Path>> issues;
private Optional<String> proposal = Optional.empty();
FileIssues(Path file, ImmutableList<Issue<Path>> issues) {
this.file = file;
this.issues = issues;
checkArgument(
issues.stream().allMatch(issue -> issue.file().equals(file)),
"Issues must all reference the same file");
}
static FileIssues of(ImmutableList<Issue<Path>> issues) {
return new FileIssues(
issues.stream()
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No issues provided"))
.file(),
ImmutableList.sortedCopyOf(
comparingInt((Issue<Path> issue) -> issue.line().orElse(-1))
.thenComparingInt(issue -> issue.column().orElse(-1)),
issues));
}
Path file() {
return file;
}
Path relativeFile() {
return file.getFileSystem().getPath("").toAbsolutePath().relativize(file);
}
ImmutableList<Issue<Path>> issues() {
return issues;
}
void setProposal(String proposal) {
this.proposal = Optional.of(proposal);
}
Optional<String> proposal() {
return proposal;
}
}
}

View File

@@ -0,0 +1,35 @@
package tech.picnic.errorprone.openai;
import java.util.OptionalInt;
import java.util.stream.Stream;
/**
* Interface of types that implement a procedure to extract {@link Issue}s from a given string.
*
* @param <F> The type used to describe the location of files against which an issue is reported.
*/
@FunctionalInterface
interface IssueExtractor<F> {
/** Extracts zero or more {@link Issue}s from the given string. */
Stream<Issue<F>> extract(String str);
// XXX: Move to separate file?
record Issue<F>(F file, OptionalInt line, OptionalInt column, String message) {
<T> Issue<T> withFile(T file) {
return new Issue<>(file, line, column, message);
}
Issue<F> withMessage(String message) {
return new Issue<>(file, line, column, message);
}
String description() {
return line().isEmpty()
? message()
: column().isEmpty()
? String.format("Line %s: %s", line().getAsInt(), message())
: String.format(
"Line %s, column %s: %s", line().getAsInt(), column().getAsInt(), message());
}
}
}

View File

@@ -0,0 +1,79 @@
package tech.picnic.errorprone.openai;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.WillClose;
// XXX: Document that this is about extracting multi-line log messages.
// XXX: Some Maven plugins emit an `INFO` message before printing a sequence of WARN/ERROR messages.
// As-is this class will drop the leading message and chop up the rest. We should fix this.
final class LogLineExtractor {
// XXX: document that this much match an issue log level as well.
private final Pattern logLineStartMarker;
private final ImmutableSet<String> issueLogLevels;
private LogLineExtractor(Pattern logLineStartMarker, ImmutableSet<String> issueLogLevels) {
this.logLineStartMarker = logLineStartMarker;
this.issueLogLevels = issueLogLevels;
}
static LogLineExtractor mavenErrorAndWarningExtractor() {
// XXX: Move the pattern to a constant.
return new LogLineExtractor(
Pattern.compile("^\\[([A-Z]+)\\] "), ImmutableSet.of("ERROR", "WARNING"));
}
ImmutableList<String> extract(@WillClose InputStream logs) throws IOException {
List<String> messages = new ArrayList<>();
boolean shouldRead = false;
StringBuilder nextMessage = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(logs, UTF_8))) {
for (String line = br.readLine(); line != null; line = br.readLine()) {
Optional<String> logLevel = getLogLevel(line);
if (logLevel.isPresent()) {
if (!nextMessage.isEmpty()) {
messages.add(nextMessage.toString());
nextMessage.setLength(0);
}
shouldRead = issueLogLevels.contains(logLevel.orElseThrow());
}
if (shouldRead) {
if (!nextMessage.isEmpty()) {
nextMessage.append(System.lineSeparator());
}
// XXX: This `+ 3` is hacky. Do better.
nextMessage.append(
logLevel.isPresent() ? line.substring(logLevel.orElseThrow().length() + 3) : line);
}
}
}
if (shouldRead && !nextMessage.isEmpty()) {
messages.add(nextMessage.toString());
}
return ImmutableList.copyOf(messages);
}
private Optional<String> getLogLevel(String logLine) {
return Optional.of(logLineStartMarker.matcher(logLine))
.filter(Matcher::find)
.map(m -> m.group(1));
}
}

View File

@@ -0,0 +1,26 @@
package tech.picnic.errorprone.openai;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
* An {@link IssueExtractor} that recognizes Checkstyle violations as reported by the Maven
* CheckStyle Plugin
*
* @see <a
* href="https://github.com/apache/maven-checkstyle-plugin/blob/997f1e4148ae6c0b399ed2a306a5cc8b365083b8/src/main/java/org/apache/maven/plugins/checkstyle/CheckstyleViolationCheckMojo.java#L728-L735">Maven
* CheckStyle Plugin message format</a>
*/
final class MavenCheckstyleIssueExtractor implements IssueExtractor<String> {
private static final Pattern LOG_LINE_FORMAT =
Pattern.compile(
"^(?<file>.+?):\\[(?<line>\\d+)(?:,(?<column>\\d+))?\\] \\(.+?\\) (?<message>\\S+?: .+)$",
Pattern.DOTALL);
private final IssueExtractor<String> delegate = new RegexIssueExtractor(LOG_LINE_FORMAT);
@Override
public Stream<Issue<String>> extract(String str) {
return delegate.extract(str);
}
}

View File

@@ -0,0 +1,117 @@
package tech.picnic.errorprone.openai;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.theokanning.openai.completion.chat.ChatCompletionRequest;
import com.theokanning.openai.completion.chat.ChatMessage;
import com.theokanning.openai.service.OpenAiService;
import java.time.Duration;
/** A class that exposes a number of high-level OpenAI-based operations. */
// XXX: Make final.
// XXX: Implement exponential backoff.
public final class OpenAi implements AutoCloseable {
// XXX: Rename.
@VisibleForTesting static final String OPENAI_TOKEN_VARIABLE = "openapi_token";
// XXX: Make configurable?
private static final Duration OPENAI_API_TIMEOUT = Duration.ofSeconds(120);
private final OpenAiService openAiService;
private OpenAi(OpenAiService openAiService) {
this.openAiService = openAiService;
}
static OpenAi create() {
String openApiToken = System.getenv(OPENAI_TOKEN_VARIABLE);
checkState(openApiToken != null, "Environment variable '%s' not set", OPENAI_TOKEN_VARIABLE);
return create(openApiToken);
}
static OpenAi create(String openAiToken) {
return new OpenAi(new OpenAiService(openAiToken, OPENAI_API_TIMEOUT));
}
// XXX: Support multiple alternatives?
// XXX: Improve error handling (catch `OpenAiHttpException`, expose its message and the backing
// HTTP error code).
String requestEdit(String input, String instruction) {
// return openAiService
// .createEdit(
// EditRequest.builder()
// .input(input)
// .model("code-davinci-edit-001")
// .instruction(instruction)
// .temperature(0.0)
// .build())
// .getChoices()
// .get(0)
// .getText();
// XXX: Replace the `replace` hacks.
// XXX: The method signature is more generic than what this code does.
return openAiService
.createChatCompletion(
ChatCompletionRequest.builder()
.messages(
ImmutableList.of(
new ChatMessage(
"system",
"""
You are an expert Java developer. You can only respond with Java code.
The user reports build issues, and you suggest solutions.
"""),
new ChatMessage(
"user",
"""
This is my code:
XXX
These are my build errors:
YYY
###
Please update the code to fix these errors. Requirements:
- Just write the new code. Don't explain yourself.
- Do not leave out unchanged parts of the code. So do not shorten your answer by including "X remains unchanged" lines.
- Do not leave out or resolve code comments.
- Omit Markdown code formatting.
"""
.replace("XXX", input)
.replace("YYY", instruction))))
.model("gpt-4-1106-preview")
.temperature(0.0)
.build())
.getChoices()
.get(0)
.getMessage()
.getContent();
}
// XXX: Improve error handling, including checking the finish reason.
String requestChatCompletion(String instruction) {
return openAiService
.createChatCompletion(
ChatCompletionRequest.builder()
.messages(
ImmutableList.of(
new ChatMessage("system", "You are an expert Java developer"),
new ChatMessage("user", instruction)))
.model("gpt-4-1106-preview")
.temperature(0.0)
.build())
.getChoices()
.get(0)
.getMessage()
.getContent();
}
@Override
public void close() {
openAiService.shutdownExecutor();
}
}

View File

@@ -0,0 +1,58 @@
package tech.picnic.errorprone.openai;
import com.google.common.collect.Iterables;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.FileSystem;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
final class PathFinder {
private final FileSystem fileSystem;
private final Path projectRoot;
PathFinder(FileSystem fileSystem, Path projectRoot) {
this.fileSystem = fileSystem;
this.projectRoot = projectRoot.toAbsolutePath();
}
Optional<Path> findPath(String pathSuffix) {
Path path = projectRoot.resolve(pathSuffix);
if (Files.exists(path)) {
return Optional.of(path);
}
PathMatcher matcher = fileSystem.getPathMatcher("glob:**" + pathSuffix);
List<Path> inexactMatches = new ArrayList<>();
try {
Files.walkFileTree(
projectRoot,
new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (matcher.matches(file)) {
inexactMatches.add(file);
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
throw new UncheckedIOException("File walk failure", e);
}
// XXX: Log if not exactly one match? Could use SLF4J + SimpleLogger on the exec:java classpath.
// XXX: Alternatively, if this code is absorbed int `PathResolvingIssueExtractor`, then perhaps
// this issue could be reflected in the returned `Issue`. (How is TBD.)
return Optional.of(inexactMatches)
.filter(matches -> matches.size() == 1)
.map(Iterables::getOnlyElement);
}
}

View File

@@ -0,0 +1,29 @@
package tech.picnic.errorprone.openai;
import java.nio.file.Path;
import java.util.stream.Stream;
/**
* An {@link IssueExtractor} that resolves delegates to another {@link IssueExtractor} and resolves
* the extracted file paths.
*
* <p>For example, if the delegate extracts issues with relative file paths, this extractor will
* resolve those paths to absolute paths.
*/
final class PathResolvingIssueExtractor implements IssueExtractor<Path> {
// XXX: Do we then need `PathFinder` to be a separate class?
private final PathFinder pathFinder;
private final IssueExtractor<String> delegate;
PathResolvingIssueExtractor(PathFinder pathFinder, IssueExtractor<String> delegate) {
this.pathFinder = pathFinder;
this.delegate = delegate;
}
@Override
public Stream<Issue<Path>> extract(String str) {
return delegate
.extract(str)
.flatMap(issue -> pathFinder.findPath(issue.file()).map(issue::withFile).stream());
}
}

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