mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 08:11:25 +00:00
Compare commits
82 Commits
sschroever
...
sschroever
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbef169556 | ||
|
|
b8e22ffef0 | ||
|
|
17035a1623 | ||
|
|
e7dacd19d7 | ||
|
|
e64af1dde0 | ||
|
|
a58630bccf | ||
|
|
9ab5bbe042 | ||
|
|
4ca75c6cf6 | ||
|
|
7883b31eb6 | ||
|
|
ef751ce785 | ||
|
|
130c3d0bc3 | ||
|
|
c89e3905bf | ||
|
|
21421ce753 | ||
|
|
c39d1251d2 | ||
|
|
9bc732b4fe | ||
|
|
74100b6c41 | ||
|
|
624f2ce753 | ||
|
|
967017eed9 | ||
|
|
b06945b833 | ||
|
|
ef562c1644 | ||
|
|
efbde936dc | ||
|
|
c57653dd5b | ||
|
|
12b03e95b1 | ||
|
|
c2f24ac739 | ||
|
|
4f5ea8beac | ||
|
|
21646ffcb1 | ||
|
|
c58dceb9df | ||
|
|
90ef2f4042 | ||
|
|
459a498d6c | ||
|
|
78035644dc | ||
|
|
ef67d41512 | ||
|
|
4cecff923a | ||
|
|
38a57db994 | ||
|
|
336557cf8e | ||
|
|
9055dfff19 | ||
|
|
3712a15195 | ||
|
|
9d487e4a88 | ||
|
|
b2b086761c | ||
|
|
ff64247b6d | ||
|
|
bc7443c72d | ||
|
|
abf4d68fba | ||
|
|
5c5f7d849e | ||
|
|
65c4694936 | ||
|
|
a45291c7d8 | ||
|
|
71012f31ab | ||
|
|
6e0905c033 | ||
|
|
af5ac85428 | ||
|
|
0329c25f78 | ||
|
|
9e67e2b795 | ||
|
|
4bafea05f4 | ||
|
|
8ce9cab2dd | ||
|
|
ae30625524 | ||
|
|
dc0046ebfc | ||
|
|
ad6d774818 | ||
|
|
405f5874ac | ||
|
|
bf5199ea3d | ||
|
|
c500516bb4 | ||
|
|
85d68a4f34 | ||
|
|
46467951dd | ||
|
|
6f7ce2067f | ||
|
|
f30ba36d18 | ||
|
|
65736ce83f | ||
|
|
17d5805d5a | ||
|
|
ce01b62832 | ||
|
|
4ee555c62b | ||
|
|
a24bbbe99d | ||
|
|
679e83bc48 | ||
|
|
0bdc171613 | ||
|
|
9b05ec62c6 | ||
|
|
72866183f5 | ||
|
|
9d7f569be5 | ||
|
|
f72adca292 | ||
|
|
5148143ae5 | ||
|
|
3d7fbbf7a2 | ||
|
|
8b6864d8a0 | ||
|
|
a03017b5e6 | ||
|
|
a227436f19 | ||
|
|
3f6558b7c0 | ||
|
|
ce06396521 | ||
|
|
268766a32a | ||
|
|
5366effd74 | ||
|
|
0a63361500 |
37
.github/workflows/build.yaml
vendored
Normal file
37
.github/workflows/build.yaml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Build and verify
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
jdk: [ 11.0.16, 17.0.4 ]
|
||||
steps:
|
||||
# We run the build twice for each supported JDK: once against the
|
||||
# original Error Prone release, using only Error Prone checks available
|
||||
# on Maven Central, and once against the Picnic Error Prone fork,
|
||||
# additionally enabling all checks defined in this project and any
|
||||
# Error Prone checks available only from other artifact repositories.
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3.0.2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v3.4.1
|
||||
with:
|
||||
java-version: ${{ matrix.jdk }}
|
||||
distribution: temurin
|
||||
cache: maven
|
||||
- name: Display build environment details
|
||||
run: mvn --version
|
||||
- name: Build project against vanilla Error Prone
|
||||
run: mvn -T1C install
|
||||
- name: Build project with self-check against Error Prone fork
|
||||
run: mvn -T1C clean verify -Perror-prone-fork -Pnon-maven-central -Pself-check -s settings.xml
|
||||
- name: Remove installed project artifacts
|
||||
run: mvn build-helper:remove-project-artifact
|
||||
|
||||
# XXX: Enable Codecov once we "go public".
|
||||
# XXX: Enable SonarCloud once we "go public".
|
||||
@@ -1,3 +1,6 @@
|
||||
-XX:ReservedCodeCacheSize=512m
|
||||
-XX:SoftRefLRUPolicyMSPerMB=10
|
||||
-XX:+UseParallelGC
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
|
||||
--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
|
||||
|
||||
1
.mvn/maven.config
Normal file
1
.mvn/maven.config
Normal file
@@ -0,0 +1 @@
|
||||
--batch-mode --errors --strict-checksums
|
||||
22
.renovaterc.json
Normal file
22
.renovaterc.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"packageRules": [
|
||||
{
|
||||
"matchPackagePatterns": [
|
||||
"^org\\.springframework:spring-framework-bom$",
|
||||
"^org\\.springframework\\.boot:spring-boot[a-z-]*$"
|
||||
],
|
||||
"separateMinorPatch": true
|
||||
},
|
||||
{
|
||||
"matchPackagePatterns": [
|
||||
"^com\\.palantir\\.baseline:baseline-error-prone$"
|
||||
],
|
||||
"schedule": "* * 1 * *"
|
||||
}
|
||||
],
|
||||
"reviewers": [
|
||||
"rickie",
|
||||
"Stephan202"
|
||||
]
|
||||
}
|
||||
38
.travis.yml
38
.travis.yml
@@ -1,38 +0,0 @@
|
||||
---
|
||||
dist: bionic
|
||||
language: java
|
||||
jdk: openjdk11
|
||||
addons:
|
||||
sonarcloud:
|
||||
organization: picnic-technologies
|
||||
token: "${SONARCLOUD_TOKEN}"
|
||||
install:
|
||||
- mvn io.takari:maven:wrapper
|
||||
script:
|
||||
# We run the build twice: once against the original Error Prone release,
|
||||
# using only Error Prone checks available on Maven Central, and once against
|
||||
# the Picnic Error Prone fork, additionally enabling all checks defined in
|
||||
# this project and any Error Prone checks available only from other artifact
|
||||
# repositories.
|
||||
- ./mvnw clean install
|
||||
- ./mvnw clean install -Perror-prone-fork -Pnon-maven-central -Pself-check -s settings.xml
|
||||
# XXX: Enable SonarCloud once we "go public".
|
||||
# ./mvnw jacoco:prepare-agent surefire:test jacoco:report sonar:sonar
|
||||
- ./mvnw jacoco:prepare-agent surefire:test jacoco:report
|
||||
before_cache:
|
||||
# Don't cache the artifacts we just generated, for multiple reasons: (1) we
|
||||
# shouldn't need them next time around and (2) if we do, that indicates a
|
||||
# dependency issue which might otherwise go unnoticed until next time we bump
|
||||
# the project's version (i.e., when tagging).
|
||||
- find "${HOME}/.m2/repository" -depth -name '*-SNAPSHOT' -exec rm -r '{}' \;
|
||||
cache:
|
||||
directories:
|
||||
# The local Maven repository in which third party dependencies are stored.
|
||||
- ${HOME}/.m2/repository
|
||||
# The Takari Maven Wrapper's storage for downloaded Maven distributions.
|
||||
- ${HOME}/.m2/wrapper
|
||||
# The SonarQube analysis cache.
|
||||
- ${HOME}/.sonar/cache
|
||||
# XXX: Enable Codecov once we "go public".
|
||||
#after_success:
|
||||
# - bash <(curl -s https://codecov.io/bash)
|
||||
25
apply-error-prone-suggestions.sh
Executable file
25
apply-error-prone-suggestions.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Compiles the code using Error Prone and applies its suggestions. The set of
|
||||
# checks applied can optionally be restricted by name.
|
||||
#
|
||||
# As this script may modify the project's code, it is important to execute it
|
||||
# in a clean Git working directory.
|
||||
|
||||
set -e -u -o pipefail
|
||||
|
||||
if [ "${#}" -gt 1 ]; then
|
||||
echo "Usage: ./$(basename "${0}") [PatchChecks]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
patchChecks=${1:-}
|
||||
|
||||
mvn clean test-compile fmt:format \
|
||||
-T 1.0C \
|
||||
-Perror-prone \
|
||||
-Perror-prone-fork \
|
||||
-Ppatch \
|
||||
-Pself-check \
|
||||
-Derror-prone.patch-checks="${patchChecks}" \
|
||||
-Dverification.skip
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>tech.picnic.error-prone-support</groupId>
|
||||
<artifactId>error-prone-support</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.1.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>error-prone-contrib</artifactId>
|
||||
@@ -41,15 +41,13 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>refaster-compiler</artifactId>
|
||||
<!-- This dependency is declared only as a hint to Maven that
|
||||
compilation depends on it; see the `maven-compiler-plugin`'s
|
||||
`annotationProcessorPaths` configuration below. -->
|
||||
<artifactId>refaster-support</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>refaster-support</artifactId>
|
||||
<artifactId>refaster-test-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
@@ -76,6 +74,10 @@
|
||||
<artifactId>javac</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.googlejavaformat</groupId>
|
||||
<artifactId>google-java-format</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
@@ -126,6 +128,11 @@
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
@@ -136,6 +143,11 @@
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.immutables</groupId>
|
||||
<artifactId>value-annotations</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
@@ -171,6 +183,11 @@
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webflux</artifactId>
|
||||
@@ -191,15 +208,6 @@
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.spotify.fmt</groupId>
|
||||
<artifactId>fmt-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<additionalSourceDirectories>
|
||||
<additionalSourceDirectory>${basedir}/src/test/resources</additionalSourceDirectory>
|
||||
</additionalSourceDirectories>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
|
||||
@@ -25,12 +25,11 @@ import javax.lang.model.element.AnnotationValue;
|
||||
/** A {@link BugChecker} which flags ambiguous {@code @JsonCreator}s in enums. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "AmbiguousJsonCreator",
|
||||
summary = "`JsonCreator.Mode` should be set for single-argument creators",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class AmbiguousJsonCreatorCheck extends BugChecker implements AnnotationTreeMatcher {
|
||||
public final class AmbiguousJsonCreator extends BugChecker implements AnnotationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<AnnotationTree> IS_JSON_CREATOR_ANNOTATION =
|
||||
isType("com.fasterxml.jackson.annotation.JsonCreator");
|
||||
@@ -0,0 +1,57 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
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 com.google.errorprone.matchers.Matchers.allOf;
|
||||
import static com.google.errorprone.matchers.Matchers.argument;
|
||||
import static com.google.errorprone.matchers.Matchers.argumentCount;
|
||||
import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.Matchers.nullLiteral;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags AssertJ {@code isEqualTo(null)} checks for simplification.
|
||||
*
|
||||
* <p>This bug checker cannot be replaced with a simple Refaster template, as the Refaster approach
|
||||
* would require that all overloads of {@link org.assertj.core.api.Assert#isEqualTo(Object)} (such
|
||||
* as {@link org.assertj.core.api.AbstractStringAssert#isEqualTo(String)}) are explicitly
|
||||
* enumerated. This bug checker generically matches all such current and future overloads.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Prefer `.isNull()` over `.isEqualTo(null)`",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class AssertJIsNull extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<MethodInvocationTree> ASSERT_IS_EQUAL_TO_NULL =
|
||||
allOf(
|
||||
instanceMethod().onDescendantOf("org.assertj.core.api.Assert").named("isEqualTo"),
|
||||
argumentCount(1),
|
||||
argument(0, nullLiteral()));
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
if (!ASSERT_IS_EQUAL_TO_NULL.matches(tree, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
SuggestedFix.Builder fix =
|
||||
SuggestedFix.builder().merge(SuggestedFixes.renameMethodInvocation(tree, "isNull", state));
|
||||
tree.getArguments().forEach(arg -> fix.merge(SuggestedFix.delete(arg)));
|
||||
|
||||
return describeMatch(tree, fix.build());
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import static com.google.errorprone.matchers.Matchers.annotations;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
@@ -26,12 +27,11 @@ import java.util.List;
|
||||
/** A {@link BugChecker} which flags redundant {@code @Autowired} constructor annotations. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "AutowiredConstructor",
|
||||
summary = "Omit `@Autowired` on a class' sole constructor, as it is redundant",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class AutowiredConstructorCheck extends BugChecker implements ClassTreeMatcher {
|
||||
public final class AutowiredConstructor extends BugChecker implements ClassTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final MultiMatcher<Tree, AnnotationTree> AUTOWIRED_ANNOTATION =
|
||||
annotations(AT_LEAST_ONE, isType("org.springframework.beans.factory.annotation.Autowired"));
|
||||
@@ -43,7 +43,7 @@ public final class AutowiredConstructorCheck extends BugChecker implements Class
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
List<AnnotationTree> annotations =
|
||||
ImmutableList<AnnotationTree> annotations =
|
||||
AUTOWIRED_ANNOTATION
|
||||
.multiMatchResult(Iterables.getOnlyElement(constructors), state)
|
||||
.matchingNodes();
|
||||
@@ -25,25 +25,24 @@ import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} which flags annotations that could be written more concisely. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "CanonicalAnnotationSyntax",
|
||||
summary = "Omit redundant syntax from annotation declarations",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class CanonicalAnnotationSyntaxCheck extends BugChecker
|
||||
implements AnnotationTreeMatcher {
|
||||
public final class CanonicalAnnotationSyntax extends BugChecker implements AnnotationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Pattern TRAILING_ARRAY_COMMA = Pattern.compile(",\\s*}$");
|
||||
private static final ImmutableSet<BiFunction<AnnotationTree, VisitorState, Optional<Fix>>>
|
||||
FIX_FACTORIES =
|
||||
ImmutableSet.of(
|
||||
CanonicalAnnotationSyntaxCheck::dropRedundantParentheses,
|
||||
CanonicalAnnotationSyntaxCheck::dropRedundantValueAttribute,
|
||||
CanonicalAnnotationSyntaxCheck::dropRedundantCurlies);
|
||||
CanonicalAnnotationSyntax::dropRedundantParentheses,
|
||||
CanonicalAnnotationSyntax::dropRedundantValueAttribute,
|
||||
CanonicalAnnotationSyntax::dropRedundantCurlies);
|
||||
|
||||
@Override
|
||||
public Description matchAnnotation(AnnotationTree tree, VisitorState state) {
|
||||
@@ -103,7 +102,8 @@ public final class CanonicalAnnotationSyntaxCheck extends BugChecker
|
||||
return Optional.of(
|
||||
SuggestedFix.replace(
|
||||
arg,
|
||||
simplifyAttributeValue(expr, state).orElseGet(() -> Util.treeToString(expr, state))));
|
||||
simplifyAttributeValue(expr, state)
|
||||
.orElseGet(() -> SourceCode.treeToString(expr, state))));
|
||||
}
|
||||
|
||||
private static Optional<Fix> dropRedundantCurlies(AnnotationTree tree, VisitorState state) {
|
||||
@@ -138,11 +138,11 @@ public final class CanonicalAnnotationSyntaxCheck extends BugChecker
|
||||
private static Optional<String> simplifySingletonArray(NewArrayTree array, VisitorState state) {
|
||||
return Optional.of(array.getInitializers())
|
||||
.filter(initializers -> initializers.size() == 1)
|
||||
.map(initializers -> Util.treeToString(initializers.get(0), state));
|
||||
.map(initializers -> SourceCode.treeToString(initializers.get(0), state));
|
||||
}
|
||||
|
||||
private static Optional<String> dropTrailingComma(NewArrayTree array, VisitorState state) {
|
||||
String src = Util.treeToString(array, state);
|
||||
String src = SourceCode.treeToString(array, state);
|
||||
return Optional.of(TRAILING_ARRAY_COMMA.matcher(src))
|
||||
.filter(Matcher::find)
|
||||
.map(m -> src.substring(0, m.start()) + '}');
|
||||
@@ -0,0 +1,117 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.FRAGILE_CODE;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import java.util.stream.Collector;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags {@link Collector Collectors} that don't clearly express
|
||||
* (im)mutability.
|
||||
*
|
||||
* <p>Replacing such collectors with alternatives that produce immutable collections is preferred.
|
||||
* Do note that Guava's immutable collections are null-hostile.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"Avoid `Collectors.to{List,Map,Set}` in favour of alternatives that emphasize (im)mutability",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = FRAGILE_CODE)
|
||||
public final class CollectorMutability extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> COLLECTOR_METHOD =
|
||||
staticMethod().onClass("java.util.stream.Collectors");
|
||||
private static final Matcher<ExpressionTree> LIST_COLLECTOR =
|
||||
staticMethod().anyClass().named("toList");
|
||||
private static final Matcher<ExpressionTree> MAP_COLLECTOR =
|
||||
staticMethod().anyClass().named("toMap");
|
||||
private static final Matcher<ExpressionTree> SET_COLLECTOR =
|
||||
staticMethod().anyClass().named("toSet");
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
if (!COLLECTOR_METHOD.matches(tree, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
if (LIST_COLLECTOR.matches(tree, state)) {
|
||||
return suggestToCollectionAlternatives(
|
||||
tree, "com.google.common.collect.ImmutableList.toImmutableList", "ArrayList", state);
|
||||
}
|
||||
|
||||
if (MAP_COLLECTOR.matches(tree, state)) {
|
||||
return suggestToMapAlternatives(tree, state);
|
||||
}
|
||||
|
||||
if (SET_COLLECTOR.matches(tree, state)) {
|
||||
return suggestToCollectionAlternatives(
|
||||
tree, "com.google.common.collect.ImmutableSet.toImmutableSet", "HashSet", state);
|
||||
}
|
||||
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
private Description suggestToCollectionAlternatives(
|
||||
MethodInvocationTree tree,
|
||||
String fullyQualifiedImmutableReplacement,
|
||||
String mutableReplacement,
|
||||
VisitorState state) {
|
||||
SuggestedFix.Builder mutableFix = SuggestedFix.builder();
|
||||
String toCollectionSelect =
|
||||
SuggestedFixes.qualifyStaticImport(
|
||||
"java.util.stream.Collectors.toCollection", mutableFix, state);
|
||||
|
||||
return buildDescription(tree)
|
||||
.addFix(replaceMethodInvocation(tree, fullyQualifiedImmutableReplacement, state))
|
||||
.addFix(
|
||||
mutableFix
|
||||
.addImport(String.format("java.util.%s", mutableReplacement))
|
||||
.replace(tree, String.format("%s(%s::new)", toCollectionSelect, mutableReplacement))
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
private Description suggestToMapAlternatives(MethodInvocationTree tree, VisitorState state) {
|
||||
int argCount = tree.getArguments().size();
|
||||
if (argCount > 3) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
return buildDescription(tree)
|
||||
.addFix(
|
||||
replaceMethodInvocation(
|
||||
tree, "com.google.common.collect.ImmutableMap.toImmutableMap", state))
|
||||
.addFix(
|
||||
SuggestedFix.builder()
|
||||
.addImport("java.util.HashMap")
|
||||
.postfixWith(
|
||||
tree.getArguments().get(argCount - 1),
|
||||
(argCount == 2 ? ", (a, b) -> { throw new IllegalStateException(); }" : "")
|
||||
+ ", HashMap::new")
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static SuggestedFix replaceMethodInvocation(
|
||||
MethodInvocationTree tree, String fullyQualifiedReplacement, VisitorState state) {
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder();
|
||||
String replacement = SuggestedFixes.qualifyStaticImport(fullyQualifiedReplacement, fix, state);
|
||||
fix.merge(SuggestedFix.replace(tree.getMethodSelect(), replacement));
|
||||
return fix.build();
|
||||
}
|
||||
}
|
||||
@@ -20,18 +20,16 @@ import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
||||
import java.util.Optional;
|
||||
|
||||
/** A {@link BugChecker} which flags empty methods that seemingly can simply be deleted. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "EmptyMethod",
|
||||
summary = "Empty method can likely be deleted",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class EmptyMethodCheck extends BugChecker implements MethodTreeMatcher {
|
||||
public final class EmptyMethod extends BugChecker implements MethodTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<Tree> PERMITTED_ANNOTATION =
|
||||
annotations(
|
||||
@@ -48,8 +46,7 @@ public final class EmptyMethodCheck extends BugChecker implements MethodTreeMatc
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
MethodSymbol sym = ASTHelpers.getSymbol(tree);
|
||||
if (sym == null || ASTHelpers.methodCanBeOverridden(sym)) {
|
||||
if (ASTHelpers.methodCanBeOverridden(ASTHelpers.getSymbol(tree))) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
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 java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.google.googlejavaformat.java.Formatter;
|
||||
import com.google.googlejavaformat.java.FormatterException;
|
||||
import com.google.googlejavaformat.java.ImportOrderer;
|
||||
import com.google.googlejavaformat.java.JavaFormatterOptions.Style;
|
||||
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 java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags improperly formatted Error Prone test code.
|
||||
*
|
||||
* <p>All test code should be formatted in accordance with Google Java Format's {@link Formatter}
|
||||
* output, and imports should be ordered according to the {@link Style#GOOGLE Google} style.
|
||||
*
|
||||
* <p>This checker inspects inline code passed to {@code
|
||||
* com.google.errorprone.CompilationTestHelper} and {@code
|
||||
* com.google.errorprone.BugCheckerRefactoringTestHelper}. It requires that this code is properly
|
||||
* formatted and that its imports are organized. Only code that represents the expected output of a
|
||||
* refactoring operation is allowed to have unused imports, as most {@link BugChecker}s do not (and
|
||||
* 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: 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.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Test code should follow the Google Java style",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = STYLE)
|
||||
public final class ErrorProneTestHelperSourceFormat extends BugChecker
|
||||
implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Formatter FORMATTER = new Formatter();
|
||||
private static final Matcher<ExpressionTree> INPUT_SOURCE_ACCEPTING_METHOD =
|
||||
anyOf(
|
||||
instanceMethod()
|
||||
.onDescendantOf("com.google.errorprone.CompilationTestHelper")
|
||||
.named("addSourceLines"),
|
||||
instanceMethod()
|
||||
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper")
|
||||
.named("addInputLines"));
|
||||
private static final Matcher<ExpressionTree> OUTPUT_SOURCE_ACCEPTING_METHOD =
|
||||
instanceMethod()
|
||||
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper.ExpectOutput")
|
||||
.named("addOutputLines");
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
boolean isOutputSource = OUTPUT_SOURCE_ACCEPTING_METHOD.matches(tree, state);
|
||||
if (!isOutputSource && !INPUT_SOURCE_ACCEPTING_METHOD.matches(tree, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
List<? extends ExpressionTree> sourceLines =
|
||||
tree.getArguments().subList(1, tree.getArguments().size());
|
||||
if (sourceLines.isEmpty()) {
|
||||
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))
|
||||
.orElse(Description.NO_MATCH);
|
||||
}
|
||||
|
||||
private Description flagFormattingIssues(
|
||||
int startPos, int endPos, String source, boolean retainUnusedImports, VisitorState state) {
|
||||
Tree methodInvocation = state.getPath().getLeaf();
|
||||
|
||||
String formatted;
|
||||
try {
|
||||
formatted = formatSourceCode(source, retainUnusedImports).trim();
|
||||
} catch (FormatterException e) {
|
||||
return buildDescription(methodInvocation)
|
||||
.setMessage(String.format("Source code is malformed: %s", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
|
||||
if (source.trim().equals(formatted)) {
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* The code isn't properly formatted; replace all lines with the properly formatted
|
||||
* alternatives.
|
||||
*/
|
||||
return describeMatch(
|
||||
methodInvocation,
|
||||
SuggestedFix.replace(
|
||||
startPos,
|
||||
endPos,
|
||||
Splitter.on('\n')
|
||||
.splitToStream(formatted)
|
||||
.map(state::getConstantExpression)
|
||||
.collect(joining(", "))));
|
||||
}
|
||||
|
||||
private static String formatSourceCode(String source, boolean retainUnusedImports)
|
||||
throws FormatterException {
|
||||
String withReorderedImports = ImportOrderer.reorderImports(source, Style.GOOGLE);
|
||||
String withOptionallyRemovedImports =
|
||||
retainUnusedImports
|
||||
? withReorderedImports
|
||||
: RemoveUnusedImports.removeUnusedImports(withReorderedImports);
|
||||
return FORMATTER.formatSource(withOptionallyRemovedImports);
|
||||
}
|
||||
|
||||
private static Optional<String> getConstantSourceCode(
|
||||
List<? extends ExpressionTree> sourceLines) {
|
||||
StringBuilder source = new StringBuilder();
|
||||
|
||||
for (ExpressionTree sourceLine : sourceLines) {
|
||||
Object value = ASTHelpers.constValue(sourceLine);
|
||||
if (value == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
source.append(value).append('\n');
|
||||
}
|
||||
|
||||
return Optional.of(source.toString());
|
||||
}
|
||||
}
|
||||
@@ -35,13 +35,11 @@ import java.util.stream.Stream;
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "ExplicitEnumOrdering",
|
||||
summary = "Make sure `Ordering#explicit` lists all of an enum's values",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = FRAGILE_CODE)
|
||||
public final class ExplicitEnumOrderingCheck extends BugChecker
|
||||
implements MethodInvocationTreeMatcher {
|
||||
public final class ExplicitEnumOrdering extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> EXPLICIT_ORDERING =
|
||||
staticMethod().onClass(Ordering.class.getName()).named("explicit");
|
||||
@@ -73,7 +71,7 @@ public final class ExplicitEnumOrderingCheck extends BugChecker
|
||||
.collect(
|
||||
collectingAndThen(
|
||||
toImmutableSetMultimap(Symbol::asType, Symbol::toString),
|
||||
ExplicitEnumOrderingCheck::getMissingEnumValues));
|
||||
ExplicitEnumOrdering::getMissingEnumValues));
|
||||
}
|
||||
|
||||
private static ImmutableSet<String> getMissingEnumValues(
|
||||
@@ -40,14 +40,13 @@ import reactor.core.publisher.Flux;
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "FluxFlatMapUsage",
|
||||
summary =
|
||||
"`Flux#flatMap` and `Flux#flatMapSequential` have subtle semantics; "
|
||||
+ "please use `Flux#concatMap` or explicitly specify the desired amount of concurrency",
|
||||
linkType = NONE,
|
||||
severity = ERROR,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class FluxFlatMapUsageCheck extends BugChecker
|
||||
public final class FluxFlatMapUsage extends BugChecker
|
||||
implements MethodInvocationTreeMatcher, MemberReferenceTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String MAX_CONCURRENCY_ARG_NAME = "MAX_CONCURRENCY";
|
||||
@@ -31,6 +31,8 @@ import com.sun.source.util.SimpleTreeVisitor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags string concatenations that produce a format string; in such
|
||||
@@ -48,12 +50,11 @@ import java.util.Optional;
|
||||
// invocations, as necessary.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "FormatStringConcatenation",
|
||||
summary = "Defer string concatenation to the invoked method",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class FormatStringConcatenationCheck extends BugChecker
|
||||
public final class FormatStringConcatenation extends BugChecker
|
||||
implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
/**
|
||||
@@ -210,6 +211,7 @@ public final class FormatStringConcatenationCheck extends BugChecker
|
||||
this.formatSpecifier = formatSpecifier;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Void visitBinary(BinaryTree tree, VisitorState state) {
|
||||
if (tree.getKind() == Kind.PLUS && isStringTyped(tree, state)) {
|
||||
@@ -222,11 +224,13 @@ public final class FormatStringConcatenationCheck extends BugChecker
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Void visitParenthesized(ParenthesizedTree tree, VisitorState state) {
|
||||
return tree.getExpression().accept(this, state);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Void defaultAction(Tree tree, VisitorState state) {
|
||||
appendExpression(tree);
|
||||
@@ -246,7 +250,7 @@ public final class FormatStringConcatenationCheck extends BugChecker
|
||||
return state.getConstantExpression(formatString.toString())
|
||||
+ ", "
|
||||
+ formatArguments.stream()
|
||||
.map(tree -> Util.treeToString(tree, state))
|
||||
.map(tree -> SourceCode.treeToString(tree, state))
|
||||
.collect(joining(", "));
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.code.Types;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} that flags redundant identity conversions. */
|
||||
// XXX: Consider detecting cases where a flagged expression is passed to a method, and where removal
|
||||
@@ -34,13 +35,11 @@ import java.util.List;
|
||||
// the target method such a modification may change the code's semantics or performance.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "IdentityConversion",
|
||||
summary = "Avoid or clarify identity conversions",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class IdentityConversionCheck extends BugChecker
|
||||
implements MethodInvocationTreeMatcher {
|
||||
public final class IdentityConversion extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> IS_CONVERSION_METHOD =
|
||||
anyOf(
|
||||
@@ -95,7 +94,7 @@ public final class IdentityConversionCheck extends BugChecker
|
||||
.setMessage(
|
||||
"This method invocation appears redundant; remove it or suppress this warning and "
|
||||
+ "add a comment explaining its purpose")
|
||||
.addFix(SuggestedFix.replace(tree, Util.treeToString(sourceTree, state)))
|
||||
.addFix(SuggestedFix.replace(tree, SourceCode.treeToString(sourceTree, state)))
|
||||
.addFix(SuggestedFixes.addSuppressWarnings(state, canonicalName()))
|
||||
.build();
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.LIKELY_ERROR;
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.enclosingClass;
|
||||
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
|
||||
import static com.google.errorprone.matchers.Matchers.hasModifier;
|
||||
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
|
||||
import static com.google.errorprone.matchers.Matchers.methodReturns;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.util.SortedSet;
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags {@link SortedSet} property declarations inside
|
||||
* {@code @Value.Immutable}- and {@code @Value.Modifiable}-annotated types that lack a
|
||||
* {@code @Value.NaturalOrder} or {@code @Value.ReverseOrder} annotation.
|
||||
*
|
||||
* <p>Without such an annotation:
|
||||
*
|
||||
* <ul>
|
||||
* <li>deserialization of the enclosing type requires that the associated JSON property is
|
||||
* present, contrary to the way in which Immutables handles other collection properties; and
|
||||
* <li>different instances may use different comparator implementations (e.g. deserialization
|
||||
* would default to natural order sorting), potentially leading to subtle bugs.
|
||||
* </ul>
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"`SortedSet` properties of a `@Value.Immutable` or `@Value.Modifiable` type must be "
|
||||
+ "annotated with `@Value.NaturalOrder` or `@Value.ReverseOrder`",
|
||||
linkType = NONE,
|
||||
severity = ERROR,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class ImmutablesSortedSetComparator extends BugChecker implements MethodTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<MethodTree> METHOD_LACKS_ANNOTATION =
|
||||
allOf(
|
||||
methodReturns(isSubtypeOf(SortedSet.class)),
|
||||
anyOf(
|
||||
allOf(
|
||||
hasModifier(Modifier.ABSTRACT),
|
||||
enclosingClass(
|
||||
anyOf(
|
||||
hasAnnotation("org.immutables.value.Value.Immutable"),
|
||||
hasAnnotation("org.immutables.value.Value.Modifiable")))),
|
||||
hasAnnotation("org.immutables.value.Value.Default")),
|
||||
not(
|
||||
anyOf(
|
||||
hasAnnotation("org.immutables.value.Value.NaturalOrder"),
|
||||
hasAnnotation("org.immutables.value.Value.ReverseOrder"))));
|
||||
|
||||
@Override
|
||||
public Description matchMethod(MethodTree tree, VisitorState state) {
|
||||
if (!METHOD_LACKS_ANNOTATION.matches(tree, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
SuggestedFix.Builder builder = SuggestedFix.builder();
|
||||
String valueTypeIdentifier =
|
||||
SuggestedFixes.qualifyType(state, builder, "org.immutables.value.Value");
|
||||
return describeMatch(
|
||||
tree,
|
||||
builder.prefixWith(tree, String.format("@%s.NaturalOrder ", valueTypeIdentifier)).build());
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import static com.google.errorprone.matchers.Matchers.enclosingClass;
|
||||
import static com.google.errorprone.matchers.Matchers.hasModifier;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static java.util.function.Predicate.not;
|
||||
import static tech.picnic.errorprone.bugpatterns.JavaKeywords.isReservedKeyword;
|
||||
import static tech.picnic.errorprone.bugpatterns.util.JavaKeywords.isReservedKeyword;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -36,21 +36,21 @@ import com.sun.tools.javac.code.Symbol;
|
||||
import java.util.Optional;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.Name;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} which flags non-canonical JUnit method declarations. */
|
||||
// XXX: Consider introducing a class-level check which enforces that test classes:
|
||||
// 1. Are named `*Test` or `Abstract*TestCase`.
|
||||
// 2. If not `abstract`, don't have public methods and subclasses.
|
||||
// 2. If not `abstract`, are package-private and don't have public methods and subclasses.
|
||||
// 3. Only have private fields.
|
||||
// XXX: If implemented, the current logic could flag only `private` JUnit methods.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "JUnitMethodDeclaration",
|
||||
summary = "JUnit method declaration can likely be improved",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class JUnitMethodDeclarationCheck extends BugChecker implements MethodTreeMatcher {
|
||||
public final class JUnitMethodDeclaration extends BugChecker implements MethodTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String TEST_PREFIX = "test";
|
||||
private static final ImmutableSet<Modifier> ILLEGAL_MODIFIERS =
|
||||
@@ -171,13 +171,12 @@ public final class JUnitMethodDeclarationCheck extends BugChecker implements Met
|
||||
}
|
||||
|
||||
private static CharSequence getStaticImportSimpleName(Tree tree, VisitorState state) {
|
||||
String source = Util.treeToString(tree, state);
|
||||
String source = SourceCode.treeToString(tree, state);
|
||||
return source.subSequence(source.lastIndexOf('.') + 1, source.length());
|
||||
}
|
||||
|
||||
private static Optional<String> tryCanonicalizeMethodName(MethodTree tree) {
|
||||
return Optional.ofNullable(ASTHelpers.getSymbol(tree))
|
||||
.map(sym -> sym.getQualifiedName().toString())
|
||||
return Optional.of(ASTHelpers.getSymbol(tree).getQualifiedName().toString())
|
||||
.filter(name -> name.startsWith(TEST_PREFIX))
|
||||
.map(name -> name.substring(TEST_PREFIX.length()))
|
||||
.filter(not(String::isEmpty))
|
||||
@@ -37,6 +37,9 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Nullable;
|
||||
import tech.picnic.errorprone.bugpatterns.util.AnnotationAttributeMatcher;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags annotation array listings which aren't sorted lexicographically.
|
||||
@@ -46,12 +49,11 @@ import java.util.stream.Stream;
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "LexicographicalAnnotationAttributeListing",
|
||||
summary = "Where possible, sort annotation array attributes lexicographically",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = STYLE)
|
||||
public final class LexicographicalAnnotationAttributeListingCheck extends BugChecker
|
||||
public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
implements AnnotationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final ImmutableSet<String> BLACKLISTED_ANNOTATIONS =
|
||||
@@ -60,24 +62,27 @@ public final class LexicographicalAnnotationAttributeListingCheck extends BugChe
|
||||
"com.fasterxml.jackson.annotation.JsonPropertyOrder#value",
|
||||
"io.swagger.annotations.ApiImplicitParams#value",
|
||||
"io.swagger.v3.oas.annotations.Parameters#value",
|
||||
"javax.xml.bind.annotation.XmlType#propOrder");
|
||||
"javax.xml.bind.annotation.XmlType#propOrder",
|
||||
"org.springframework.context.annotation.PropertySource#value",
|
||||
"org.springframework.test.context.TestPropertySource#locations",
|
||||
"org.springframework.test.context.TestPropertySource#value");
|
||||
private static final String FLAG_PREFIX = "LexicographicalAnnotationAttributeListing:";
|
||||
private static final String INCLUDED_ANNOTATIONS_FLAG = FLAG_PREFIX + "Includes";
|
||||
private static final String EXCLUDED_ANNOTATIONS_FLAG = FLAG_PREFIX + "Excludes";
|
||||
|
||||
private final AnnotationAttributeMatcher matcher;
|
||||
|
||||
/** Instantiates the default {@link LexicographicalAnnotationAttributeListingCheck}. */
|
||||
public LexicographicalAnnotationAttributeListingCheck() {
|
||||
/** Instantiates the default {@link LexicographicalAnnotationAttributeListing}. */
|
||||
public LexicographicalAnnotationAttributeListing() {
|
||||
this(ErrorProneFlags.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a customized {@link LexicographicalAnnotationAttributeListingCheck}.
|
||||
* Instantiates a customized {@link LexicographicalAnnotationAttributeListing}.
|
||||
*
|
||||
* @param flags Any provided command line flags.
|
||||
*/
|
||||
public LexicographicalAnnotationAttributeListingCheck(ErrorProneFlags flags) {
|
||||
public LexicographicalAnnotationAttributeListing(ErrorProneFlags flags) {
|
||||
matcher = createAnnotationAttributeMatcher(flags);
|
||||
}
|
||||
|
||||
@@ -128,7 +133,7 @@ public final class LexicographicalAnnotationAttributeListingCheck extends BugChe
|
||||
/* The elements aren't sorted. Suggest the sorted alternative. */
|
||||
String suggestion =
|
||||
desiredOrdering.stream()
|
||||
.map(expr -> Util.treeToString(expr, state))
|
||||
.map(expr -> SourceCode.treeToString(expr, state))
|
||||
.collect(joining(", ", "{", "}"));
|
||||
return Optional.of(SuggestedFix.builder().replace(array, suggestion));
|
||||
}
|
||||
@@ -171,20 +176,23 @@ public final class LexicographicalAnnotationAttributeListingCheck extends BugChe
|
||||
ImmutableList.Builder<ImmutableList<String>> nodes = ImmutableList.builder();
|
||||
|
||||
new TreeScanner<Void, Void>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Void visitIdentifier(IdentifierTree node, Void ctx) {
|
||||
public Void visitIdentifier(IdentifierTree node, @Nullable Void ctx) {
|
||||
nodes.add(tokenize(node));
|
||||
return super.visitIdentifier(node, ctx);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Void visitLiteral(LiteralTree node, Void ctx) {
|
||||
public Void visitLiteral(LiteralTree node, @Nullable Void ctx) {
|
||||
nodes.add(tokenize(node));
|
||||
return super.visitLiteral(node, ctx);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Void visitPrimitiveType(PrimitiveTypeTree node, Void ctx) {
|
||||
public Void visitPrimitiveType(PrimitiveTypeTree node, @Nullable Void ctx) {
|
||||
nodes.add(tokenize(node));
|
||||
return super.visitPrimitiveType(node, ctx);
|
||||
}
|
||||
@@ -194,7 +202,7 @@ public final class LexicographicalAnnotationAttributeListingCheck extends BugChe
|
||||
* Tokens are split on `=` so that e.g. inline Spring property declarations are properly
|
||||
* sorted by key, then value.
|
||||
*/
|
||||
return ImmutableList.copyOf(Util.treeToString(node, state).split("=", -1));
|
||||
return ImmutableList.copyOf(SourceCode.treeToString(node, state).split("=", -1));
|
||||
}
|
||||
}.scan(array, null);
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that flags annotations that are not lexicographically sorted.
|
||||
@@ -29,12 +30,11 @@ import java.util.Optional;
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "LexicographicalAnnotationListing",
|
||||
summary = "Sort annotations lexicographically where possible",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = STYLE)
|
||||
public final class LexicographicalAnnotationListingCheck extends BugChecker
|
||||
public final class LexicographicalAnnotationListing extends BugChecker
|
||||
implements MethodTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -52,7 +52,7 @@ public final class LexicographicalAnnotationListingCheck extends BugChecker
|
||||
|
||||
Optional<Fix> fix = tryFixOrdering(originalOrdering, sortedAnnotations, state);
|
||||
|
||||
Description.Builder description = buildDescription(tree);
|
||||
Description.Builder description = buildDescription(originalOrdering.get(0));
|
||||
fix.ifPresent(description::addFix);
|
||||
return description.build();
|
||||
}
|
||||
@@ -60,7 +60,7 @@ public final class LexicographicalAnnotationListingCheck extends BugChecker
|
||||
private static ImmutableList<? extends AnnotationTree> sort(
|
||||
List<? extends AnnotationTree> annotations, VisitorState state) {
|
||||
return annotations.stream()
|
||||
.sorted(comparing(annotation -> Util.treeToString(annotation, state)))
|
||||
.sorted(comparing(annotation -> SourceCode.treeToString(annotation, state)))
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
@@ -72,7 +72,8 @@ public final class LexicographicalAnnotationListingCheck extends BugChecker
|
||||
originalAnnotations.stream(),
|
||||
sortedAnnotations.stream(),
|
||||
(original, replacement) ->
|
||||
SuggestedFix.builder().replace(original, Util.treeToString(replacement, state)))
|
||||
SuggestedFix.builder()
|
||||
.replace(original, SourceCode.treeToString(replacement, state)))
|
||||
.reduce(SuggestedFix.Builder::merge)
|
||||
.map(SuggestedFix.Builder::build);
|
||||
}
|
||||
@@ -51,13 +51,11 @@ import javax.lang.model.element.Name;
|
||||
// Palantir's `LambdaMethodReference` check seems to suffer a similar issue at this time.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "MethodReferenceUsage",
|
||||
summary = "Prefer method references over lambda expressions",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = STYLE)
|
||||
public final class MethodReferenceUsageCheck extends BugChecker
|
||||
implements LambdaExpressionTreeMatcher {
|
||||
public final class MethodReferenceUsage extends BugChecker implements LambdaExpressionTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
@@ -102,6 +100,8 @@ public final class MethodReferenceUsageCheck extends BugChecker
|
||||
.flatMap(statements -> constructMethodRef(lambdaExpr, statements.get(0)));
|
||||
}
|
||||
|
||||
// XXX: Replace nested `Optional` usage.
|
||||
@SuppressWarnings("NestedOptionals")
|
||||
private static Optional<SuggestedFix.Builder> constructMethodRef(
|
||||
LambdaExpressionTree lambdaExpr, MethodInvocationTree subTree) {
|
||||
return matchArguments(lambdaExpr, subTree)
|
||||
@@ -158,6 +158,8 @@ public final class MethodReferenceUsageCheck extends BugChecker
|
||||
return constructFix(lambdaExpr, lhsType.tsym, subTree.getIdentifier());
|
||||
}
|
||||
|
||||
// XXX: Refactor or replace inner `Optional` with a custom type.
|
||||
@SuppressWarnings("NestedOptionals")
|
||||
private static Optional<Optional<Name>> matchArguments(
|
||||
LambdaExpressionTree lambdaExpr, MethodInvocationTree subTree) {
|
||||
ImmutableList<Name> expectedArguments = getVariables(lambdaExpr);
|
||||
@@ -24,12 +24,11 @@ import com.sun.source.tree.Tree;
|
||||
/** A {@link BugChecker} that flags likely missing Refaster annotations. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "MissingRefasterAnnotation",
|
||||
summary = "The Refaster template contains a method without any Refaster annotations",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class MissingRefasterAnnotationCheck extends BugChecker implements ClassTreeMatcher {
|
||||
public final class MissingRefasterAnnotation extends BugChecker implements ClassTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final MultiMatcher<Tree, AnnotationTree> REFASTER_ANNOTATION =
|
||||
annotations(
|
||||
@@ -17,6 +17,7 @@ import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import java.util.List;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags method invocations for which all arguments are wrapped using
|
||||
@@ -24,12 +25,11 @@ import java.util.List;
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "MockitoStubbing",
|
||||
summary = "Don't unnecessarily use Mockito's `eq(...)`",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class MockitoStubbingCheck extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
public final class MockitoStubbing extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> MOCKITO_EQ_METHOD =
|
||||
staticMethod().onClass("org.mockito.ArgumentMatchers").named("eq");
|
||||
@@ -45,7 +45,7 @@ public final class MockitoStubbingCheck extends BugChecker implements MethodInvo
|
||||
for (ExpressionTree arg : arguments) {
|
||||
suggestedFix.replace(
|
||||
arg,
|
||||
Util.treeToString(
|
||||
SourceCode.treeToString(
|
||||
Iterables.getOnlyElement(((MethodInvocationTree) arg).getArguments()), state));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.FRAGILE_CODE;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.suppliers.Supplier;
|
||||
import com.google.errorprone.suppliers.Suppliers;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/** A {@link BugChecker} which flags nesting of {@link Optional Optionals}. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary =
|
||||
"Avoid nesting `Optional`s inside `Optional`s; the resultant code is hard to reason about",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = FRAGILE_CODE)
|
||||
public final class NestedOptionals extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Supplier<Type> OPTIONAL = Suppliers.typeFromClass(Optional.class);
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
return isOptionalOfOptional(tree, state) ? describeMatch(tree) : Description.NO_MATCH;
|
||||
}
|
||||
|
||||
private static boolean isOptionalOfOptional(Tree tree, VisitorState state) {
|
||||
Type optionalType = OPTIONAL.get(state);
|
||||
Type type = ASTHelpers.getType(tree);
|
||||
if (!ASTHelpers.isSubtype(type, optionalType, state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Type> typeArguments = type.getTypeArguments();
|
||||
return !typeArguments.isEmpty()
|
||||
&& ASTHelpers.isSubtype(Iterables.getOnlyElement(typeArguments), optionalType, state);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import java.util.function.BiFunction;
|
||||
import reactor.core.publisher.Mono;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags {@link Mono} operations that are known to be vacuous, given that
|
||||
* they are invoked on a {@link Mono} that is known not to complete empty.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Avoid vacuous operations on known non-empty `Mono`s",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = SIMPLIFICATION)
|
||||
// XXX: This check does not simplify `someFlux.defaultIfEmpty(T).{defaultIfEmpty(T),hasElements()}`,
|
||||
// as `someFlux.defaultIfEmpty(T)` yields a `Flux` rather than a `Mono`. Consider adding support for
|
||||
// these cases.
|
||||
// XXX: Given more advanced analysis many more expressions could be flagged. Consider
|
||||
// `Mono.just(someValue)`, `Flux.just(someNonEmptySequence)`,
|
||||
// `someMono.switchIfEmpty(someProvablyNonEmptyMono)` and many other variants.
|
||||
// XXX: Consider implementing a similar check for `Publisher`s that are known to complete without
|
||||
// emitting a value (e.g. `Mono.empty()`, `someFlux.then()`, ...), or known not to complete normally
|
||||
// (`Mono.never()`, `someFlux.repeat()`, `Mono.error(...)`, ...). The latter category could
|
||||
// potentially be split out further.
|
||||
public final class NonEmptyMono extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> MONO_SIZE_CHECK =
|
||||
instanceMethod()
|
||||
.onDescendantOf("reactor.core.publisher.Mono")
|
||||
.namedAnyOf("defaultIfEmpty", "single", "switchIfEmpty");
|
||||
private static final Matcher<ExpressionTree> NON_EMPTY_MONO =
|
||||
anyOf(
|
||||
instanceMethod()
|
||||
.onDescendantOf("reactor.core.publisher.Flux")
|
||||
.namedAnyOf(
|
||||
"all",
|
||||
"any",
|
||||
"collect",
|
||||
"collectList",
|
||||
"collectMap",
|
||||
"collectMultimap",
|
||||
"collectSortedList",
|
||||
"count",
|
||||
"elementAt",
|
||||
"hasElement",
|
||||
"hasElements",
|
||||
"last",
|
||||
"reduceWith",
|
||||
"single"),
|
||||
instanceMethod()
|
||||
.onDescendantOf("reactor.core.publisher.Flux")
|
||||
.named("reduce")
|
||||
.withParameters(Object.class.getName(), BiFunction.class.getName()),
|
||||
instanceMethod()
|
||||
.onDescendantOf("reactor.core.publisher.Mono")
|
||||
.namedAnyOf("defaultIfEmpty", "hasElement", "single"));
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
if (!MONO_SIZE_CHECK.matches(tree, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
|
||||
if (!NON_EMPTY_MONO.matches(receiver, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
return describeMatch(
|
||||
tree, SuggestedFix.replace(tree, SourceCode.treeToString(receiver, state)));
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import static com.google.errorprone.BugPattern.StandardTags.PERFORMANCE;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
|
||||
import static java.util.function.Predicate.not;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
@@ -32,6 +31,7 @@ import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags {@code Comparator#comparing*} invocations that can be replaced
|
||||
@@ -41,15 +41,13 @@ import java.util.stream.Stream;
|
||||
// specific types.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "PrimitiveComparison",
|
||||
summary =
|
||||
"Ensure invocations of `Comparator#comparing{,Double,Int,Long}` match the return type"
|
||||
+ " of the provided function",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = PERFORMANCE)
|
||||
public final class PrimitiveComparisonCheck extends BugChecker
|
||||
implements MethodInvocationTreeMatcher {
|
||||
public final class PrimitiveComparison extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> STATIC_COMPARISON_METHOD =
|
||||
anyOf(
|
||||
@@ -85,16 +83,15 @@ public final class PrimitiveComparisonCheck extends BugChecker
|
||||
|
||||
private static Optional<Fix> attemptMethodInvocationReplacement(
|
||||
MethodInvocationTree tree, Type cmpType, boolean isStatic, VisitorState state) {
|
||||
return Optional.ofNullable(ASTHelpers.getSymbol(tree))
|
||||
.map(methodSymbol -> methodSymbol.getSimpleName().toString())
|
||||
.flatMap(
|
||||
actualMethodName ->
|
||||
Optional.of(getPreferredMethod(cmpType, isStatic, state))
|
||||
.filter(not(actualMethodName::equals)))
|
||||
.map(
|
||||
preferredMethodName ->
|
||||
prefixTypeArgumentsIfRelevant(preferredMethodName, tree, cmpType, state))
|
||||
.map(preferredMethodName -> suggestFix(tree, preferredMethodName, state));
|
||||
String actualMethodName = ASTHelpers.getSymbol(tree).getSimpleName().toString();
|
||||
String preferredMethodName = getPreferredMethod(cmpType, isStatic, state);
|
||||
if (actualMethodName.equals(preferredMethodName)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(
|
||||
suggestFix(
|
||||
tree, prefixTypeArgumentsIfRelevant(preferredMethodName, tree, cmpType, state), state));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +113,7 @@ public final class PrimitiveComparisonCheck extends BugChecker
|
||||
|
||||
String typeArguments =
|
||||
Stream.concat(
|
||||
Stream.of(Util.treeToString(tree.getTypeArguments().get(0), state)),
|
||||
Stream.of(SourceCode.treeToString(tree.getTypeArguments().get(0), state)),
|
||||
Stream.of(cmpType.tsym.getSimpleName())
|
||||
.filter(u -> "comparing".equals(preferredMethodName)))
|
||||
.collect(joining(", ", "<", ">"));
|
||||
@@ -171,7 +168,7 @@ public final class PrimitiveComparisonCheck extends BugChecker
|
||||
case MEMBER_SELECT:
|
||||
MemberSelectTree ms = (MemberSelectTree) tree.getMethodSelect();
|
||||
return SuggestedFix.replace(
|
||||
ms, Util.treeToString(ms.getExpression(), state) + '.' + preferredMethodName);
|
||||
ms, SourceCode.treeToString(ms.getExpression(), state) + '.' + preferredMethodName);
|
||||
default:
|
||||
throw new VerifyException("Unexpected type of expression: " + expr.getKind());
|
||||
}
|
||||
@@ -41,16 +41,17 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.bugpatterns.util.MethodMatcherFactory;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} which flags redundant explicit string conversions. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "RedundantStringConversion",
|
||||
summary = "Avoid redundant string conversions when possible",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class RedundantStringConversionCheck extends BugChecker
|
||||
public final class RedundantStringConversion extends BugChecker
|
||||
implements BinaryTreeMatcher, CompoundAssignmentTreeMatcher, MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String FLAG_PREFIX = "RedundantStringConversion:";
|
||||
@@ -157,17 +158,17 @@ public final class RedundantStringConversionCheck extends BugChecker
|
||||
|
||||
private final Matcher<ExpressionTree> conversionMethodMatcher;
|
||||
|
||||
/** Instantiates the default {@link RedundantStringConversionCheck}. */
|
||||
public RedundantStringConversionCheck() {
|
||||
/** Instantiates the default {@link RedundantStringConversion}. */
|
||||
public RedundantStringConversion() {
|
||||
this(ErrorProneFlags.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a customized {@link RedundantStringConversionCheck}.
|
||||
* Instantiates a customized {@link RedundantStringConversion}.
|
||||
*
|
||||
* @param flags Any provided command line flags.
|
||||
*/
|
||||
public RedundantStringConversionCheck(ErrorProneFlags flags) {
|
||||
public RedundantStringConversion(ErrorProneFlags flags) {
|
||||
conversionMethodMatcher = createConversionMethodMatcher(flags);
|
||||
}
|
||||
|
||||
@@ -326,7 +327,7 @@ public final class RedundantStringConversionCheck extends BugChecker
|
||||
return trySimplify(tree, state, filter)
|
||||
.map(
|
||||
replacement ->
|
||||
SuggestedFix.builder().replace(tree, Util.treeToString(replacement, state)));
|
||||
SuggestedFix.builder().replace(tree, SourceCode.treeToString(replacement, state)));
|
||||
}
|
||||
|
||||
private Optional<ExpressionTree> trySimplify(
|
||||
@@ -350,7 +351,7 @@ public final class RedundantStringConversionCheck extends BugChecker
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
"Cannot simplify method call with two or more arguments: "
|
||||
+ Util.treeToString(tree, state));
|
||||
+ SourceCode.treeToString(tree, state));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,7 +364,7 @@ public final class RedundantStringConversionCheck extends BugChecker
|
||||
return Optional.of(methodInvocation.getMethodSelect())
|
||||
.filter(methodSelect -> methodSelect.getKind() == Kind.MEMBER_SELECT)
|
||||
.map(methodSelect -> ((MemberSelectTree) methodSelect).getExpression())
|
||||
.filter(expr -> !"super".equals(Util.treeToString(expr, state)));
|
||||
.filter(expr -> !"super".equals(SourceCode.treeToString(expr, state)));
|
||||
}
|
||||
|
||||
private static Optional<ExpressionTree> trySimplifyUnaryMethod(
|
||||
@@ -16,6 +16,7 @@ import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags unnecessary {@link Refaster#anyOf(Object[])} usages.
|
||||
@@ -25,13 +26,11 @@ import com.sun.source.tree.MethodInvocationTree;
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "RefasterAnyOfUsage",
|
||||
summary = "`Refaster#anyOf` should be passed at least two parameters",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class RefasterAnyOfUsageCheck extends BugChecker
|
||||
implements MethodInvocationTreeMatcher {
|
||||
public final class RefasterAnyOfUsage extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> REFASTER_ANY_OF =
|
||||
staticMethod().onClass(Refaster.class.getName()).named("anyOf");
|
||||
@@ -46,7 +45,8 @@ public final class RefasterAnyOfUsageCheck extends BugChecker
|
||||
case 1:
|
||||
return describeMatch(
|
||||
tree,
|
||||
SuggestedFix.replace(tree, Util.treeToString(tree.getArguments().get(0), state)));
|
||||
SuggestedFix.replace(
|
||||
tree, SourceCode.treeToString(tree.getArguments().get(0), state)));
|
||||
default:
|
||||
/* Handled below. */
|
||||
}
|
||||
@@ -30,12 +30,11 @@ import com.sun.source.tree.Tree;
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "RequestMappingAnnotation",
|
||||
summary = "Make sure all `@RequestMapping` method parameters are annotated",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class RequestMappingAnnotationCheck extends BugChecker implements MethodTreeMatcher {
|
||||
public final class RequestMappingAnnotation extends BugChecker implements MethodTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String ANN_PACKAGE_PREFIX = "org.springframework.web.bind.annotation.";
|
||||
// XXX: Generalize this logic to fully support Spring meta-annotations, then update the class
|
||||
@@ -64,6 +63,7 @@ public final class RequestMappingAnnotationCheck extends BugChecker implements M
|
||||
AT_LEAST_ONE,
|
||||
anyOf(
|
||||
isType(ANN_PACKAGE_PREFIX + "PathVariable"),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestAttribute"),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestBody"),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestHeader"),
|
||||
isType(ANN_PACKAGE_PREFIX + "RequestParam"))),
|
||||
@@ -24,12 +24,11 @@ import com.sun.source.tree.VariableTree;
|
||||
/** A {@link BugChecker} which flags {@code @RequestParam} parameters with an unsupported type. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "RequestParamType",
|
||||
summary = "`@RequestParam` does not support `ImmutableCollection` and `ImmutableMap` subtypes",
|
||||
linkType = NONE,
|
||||
severity = ERROR,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class RequestParamTypeCheck extends BugChecker implements VariableTreeMatcher {
|
||||
public final class RequestParamType extends BugChecker implements VariableTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<VariableTree> HAS_UNSUPPORTED_REQUEST_PARAM =
|
||||
allOf(
|
||||
@@ -32,12 +32,11 @@ import com.sun.source.tree.Tree;
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "ScheduledTransactionTrace",
|
||||
summary = "Scheduled operation must start a new New Relic transaction",
|
||||
linkType = NONE,
|
||||
severity = ERROR,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class ScheduledTransactionTraceCheck extends BugChecker implements MethodTreeMatcher {
|
||||
public final class ScheduledTransactionTrace extends BugChecker implements MethodTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String TRACE_ANNOTATION_FQCN = "com.newrelic.api.agent.Trace";
|
||||
private static final Matcher<Tree> IS_SCHEDULED =
|
||||
@@ -75,7 +74,7 @@ public final class ScheduledTransactionTraceCheck extends BugChecker implements
|
||||
return describeMatch(
|
||||
traceAnnotation,
|
||||
SuggestedFixes.updateAnnotationArgumentValues(
|
||||
traceAnnotation, "dispatcher", ImmutableList.of("true"))
|
||||
traceAnnotation, state, "dispatcher", ImmutableList.of("true"))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} which flags SLF4J usages that are likely to be in error. */
|
||||
// XXX: The special-casing of Throwable applies only to SLF4J 1.6.0+; see
|
||||
@@ -31,13 +32,11 @@ import java.util.Optional;
|
||||
// preconditions, ...
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "Slf4jLogStatement",
|
||||
summary = "Make sure SLF4J log statements contain proper placeholders with matching arguments",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = LIKELY_ERROR)
|
||||
public final class Slf4jLogStatementCheck extends BugChecker
|
||||
implements MethodInvocationTreeMatcher {
|
||||
public final class Slf4jLogStatement extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> MARKER = isSubtypeOf("org.slf4j.Marker");
|
||||
private static final Matcher<ExpressionTree> THROWABLE = isSubtypeOf(Throwable.class);
|
||||
@@ -114,7 +113,7 @@ public final class Slf4jLogStatementCheck extends BugChecker
|
||||
* replaced at this usage site.
|
||||
*/
|
||||
description.addFix(
|
||||
SuggestedFix.replace(tree, Util.treeToString(tree, state).replace("%s", "{}")));
|
||||
SuggestedFix.replace(tree, SourceCode.treeToString(tree, state).replace("%s", "{}")));
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -25,6 +25,8 @@ 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;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags {@code @RequestMapping} annotations that can be written more
|
||||
@@ -32,13 +34,12 @@ import java.util.Optional;
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "SpringMvcAnnotation",
|
||||
summary =
|
||||
"Prefer the conciseness of `@{Get,Put,Post,Delete,Patch}Mapping` over `@RequestMapping`",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class SpringMvcAnnotationCheck extends BugChecker implements AnnotationTreeMatcher {
|
||||
public final class SpringMvcAnnotation extends BugChecker implements AnnotationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String ANN_PACKAGE_PREFIX = "org.springframework.web.bind.annotation.";
|
||||
private static final AnnotationAttributeMatcher ARGUMENT_SELECTOR =
|
||||
@@ -92,7 +93,7 @@ public final class SpringMvcAnnotationCheck extends BugChecker implements Annota
|
||||
private static String extractMethod(ExpressionTree expr, VisitorState state) {
|
||||
switch (expr.getKind()) {
|
||||
case IDENTIFIER:
|
||||
return Util.treeToString(expr, state);
|
||||
return SourceCode.treeToString(expr, state);
|
||||
case MEMBER_SELECT:
|
||||
return ((MemberSelectTree) expr).getIdentifier().toString();
|
||||
default:
|
||||
@@ -105,7 +106,7 @@ public final class SpringMvcAnnotationCheck extends BugChecker implements Annota
|
||||
String newArguments =
|
||||
tree.getArguments().stream()
|
||||
.filter(not(argToRemove::equals))
|
||||
.map(arg -> Util.treeToString(arg, state))
|
||||
.map(arg -> SourceCode.treeToString(arg, state))
|
||||
.collect(joining(", "));
|
||||
|
||||
return SuggestedFix.builder()
|
||||
@@ -45,12 +45,11 @@ import java.util.Optional;
|
||||
// - Likely any of `*.{ZERO, ONE, MIX, MAX, MIN_VALUE, MAX_VALUE}`.
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "StaticImport",
|
||||
summary = "Identifier should be statically imported",
|
||||
linkType = NONE,
|
||||
severity = SUGGESTION,
|
||||
tags = SIMPLIFICATION)
|
||||
public final class StaticImportCheck extends BugChecker implements MemberSelectTreeMatcher {
|
||||
public final class StaticImport extends BugChecker implements MemberSelectTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
@@ -67,6 +66,7 @@ public final class StaticImportCheck extends BugChecker implements MemberSelectT
|
||||
"com.google.errorprone.BugPattern.LinkType",
|
||||
"com.google.errorprone.BugPattern.SeverityLevel",
|
||||
"com.google.errorprone.BugPattern.StandardTags",
|
||||
"com.google.errorprone.matchers.Matchers",
|
||||
"com.google.errorprone.refaster.ImportPolicy",
|
||||
"com.mongodb.client.model.Accumulators",
|
||||
"com.mongodb.client.model.Aggregates",
|
||||
@@ -29,13 +29,12 @@ import java.time.LocalTime;
|
||||
/** A {@link BugChecker} which flags illegal time-zone related operations. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "TimeZoneUsage",
|
||||
summary =
|
||||
"Derive the current time from an existing `Clock` Spring bean, and don't rely on a `Clock`'s time zone",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = FRAGILE_CODE)
|
||||
public final class TimeZoneUsageCheck extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
public final class TimeZoneUsage extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> BANNED_TIME_METHOD =
|
||||
anyOf(
|
||||
@@ -59,7 +58,7 @@ public final class TimeZoneUsageCheck extends BugChecker implements MethodInvoca
|
||||
LocalDateTime.class.getName(),
|
||||
LocalTime.class.getName())
|
||||
.named("now"),
|
||||
staticMethod().onClassAny(Instant.class.getName()).named("now").withParameters());
|
||||
staticMethod().onClassAny(Instant.class.getName()).named("now").withNoParameters());
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.bugpatterns.util;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
@@ -28,7 +28,7 @@ import java.util.stream.Stream;
|
||||
* <p>This class allows one to define a whitelist or blacklist of annotations or their attributes.
|
||||
* Annotations are identified by their fully qualified name.
|
||||
*/
|
||||
final class AnnotationAttributeMatcher implements Serializable {
|
||||
public final class AnnotationAttributeMatcher implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final boolean complement;
|
||||
@@ -59,7 +59,7 @@ final class AnnotationAttributeMatcher implements Serializable {
|
||||
* @param exclusions The listed annotations or annotation attributes are not matched.
|
||||
* @return A non-{@code null} {@link AnnotationAttributeMatcher}.
|
||||
*/
|
||||
static AnnotationAttributeMatcher create(
|
||||
public static AnnotationAttributeMatcher create(
|
||||
Optional<? extends List<String>> inclusions, Iterable<String> exclusions) {
|
||||
Set<String> includedWholeTypes = new HashSet<>();
|
||||
Set<String> excludedWholeTypes = new HashSet<>();
|
||||
@@ -97,7 +97,13 @@ final class AnnotationAttributeMatcher implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
Stream<? extends ExpressionTree> extractMatchingArguments(AnnotationTree tree) {
|
||||
/**
|
||||
* Returns the subset of arguments of the given {@link AnnotationTree} matched by this instance.
|
||||
*
|
||||
* @param tree The annotation AST node to be inspected.
|
||||
* @return Any matching annotation arguments.
|
||||
*/
|
||||
public Stream<? extends ExpressionTree> extractMatchingArguments(AnnotationTree tree) {
|
||||
Type type = ASTHelpers.getType(tree.getAnnotationType());
|
||||
if (type == null) {
|
||||
return Stream.empty();
|
||||
@@ -1,9 +1,10 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.bugpatterns.util;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
final class JavaKeywords {
|
||||
/** Utility class that can be used to identify reserved keywords of the Java language. */
|
||||
public final class JavaKeywords {
|
||||
/**
|
||||
* List of all reserved keywords in the Java language.
|
||||
*
|
||||
@@ -95,17 +96,33 @@ final class JavaKeywords {
|
||||
|
||||
private JavaKeywords() {}
|
||||
|
||||
/** Tells whether the given string is a reserved keyword in the Java language. */
|
||||
/**
|
||||
* Tells whether the given string is a reserved keyword in the Java language.
|
||||
*
|
||||
* @param str The string of interest.
|
||||
* @return {@code true} if the given string is a reserved keyword in the Java language.
|
||||
*/
|
||||
public static boolean isReservedKeyword(String str) {
|
||||
return RESERVED_KEYWORDS.contains(str);
|
||||
}
|
||||
|
||||
/** Tells whether the given string is a contextual keyword in the Java language. */
|
||||
/**
|
||||
* Tells whether the given string is a contextual keyword in the Java language.
|
||||
*
|
||||
* @param str The string of interest.
|
||||
* @return {@code true} if the given string is a contextual keyword in the Java language.
|
||||
*/
|
||||
public static boolean isContextualKeyword(String str) {
|
||||
return CONTEXTUAL_KEYWORDS.contains(str);
|
||||
}
|
||||
|
||||
/** Tells whether the given string is a reserved or contextual keyword in the Java language. */
|
||||
/**
|
||||
* Tells whether the given string is a reserved or contextual keyword in the Java language.
|
||||
*
|
||||
* @param str The string of interest.
|
||||
* @return {@code true} if the given string is a reserved or contextual keyword in the Java
|
||||
* language.
|
||||
*/
|
||||
public static boolean isKeyword(String str) {
|
||||
return ALL_KEYWORDS.contains(str);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.bugpatterns.util;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
@@ -15,13 +15,20 @@ import java.util.regex.Pattern;
|
||||
/** A method invocation expression {@link Matcher} factory. */
|
||||
// XXX: Document better. The expressions accepted here could also be defined using `MethodMatchers`.
|
||||
// So explain why this class is still useful.
|
||||
final class MethodMatcherFactory {
|
||||
public final class MethodMatcherFactory {
|
||||
private static final Splitter ARGUMENT_TYPE_SPLITTER =
|
||||
Splitter.on(',').trimResults().omitEmptyStrings();
|
||||
private static final Pattern METHOD_SIGNATURE =
|
||||
Pattern.compile("([^\\s#(,)]+)#([^\\s#(,)]+)\\(((?:[^\\s#(,)]+(?:,[^\\s#(,)]+)*)?)\\)");
|
||||
|
||||
Matcher<ExpressionTree> create(Collection<String> signatures) {
|
||||
/**
|
||||
* Creates a {@link Matcher} of methods with any of the given signatures.
|
||||
*
|
||||
* @param signatures The method signatures of interest.
|
||||
* @return A new {@link Matcher} which accepts invocation expressions of any method identified by
|
||||
* the given signatures.
|
||||
*/
|
||||
public Matcher<ExpressionTree> create(Collection<String> signatures) {
|
||||
return anyOf(
|
||||
signatures.stream()
|
||||
.map(MethodMatcherFactory::createMethodMatcher)
|
||||
@@ -1,19 +1,26 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.bugpatterns.util;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.sun.source.tree.Tree;
|
||||
|
||||
/**
|
||||
* A collection of Error Prone utility methods for dealing with the source code representation of
|
||||
* AST nodes.
|
||||
*/
|
||||
// XXX: Can we locate this code in a better place? Maybe contribute it upstream?
|
||||
final class Util {
|
||||
private Util() {}
|
||||
public final class SourceCode {
|
||||
private SourceCode() {}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the given {@link Tree}, preferring the original source code
|
||||
* (if available) over its prettified representation.
|
||||
*
|
||||
* @param tree The AST node of interest.
|
||||
* @param state A {@link VisitorState} describing the context in which the given {@link Tree} is
|
||||
* found.
|
||||
* @return A non-{@code null} string.
|
||||
*/
|
||||
static String treeToString(Tree tree, VisitorState state) {
|
||||
public static String treeToString(Tree tree, VisitorState state) {
|
||||
String src = state.getSourceForNode(tree);
|
||||
return src != null ? src : tree.toString();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/** Auxiliary utilities for use by Error Prone checks. */
|
||||
@com.google.errorprone.annotations.CheckReturnValue
|
||||
@javax.annotation.ParametersAreNonnullByDefault
|
||||
package tech.picnic.errorprone.bugpatterns.util;
|
||||
@@ -59,6 +59,27 @@ final class AssertJThrowingCallableTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByIllegalArgumentExceptionHasMessageParameters {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByIllegalArgumentException" /* Matches strictly more specific expressions. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
ThrowingCallable throwingCallable, String message, @Repeated Object parameters) {
|
||||
return assertThatIllegalArgumentException()
|
||||
.isThrownBy(throwingCallable)
|
||||
.withMessage(message, parameters);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(
|
||||
ThrowingCallable throwingCallable, String message, @Repeated Object parameters) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage(message, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByIllegalArgumentExceptionHasMessageStartingWith {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
@@ -148,6 +169,27 @@ final class AssertJThrowingCallableTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByIllegalStateExceptionHasMessageParameters {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByIllegalStateException" /* Matches strictly more specific expressions. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
ThrowingCallable throwingCallable, String message, @Repeated Object parameters) {
|
||||
return assertThatIllegalStateException()
|
||||
.isThrownBy(throwingCallable)
|
||||
.withMessage(message, parameters);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(
|
||||
ThrowingCallable throwingCallable, String message, @Repeated Object parameters) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.hasMessage(message, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByIllegalStateExceptionHasMessageStartingWith {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
@@ -235,6 +277,27 @@ final class AssertJThrowingCallableTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByNullPointerExceptionHasMessageParameters {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByNullPointerException" /* Matches strictly more specific expressions. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
ThrowingCallable throwingCallable, String message, @Repeated Object parameters) {
|
||||
return assertThatNullPointerException()
|
||||
.isThrownBy(throwingCallable)
|
||||
.withMessage(message, parameters);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(
|
||||
ThrowingCallable throwingCallable, String message, @Repeated Object parameters) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
.isInstanceOf(NullPointerException.class)
|
||||
.hasMessage(message, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByNullPointerExceptionHasMessageStartingWith {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
@@ -268,9 +331,9 @@ final class AssertJThrowingCallableTemplates {
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByIOExceptionHasMessage {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByIOException" /* Matches strictly more specific expressions. */)
|
||||
@BeforeTemplate
|
||||
AbstractObjectAssert<?, ?> before(ThrowingCallable throwingCallable, String message) {
|
||||
return assertThatIOException().isThrownBy(throwingCallable).withMessage(message);
|
||||
}
|
||||
@@ -284,6 +347,25 @@ final class AssertJThrowingCallableTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByIOExceptionHasMessageParameters {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings(
|
||||
"AssertThatThrownByIOException" /* Matches strictly more specific expressions. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
ThrowingCallable throwingCallable, String message, @Repeated Object parameters) {
|
||||
return assertThatIOException().isThrownBy(throwingCallable).withMessage(message, parameters);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(
|
||||
ThrowingCallable throwingCallable, String message, @Repeated Object parameters) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
.isInstanceOf(IOException.class)
|
||||
.hasMessage(message, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownBy {
|
||||
@BeforeTemplate
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
@@ -321,6 +403,32 @@ final class AssertJThrowingCallableTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
static final class AssertThatThrownByHasMessageParameters {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("AssertThatThrownBy" /* Matches strictly more specific expressions. */)
|
||||
AbstractObjectAssert<?, ?> before(
|
||||
Class<? extends Throwable> exceptionType,
|
||||
ThrowingCallable throwingCallable,
|
||||
String message,
|
||||
@Repeated Object parameters) {
|
||||
return assertThatExceptionOfType(exceptionType)
|
||||
.isThrownBy(throwingCallable)
|
||||
.withMessage(message, parameters);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
|
||||
AbstractObjectAssert<?, ?> after(
|
||||
Class<? extends Throwable> exceptionType,
|
||||
ThrowingCallable throwingCallable,
|
||||
String message,
|
||||
@Repeated Object parameters) {
|
||||
return assertThatThrownBy(throwingCallable)
|
||||
.isInstanceOf(exceptionType)
|
||||
.hasMessage(message, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Drop this template in favour of a generic Error Prone check which flags
|
||||
// `String.format(...)` arguments to a wide range of format methods.
|
||||
static final class AbstractThrowableAssertHasMessage {
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.List;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.stream.Stream;
|
||||
@@ -129,31 +130,32 @@ final class CollectionTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
static final class CollectionRemoveAllFromCollectionBlock<T, S extends T> {
|
||||
static final class SetRemoveAllCollection<T, S extends T> {
|
||||
@BeforeTemplate
|
||||
void before(Collection<T> removeTo, Collection<S> elementsToRemove) {
|
||||
elementsToRemove.forEach(removeTo::remove);
|
||||
void before(Set<T> removeFrom, Collection<S> elementsToRemove) {
|
||||
elementsToRemove.forEach(removeFrom::remove);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
void before2(Collection<T> removeTo, Collection<S> elementsToRemove) {
|
||||
void before2(Set<T> removeFrom, Collection<S> elementsToRemove) {
|
||||
for (T element : elementsToRemove) {
|
||||
removeTo.remove(element);
|
||||
removeFrom.remove(element);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: This method is identical to `before2` except for the loop type. Make Refaster smarter so
|
||||
// that this is supported out of the box.
|
||||
// that this is supported out of the box. After doing so, also drop the `S extends T` type
|
||||
// constraint; ideally this check applies to any `S`.
|
||||
@BeforeTemplate
|
||||
void before3(Collection<T> removeTo, Collection<S> elementsToRemove) {
|
||||
void before3(Set<T> removeFrom, Collection<S> elementsToRemove) {
|
||||
for (S element : elementsToRemove) {
|
||||
removeTo.remove(element);
|
||||
removeFrom.remove(element);
|
||||
}
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
void after(Collection<T> removeTo, Collection<S> elementsToRemove) {
|
||||
removeTo.removeAll(elementsToRemove);
|
||||
void after(Set<T> removeFrom, Collection<S> elementsToRemove) {
|
||||
removeFrom.removeAll(elementsToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ final class EqualityTemplates {
|
||||
* remaining reference-based equality checks.
|
||||
*/
|
||||
// XXX: This Refaster rule is the topic of https://github.com/google/error-prone/issues/559. We
|
||||
// work around the issue by selecting the "largest replacements". See RefasterCheck.
|
||||
// work around the issue by selecting the "largest replacements". See the `Refaster` check.
|
||||
@BeforeTemplate
|
||||
boolean before(T a, T b) {
|
||||
return Refaster.anyOf(a.equals(b), Objects.equals(a, b));
|
||||
@@ -34,9 +34,8 @@ final class EqualityTemplates {
|
||||
}
|
||||
|
||||
/** Prefer {@link Object#equals(Object)} over the equivalent lambda function. */
|
||||
// XXX: As it stands, this rule is a special case of what `MethodReferenceUsageCheck` tries to
|
||||
// achieve. If/when `MethodReferenceUsageCheck` becomes production ready, we should simply drop
|
||||
// this check.
|
||||
// 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.
|
||||
// XXX: Alternatively, the rule should be replaced with a plugin which also identifies cases where
|
||||
// the arguments are swapped but simplification is possible anyway, by virtue of `v` being
|
||||
// non-null.
|
||||
|
||||
@@ -92,7 +92,7 @@ final class ImmutableListMultimapTemplates {
|
||||
*/
|
||||
static final class EntryToImmutableListMultimap<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableMultimap<K, V> before(Map.Entry<? extends K, ? extends V> entry) {
|
||||
ImmutableListMultimap<K, V> before(Map.Entry<? extends K, ? extends V> entry) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableListMultimap.<K, V>builder().put(entry).build(),
|
||||
Stream.of(entry)
|
||||
|
||||
@@ -6,8 +6,6 @@ import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.stream.Collectors.collectingAndThen;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -78,17 +76,11 @@ final class ImmutableListTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link ImmutableList#toImmutableList()} over the more verbose alternative. */
|
||||
// XXX: Once the code base has been sufficiently cleaned up, we might want to also rewrite
|
||||
// `Collectors.toList(`), with the caveat that it allows mutation (though this cannot be relied
|
||||
// upon) as well as nulls. Another option is to explicitly rewrite those variants to
|
||||
// `Collectors.toSet(ArrayList::new)`.
|
||||
/** Prefer {@link ImmutableList#toImmutableList()} over less idiomatic alternatives. */
|
||||
static final class StreamToImmutableList<T> {
|
||||
@BeforeTemplate
|
||||
ImmutableList<T> before(Stream<T> stream) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableList.copyOf(stream.iterator()),
|
||||
stream.collect(collectingAndThen(toList(), ImmutableList::copyOf)));
|
||||
return ImmutableList.copyOf(stream.iterator());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
|
||||
@@ -192,7 +192,7 @@ final class ImmutableMapTemplates {
|
||||
|
||||
// XXX: Instead of `Map.Entry::getKey` we could also match `e -> e.getKey()`. But for some
|
||||
// reason Refaster doesn't handle that case. This doesn't matter if we roll out use of
|
||||
// `MethodReferenceUsageCheck`. Same observation applies to a lot of other Refaster checks.
|
||||
// `MethodReferenceUsage`. Same observation applies to a lot of other Refaster checks.
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("NullAway")
|
||||
ImmutableMap<K, V2> before(Map<K, V1> map) {
|
||||
|
||||
@@ -2,8 +2,6 @@ package tech.picnic.errorprone.refastertemplates;
|
||||
|
||||
import static com.google.common.collect.ImmutableMultiset.toImmutableMultiset;
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static java.util.stream.Collectors.collectingAndThen;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.google.common.collect.ImmutableMultiset;
|
||||
import com.google.common.collect.Streams;
|
||||
@@ -90,9 +88,7 @@ final class ImmutableMultisetTemplates {
|
||||
static final class StreamToImmutableMultiset<T> {
|
||||
@BeforeTemplate
|
||||
ImmutableMultiset<T> before(Stream<T> stream) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableMultiset.copyOf(stream.iterator()),
|
||||
stream.collect(collectingAndThen(toList(), ImmutableMultiset::copyOf)));
|
||||
return ImmutableMultiset.copyOf(stream.iterator());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
|
||||
@@ -4,9 +4,6 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static java.util.Collections.emptySet;
|
||||
import static java.util.Collections.singleton;
|
||||
import static java.util.stream.Collectors.collectingAndThen;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets.SetView;
|
||||
@@ -75,18 +72,11 @@ final class ImmutableSetTemplates {
|
||||
}
|
||||
|
||||
/** Prefer {@link ImmutableSet#toImmutableSet()} over less idiomatic alternatives. */
|
||||
// XXX: Once the code base has been sufficiently cleaned up, we might want to also rewrite
|
||||
// `Collectors.toSet(`), with the caveat that it allows mutation (though this cannot be relied
|
||||
// upon) as well as nulls. Another option is to explicitly rewrite those variants to
|
||||
// `Collectors.toSet(HashSet::new)`.
|
||||
static final class StreamToImmutableSet<T> {
|
||||
@BeforeTemplate
|
||||
ImmutableSet<T> before(Stream<T> stream) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSet.copyOf(stream.iterator()),
|
||||
stream.distinct().collect(toImmutableSet()),
|
||||
stream.collect(collectingAndThen(toList(), ImmutableSet::copyOf)),
|
||||
stream.collect(collectingAndThen(toSet(), ImmutableSet::copyOf)));
|
||||
ImmutableSet.copyOf(stream.iterator()), stream.distinct().collect(toImmutableSet()));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
|
||||
@@ -3,10 +3,7 @@ package tech.picnic.errorprone.refastertemplates;
|
||||
import static com.google.common.collect.ImmutableSortedMultiset.toImmutableSortedMultiset;
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.stream.Collectors.collectingAndThen;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.google.common.collect.ImmutableMultiset;
|
||||
import com.google.common.collect.ImmutableSortedMultiset;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
@@ -93,14 +90,14 @@ final class ImmutableSortedMultisetTemplates {
|
||||
// `reverseOrder`.) Worth the hassle?
|
||||
static final class IterableToImmutableSortedMultiset<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
ImmutableMultiset<T> before(T[] iterable) {
|
||||
ImmutableSortedMultiset<T> before(T[] iterable) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSortedMultiset.<T>naturalOrder().add(iterable).build(),
|
||||
Arrays.stream(iterable).collect(toImmutableSortedMultiset(naturalOrder())));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableMultiset<T> before(Iterator<T> iterable) {
|
||||
ImmutableSortedMultiset<T> before(Iterator<T> iterable) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSortedMultiset.copyOf(naturalOrder(), iterable),
|
||||
ImmutableSortedMultiset.<T>naturalOrder().addAll(iterable).build(),
|
||||
@@ -108,7 +105,7 @@ final class ImmutableSortedMultisetTemplates {
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableMultiset<T> before(Iterable<T> iterable) {
|
||||
ImmutableSortedMultiset<T> before(Iterable<T> iterable) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSortedMultiset.copyOf(naturalOrder(), iterable),
|
||||
ImmutableSortedMultiset.<T>naturalOrder().addAll(iterable).build(),
|
||||
@@ -134,9 +131,7 @@ final class ImmutableSortedMultisetTemplates {
|
||||
static final class StreamToImmutableSortedMultiset<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
ImmutableSortedMultiset<T> before(Stream<T> stream) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSortedMultiset.copyOf(stream.iterator()),
|
||||
stream.collect(collectingAndThen(toList(), ImmutableSortedMultiset::copyOf)));
|
||||
return ImmutableSortedMultiset.copyOf(stream.iterator());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
|
||||
@@ -3,10 +3,7 @@ package tech.picnic.errorprone.refastertemplates;
|
||||
import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
import static java.util.stream.Collectors.collectingAndThen;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
@@ -91,14 +88,14 @@ final class ImmutableSortedSetTemplates {
|
||||
// `reverseOrder`.) Worth the hassle?
|
||||
static final class IterableToImmutableSortedSet<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
ImmutableSet<T> before(T[] iterable) {
|
||||
ImmutableSortedSet<T> before(T[] iterable) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSortedSet.<T>naturalOrder().add(iterable).build(),
|
||||
Arrays.stream(iterable).collect(toImmutableSortedSet(naturalOrder())));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableSet<T> before(Iterator<T> iterable) {
|
||||
ImmutableSortedSet<T> before(Iterator<T> iterable) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSortedSet.copyOf(naturalOrder(), iterable),
|
||||
ImmutableSortedSet.<T>naturalOrder().addAll(iterable).build(),
|
||||
@@ -106,7 +103,7 @@ final class ImmutableSortedSetTemplates {
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableSet<T> before(Iterable<T> iterable) {
|
||||
ImmutableSortedSet<T> before(Iterable<T> iterable) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSortedSet.copyOf(naturalOrder(), iterable),
|
||||
ImmutableSortedSet.<T>naturalOrder().addAll(iterable).build(),
|
||||
@@ -134,9 +131,7 @@ final class ImmutableSortedSetTemplates {
|
||||
static final class StreamToImmutableSortedSet<T extends Comparable<? super T>> {
|
||||
@BeforeTemplate
|
||||
ImmutableSortedSet<T> before(Stream<T> stream) {
|
||||
return Refaster.anyOf(
|
||||
ImmutableSortedSet.copyOf(stream.iterator()),
|
||||
stream.collect(collectingAndThen(toList(), ImmutableSortedSet::copyOf)));
|
||||
return ImmutableSortedSet.copyOf(stream.iterator());
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
|
||||
@@ -113,7 +113,7 @@ final class OptionalTemplates {
|
||||
/** Prefer {@link Optional#filter(Predicate)} over usage of the ternary operator. */
|
||||
// XXX: This rule may introduce a compilation error: the `test` expression may reference a
|
||||
// non-effectively final variable, which is not allowed in the replacement lambda expression.
|
||||
// Maybe our RefasterCheck should test `compilesWithFix`?
|
||||
// Maybe our `Refaster` checker should test `compilesWithFix`?
|
||||
abstract static class TernaryOperatorOptionalPositiveFiltering<T> {
|
||||
@Placeholder
|
||||
abstract boolean test(T value);
|
||||
@@ -133,7 +133,7 @@ final class OptionalTemplates {
|
||||
/** Prefer {@link Optional#filter(Predicate)} over usage of the ternary operator. */
|
||||
// XXX: This rule may introduce a compilation error: the `test` expression may reference a
|
||||
// non-effectively final variable, which is not allowed in the replacement lambda expression.
|
||||
// Maybe our RefasterCheck should test `compilesWithFix`?
|
||||
// Maybe our `Refaster` checker should test `compilesWithFix`?
|
||||
abstract static class TernaryOperatorOptionalNegativeFiltering<T> {
|
||||
@Placeholder
|
||||
abstract boolean test(T value);
|
||||
@@ -166,6 +166,11 @@ final class OptionalTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Optional#map} over a {@link Optional#flatMap} which wraps the result of a
|
||||
* transformation in an {@link Optional}; the former operation transforms {@code null} to {@link
|
||||
* Optional#empty()}.
|
||||
*/
|
||||
abstract static class MapToNullable<T, S> {
|
||||
@Placeholder
|
||||
abstract S toNullableFunction(@MayOptionallyUse T element);
|
||||
@@ -234,9 +239,16 @@ final class OptionalTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
/** Within a stream's map operation unconditional {@link Optional#get()} calls can be avoided. */
|
||||
// XXX: An alternative approach is to `.flatMap(Optional::stream)`. That may be a bit longer, but
|
||||
// yield nicer code. Think about it.
|
||||
/**
|
||||
* Within a stream's map operation unconditional {@link Optional#orElseThrow()} calls can be
|
||||
* avoided.
|
||||
*
|
||||
* <p><strong>Warning:</strong> this rewrite rule is not completely behavior preserving. The
|
||||
* original code throws an exception if the mapping operation does not produce a value, while the
|
||||
* replacement does not.
|
||||
*/
|
||||
// XXX: An alternative approach is to use `.flatMap(Optional::stream)`. That may be a bit longer,
|
||||
// but yields nicer code. Think about it.
|
||||
abstract static class StreamMapToOptionalGet<T, S> {
|
||||
@Placeholder
|
||||
abstract Optional<S> toOptionalFunction(@MayOptionallyUse T element);
|
||||
@@ -306,6 +318,7 @@ final class OptionalTemplates {
|
||||
/** Prefer {@link Optional#or(Supplier)} over more verbose alternatives. */
|
||||
abstract static class OptionalOrOtherOptional<T> {
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("NestedOptionals" /* Auto-fix for the `NestedOptionals` check. */)
|
||||
Optional<T> before(Optional<T> optional1, Optional<T> optional2) {
|
||||
// XXX: Note that rewriting the first and third variant will change the code's behavior if
|
||||
// `optional2` has side-effects.
|
||||
|
||||
@@ -157,8 +157,8 @@ final class ReactorTemplates {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Flux#concatMapIterable(Function)} over {@link Flux#concatMapIterable(Function)},
|
||||
* as the former has equivalent semantics but a clearer name.
|
||||
* Prefer {@link Flux#concatMapIterable(Function)} over {@link Flux#flatMapIterable(Function)}, as
|
||||
* the former has equivalent semantics but a clearer name.
|
||||
*/
|
||||
static final class FluxConcatMapIterable<T, S> {
|
||||
@BeforeTemplate
|
||||
@@ -224,6 +224,32 @@ final class ReactorTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Mono#cast(Class)} over {@link Mono#map(Function)} with a cast. */
|
||||
static final class MonoCast<T, S> {
|
||||
@BeforeTemplate
|
||||
Mono<S> before(Mono<T> mono) {
|
||||
return mono.map(Refaster.<S>clazz()::cast);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Mono<S> after(Mono<T> mono) {
|
||||
return mono.cast(Refaster.<S>clazz());
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Flux#cast(Class)} over {@link Flux#map(Function)} with a cast. */
|
||||
static final class FluxCast<T, S> {
|
||||
@BeforeTemplate
|
||||
Flux<S> before(Flux<T> flux) {
|
||||
return flux.map(Refaster.<S>clazz()::cast);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
Flux<S> after(Flux<T> flux) {
|
||||
return flux.cast(Refaster.<S>clazz());
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link PublisherProbe#empty()}} over more verbose alternatives. */
|
||||
static final class PublisherProbeEmpty<T> {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
package tech.picnic.errorprone.refastertemplates;
|
||||
|
||||
import static org.springframework.http.HttpMethod.GET;
|
||||
import static org.springframework.http.HttpMethod.HEAD;
|
||||
import static org.springframework.http.HttpMethod.OPTIONS;
|
||||
import static org.springframework.http.HttpMethod.PATCH;
|
||||
import static org.springframework.http.HttpMethod.POST;
|
||||
import static org.springframework.http.HttpMethod.PUT;
|
||||
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
|
||||
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import com.google.errorprone.refaster.annotation.Repeated;
|
||||
import java.util.function.Function;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec;
|
||||
import org.springframework.web.reactive.function.client.WebClient.RequestBodyUriSpec;
|
||||
import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;
|
||||
import org.springframework.web.reactive.function.client.WebClient.RequestHeadersUriSpec;
|
||||
|
||||
/**
|
||||
* Refaster templates related to expressions dealing with {@link
|
||||
@@ -23,7 +36,7 @@ final class WebClientTemplates {
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
WebTestClient.RequestHeadersSpec<?> before2(
|
||||
WebTestClient.RequestHeadersSpec<?> before(
|
||||
WebTestClient.RequestBodySpec requestBodySpec, T value) {
|
||||
return requestBodySpec.body(fromValue(value));
|
||||
}
|
||||
@@ -33,4 +46,159 @@ final class WebClientTemplates {
|
||||
return requestBodySpec.bodyValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link WebClient#get()} over {@link WebClient#method(HttpMethod)} with {@link
|
||||
* HttpMethod#GET}.
|
||||
*/
|
||||
static final class WebClientGet {
|
||||
@BeforeTemplate
|
||||
RequestHeadersSpec<?> before(WebClient webClient) {
|
||||
return webClient.method(GET);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
WebTestClient.RequestHeadersSpec<?> before(WebTestClient webClient) {
|
||||
return webClient.method(GET);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
RequestHeadersSpec<?> after(WebClient webClient) {
|
||||
return webClient.get();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link WebClient#head()} over {@link WebClient#method(HttpMethod)} with {@link
|
||||
* HttpMethod#HEAD}.
|
||||
*/
|
||||
static final class WebClientHead {
|
||||
@BeforeTemplate
|
||||
RequestHeadersSpec<?> before(WebClient webClient) {
|
||||
return webClient.method(HEAD);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
WebTestClient.RequestHeadersSpec<?> before(WebTestClient webClient) {
|
||||
return webClient.method(HEAD);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
RequestHeadersSpec<?> after(WebClient webClient) {
|
||||
return webClient.head();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link WebClient#options()} over {@link WebClient#method(HttpMethod)} with {@link
|
||||
* HttpMethod#OPTIONS}.
|
||||
*/
|
||||
static final class WebClientOptions {
|
||||
@BeforeTemplate
|
||||
RequestHeadersSpec<?> before(WebClient webClient) {
|
||||
return webClient.method(OPTIONS);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
WebTestClient.RequestHeadersSpec<?> before(WebTestClient webClient) {
|
||||
return webClient.method(OPTIONS);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
RequestHeadersSpec<?> after(WebClient webClient) {
|
||||
return webClient.options();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link WebClient#patch()} over {@link WebClient#method(HttpMethod)} with {@link
|
||||
* HttpMethod#PATCH}.
|
||||
*/
|
||||
static final class WebClientPatch {
|
||||
@BeforeTemplate
|
||||
RequestBodyUriSpec before(WebClient webClient) {
|
||||
return webClient.method(PATCH);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
WebTestClient.RequestBodyUriSpec before(WebTestClient webClient) {
|
||||
return webClient.method(PATCH);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
RequestBodyUriSpec after(WebClient webClient) {
|
||||
return webClient.patch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link WebClient#post()} over {@link WebClient#method(HttpMethod)} with {@link
|
||||
* HttpMethod#POST}.
|
||||
*/
|
||||
static final class WebClientPost {
|
||||
@BeforeTemplate
|
||||
RequestBodyUriSpec before(WebClient webClient) {
|
||||
return webClient.method(POST);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
WebTestClient.RequestBodyUriSpec before(WebTestClient webClient) {
|
||||
return webClient.method(POST);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
RequestBodyUriSpec after(WebClient webClient) {
|
||||
return webClient.post();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link WebClient#put()} over {@link WebClient#method(HttpMethod)} with {@link
|
||||
* HttpMethod#PUT}.
|
||||
*/
|
||||
static final class WebClientPut {
|
||||
@BeforeTemplate
|
||||
RequestBodyUriSpec before(WebClient webClient) {
|
||||
return webClient.method(PUT);
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
WebTestClient.RequestBodyUriSpec before(WebTestClient webClient) {
|
||||
return webClient.method(PUT);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
RequestBodyUriSpec after(WebClient webClient) {
|
||||
return webClient.put();
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily use {@link RequestHeadersUriSpec#uri(Function)}. */
|
||||
abstract static class RequestHeadersUriSpecUri {
|
||||
@BeforeTemplate
|
||||
RequestHeadersSpec<?> before(
|
||||
RequestHeadersUriSpec<?> requestHeadersUriSpec,
|
||||
String path,
|
||||
@Repeated Object uriVariables) {
|
||||
return requestHeadersUriSpec.uri(
|
||||
uriBuilder -> uriBuilder.path(path).build(Refaster.asVarargs(uriVariables)));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
WebTestClient.RequestHeadersSpec<?> before(
|
||||
WebTestClient.RequestHeadersUriSpec<?> requestHeadersUriSpec,
|
||||
String path,
|
||||
@Repeated Object uriVariables) {
|
||||
return requestHeadersUriSpec.uri(
|
||||
uriBuilder -> uriBuilder.path(path).build(Refaster.asVarargs(uriVariables)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
RequestHeadersSpec<?> after(
|
||||
RequestHeadersUriSpec<?> requestHeadersUriSpec,
|
||||
String path,
|
||||
@Repeated Object uriVariables) {
|
||||
return requestHeadersUriSpec.uri(path, Refaster.asVarargs(uriVariables));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class AmbiguousJsonCreatorCheckTest {
|
||||
final class AmbiguousJsonCreatorTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(AmbiguousJsonCreatorCheck.class, getClass())
|
||||
CompilationTestHelper.newInstance(AmbiguousJsonCreator.class, getClass())
|
||||
.expectErrorMessage(
|
||||
"X",
|
||||
containsPattern("`JsonCreator.Mode` should be set for single-argument creators"));
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(AmbiguousJsonCreatorCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(AmbiguousJsonCreator.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
@@ -112,7 +112,6 @@ final class AmbiguousJsonCreatorCheckTest {
|
||||
" return new F(s);",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class AssertJIsNullTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(AssertJIsNull.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(AssertJIsNull.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.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\");",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"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();",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class AutowiredConstructorCheckTest {
|
||||
final class AutowiredConstructorTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(AutowiredConstructorCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(AutowiredConstructor.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(AutowiredConstructorCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(AutowiredConstructor.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
@@ -27,27 +27,34 @@ public final class AutowiredConstructorCheckTest {
|
||||
" }",
|
||||
"",
|
||||
" class B {",
|
||||
" @Autowired void setProperty(Object o) {}",
|
||||
" @Autowired",
|
||||
" void setProperty(Object o) {}",
|
||||
" }",
|
||||
"",
|
||||
" class C {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @Autowired C() {}",
|
||||
" @Autowired",
|
||||
" C() {}",
|
||||
" }",
|
||||
"",
|
||||
" class D {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @Autowired D(String x) {}",
|
||||
" @Autowired",
|
||||
" D(String x) {}",
|
||||
" }",
|
||||
"",
|
||||
" class E {",
|
||||
" @Autowired E() {}",
|
||||
" @Autowired",
|
||||
" E() {}",
|
||||
"",
|
||||
" E(String x) {}",
|
||||
" }",
|
||||
"",
|
||||
" class F {",
|
||||
" F() {}",
|
||||
" @Autowired F(String x) {}",
|
||||
"",
|
||||
" @Autowired",
|
||||
" F(String x) {}",
|
||||
" }",
|
||||
"",
|
||||
" class G {",
|
||||
@@ -55,7 +62,8 @@ public final class AutowiredConstructorCheckTest {
|
||||
" }",
|
||||
"",
|
||||
" class H {",
|
||||
" @SafeVarargs H(List<String>... lists) {}",
|
||||
" @SafeVarargs",
|
||||
" H(List<String>... lists) {}",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
@@ -70,11 +78,14 @@ public final class AutowiredConstructorCheckTest {
|
||||
"",
|
||||
"interface Container {",
|
||||
" class A {",
|
||||
" @Autowired @Deprecated A() {}",
|
||||
" @Autowired",
|
||||
" @Deprecated",
|
||||
" A() {}",
|
||||
" }",
|
||||
"",
|
||||
" class B {",
|
||||
" @Autowired B(String x) {}",
|
||||
" @Autowired",
|
||||
" B(String x) {}",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
@@ -83,10 +94,13 @@ public final class AutowiredConstructorCheckTest {
|
||||
"",
|
||||
"interface Container {",
|
||||
" class A {",
|
||||
" @Deprecated A() {}",
|
||||
"",
|
||||
" @Deprecated",
|
||||
" A() {}",
|
||||
" }",
|
||||
"",
|
||||
" class B {",
|
||||
"",
|
||||
" B(String x) {}",
|
||||
" }",
|
||||
"}")
|
||||
@@ -1,156 +0,0 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class CanonicalAnnotationSyntaxCheckTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(CanonicalAnnotationSyntaxCheck.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(CanonicalAnnotationSyntaxCheck.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.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();",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/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();",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/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();",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class CanonicalAnnotationSyntaxTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(CanonicalAnnotationSyntax.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(CanonicalAnnotationSyntax.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.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();",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/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();",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/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();",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers.SECOND;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class CollectorMutabilityTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(CollectorMutability.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(CollectorMutability.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.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());",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacementFirstSuggestedFix() {
|
||||
refactoringTestHelper
|
||||
.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());",
|
||||
" }",
|
||||
"}")
|
||||
.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());",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacementSecondSuggestedFix() {
|
||||
refactoringTestHelper
|
||||
.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());",
|
||||
" }",
|
||||
"}")
|
||||
.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));",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class EmptyMethodCheckTest {
|
||||
final class EmptyMethodTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(EmptyMethodCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(EmptyMethod.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(EmptyMethodCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
@@ -0,0 +1,151 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class ErrorProneTestHelperSourceFormatTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(ErrorProneTestHelperSourceFormat.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(
|
||||
ErrorProneTestHelperSourceFormat.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.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(\"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();
|
||||
}
|
||||
|
||||
@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.
|
||||
*/
|
||||
refactoringTestHelper
|
||||
.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 {",
|
||||
" 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(",
|
||||
" \"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 {",
|
||||
" 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(",
|
||||
" \"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);
|
||||
}
|
||||
}
|
||||
@@ -3,23 +3,23 @@ package tech.picnic.errorprone.bugpatterns;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class ExplicitEnumOrderingCheckTest {
|
||||
final class ExplicitEnumOrderingTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(ExplicitEnumOrderingCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(ExplicitEnumOrdering.class, getClass());
|
||||
|
||||
@Test
|
||||
void Identification() {
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static java.lang.annotation.RetentionPolicy.SOURCE;",
|
||||
"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.Ordering;",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.Ordering;",
|
||||
"import java.lang.annotation.RetentionPolicy;",
|
||||
"import java.time.chrono.IsoEra;",
|
||||
"",
|
||||
@@ -7,11 +7,11 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class FluxFlatMapUsageCheckTest {
|
||||
final class FluxFlatMapUsageTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(FluxFlatMapUsageCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(FluxFlatMapUsage.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
newInstance(FluxFlatMapUsageCheck.class, getClass());
|
||||
newInstance(FluxFlatMapUsage.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
@@ -20,8 +20,8 @@ final class FluxFlatMapUsageCheckTest {
|
||||
"A.java",
|
||||
"import java.util.function.BiFunction;",
|
||||
"import java.util.function.Function;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
"",
|
||||
"class A {",
|
||||
" void m() {",
|
||||
@@ -5,11 +5,11 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class FormatStringConcatenationCheckTest {
|
||||
final class FormatStringConcatenationTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(FormatStringConcatenationCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(FormatStringConcatenation.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(FormatStringConcatenationCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(FormatStringConcatenation.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
@@ -23,8 +23,8 @@ public final class FormatStringConcatenationCheckTest {
|
||||
"import static org.assertj.core.api.Assertions.assertThat;",
|
||||
"import static org.assertj.core.api.SoftAssertions.assertSoftly;",
|
||||
"",
|
||||
"import java.util.Locale;",
|
||||
"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;",
|
||||
@@ -269,9 +269,9 @@ public final class FormatStringConcatenationCheckTest {
|
||||
" LOG.error(\"{} \" + toString(), \"arg\");",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" LOG.error((Marker) null,\"str \" + toString());",
|
||||
" LOG.error((Marker) null, \"str \" + toString());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" LOG.error((Marker) null,\"{} \" + toString(), \"arg\");",
|
||||
" LOG.error((Marker) null, \"{} \" + toString(), \"arg\");",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" LOG.info(\"str \" + toString());",
|
||||
@@ -279,9 +279,9 @@ public final class FormatStringConcatenationCheckTest {
|
||||
" LOG.info(\"{} \" + toString(), \"arg\");",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" LOG.info((Marker) null,\"str \" + toString());",
|
||||
" LOG.info((Marker) null, \"str \" + toString());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" LOG.info((Marker) null,\"{} \" + toString(), \"arg\");",
|
||||
" LOG.info((Marker) null, \"{} \" + toString(), \"arg\");",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" LOG.trace(\"str \" + toString());",
|
||||
@@ -289,9 +289,9 @@ public final class FormatStringConcatenationCheckTest {
|
||||
" LOG.trace(\"{} \" + toString(), \"arg\");",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" LOG.trace((Marker) null,\"str \" + toString());",
|
||||
" LOG.trace((Marker) null, \"str \" + toString());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" LOG.trace((Marker) null,\"{} \" + toString(), \"arg\");",
|
||||
" LOG.trace((Marker) null, \"{} \" + toString(), \"arg\");",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" LOG.warn(\"str \" + toString());",
|
||||
@@ -299,9 +299,9 @@ public final class FormatStringConcatenationCheckTest {
|
||||
" LOG.warn(\"{} \" + toString(), \"arg\");",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" LOG.warn((Marker) null,\"str \" + toString());",
|
||||
" LOG.warn((Marker) null, \"str \" + toString());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" LOG.warn((Marker) null,\"{} \" + toString(), \"arg\");",
|
||||
" LOG.warn((Marker) null, \"{} \" + toString(), \"arg\");",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
@@ -316,7 +316,6 @@ public final class FormatStringConcatenationCheckTest {
|
||||
"import static org.assertj.core.api.Assertions.assertThat;",
|
||||
"",
|
||||
"import java.util.Locale;",
|
||||
"import java.util.Formatter;",
|
||||
"import org.slf4j.Logger;",
|
||||
"import org.slf4j.LoggerFactory;",
|
||||
"import org.slf4j.Marker;",
|
||||
@@ -366,7 +365,6 @@ public final class FormatStringConcatenationCheckTest {
|
||||
"import static org.assertj.core.api.Assertions.assertThat;",
|
||||
"",
|
||||
"import java.util.Locale;",
|
||||
"import java.util.Formatter;",
|
||||
"import org.slf4j.Logger;",
|
||||
"import org.slf4j.LoggerFactory;",
|
||||
"import org.slf4j.Marker;",
|
||||
@@ -400,6 +398,7 @@ public final class FormatStringConcatenationCheckTest {
|
||||
" String.format(\"{} \" + toString(), \"arg\");",
|
||||
" String.format(Locale.ROOT, \"{} \" + toString(), \"arg\");",
|
||||
" }",
|
||||
"",
|
||||
" void slf4j() {",
|
||||
" LOG.debug(\"str {}\", toString());",
|
||||
" LOG.debug((Marker) null, \"str {}\", toString());",
|
||||
@@ -6,11 +6,11 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class IdentityConversionCheckTest {
|
||||
final class IdentityConversionTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(IdentityConversionCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(IdentityConversion.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(IdentityConversionCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(IdentityConversion.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
@@ -121,8 +121,9 @@ final class IdentityConversionCheckTest {
|
||||
" ImmutableBiMap<Object, Object> o1 = ImmutableBiMap.copyOf(ImmutableBiMap.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableList<Object> o2 = ImmutableList.copyOf(ImmutableList.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableListMultimap<Object, Object> o3 = ImmutableListMultimap.copyOf(ImmutableListMultimap.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:",
|
||||
@@ -135,8 +136,9 @@ final class IdentityConversionCheckTest {
|
||||
" ImmutableRangeSet<String> o8 = ImmutableRangeSet.copyOf(ImmutableRangeSet.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableSet<Object> o9 = ImmutableSet.copyOf(ImmutableSet.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableSetMultimap<Object, Object> o10 = ImmutableSetMultimap.copyOf(ImmutableSetMultimap.of());",
|
||||
" ImmutableSetMultimap<Object, Object> o10 =",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableSetMultimap.copyOf(ImmutableSetMultimap.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableTable<Object, Object, Object> o11 = ImmutableTable.copyOf(ImmutableTable.of());",
|
||||
"",
|
||||
@@ -171,8 +173,8 @@ final class IdentityConversionCheckTest {
|
||||
"import com.google.common.collect.ImmutableCollection;",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import java.util.Collection;",
|
||||
"import java.util.ArrayList;",
|
||||
"import java.util.Collection;",
|
||||
"import org.reactivestreams.Publisher;",
|
||||
"import reactor.adapter.rxjava.RxJava2Adapter;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
@@ -204,8 +206,7 @@ final class IdentityConversionCheckTest {
|
||||
" Object o1 = ImmutableSet.copyOf(ImmutableList.of());",
|
||||
" Object o2 = ImmutableSet.copyOf(ImmutableSet.of());",
|
||||
"",
|
||||
" when(\"foo\".contains(\"f\"))",
|
||||
" .thenAnswer(inv-> ImmutableSet.copyOf(ImmutableList.of(1)));",
|
||||
" when(\"foo\".contains(\"f\")).thenAnswer(inv -> ImmutableSet.copyOf(ImmutableList.of(1)));",
|
||||
" }",
|
||||
"",
|
||||
" void bar(Publisher<Integer> publisher) {}",
|
||||
@@ -217,8 +218,8 @@ final class IdentityConversionCheckTest {
|
||||
"import com.google.common.collect.ImmutableCollection;",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import java.util.Collection;",
|
||||
"import java.util.ArrayList;",
|
||||
"import java.util.Collection;",
|
||||
"import org.reactivestreams.Publisher;",
|
||||
"import reactor.adapter.rxjava.RxJava2Adapter;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
@@ -250,8 +251,7 @@ final class IdentityConversionCheckTest {
|
||||
" Object o1 = ImmutableSet.copyOf(ImmutableList.of());",
|
||||
" Object o2 = ImmutableSet.of();",
|
||||
"",
|
||||
" when(\"foo\".contains(\"f\"))",
|
||||
" .thenAnswer(inv-> ImmutableSet.copyOf(ImmutableList.of(1)));",
|
||||
" when(\"foo\".contains(\"f\")).thenAnswer(inv -> ImmutableSet.copyOf(ImmutableList.of(1)));",
|
||||
" }",
|
||||
"",
|
||||
" void bar(Publisher<Integer> publisher) {}",
|
||||
@@ -269,9 +269,6 @@ final class IdentityConversionCheckTest {
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import java.util.ArrayList;",
|
||||
"import reactor.adapter.rxjava.RxJava2Adapter;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
"",
|
||||
"public final class Foo {",
|
||||
" public void foo() {",
|
||||
@@ -288,9 +285,6 @@ final class IdentityConversionCheckTest {
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import java.util.ArrayList;",
|
||||
"import reactor.adapter.rxjava.RxJava2Adapter;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
"",
|
||||
"public final class Foo {",
|
||||
" public void foo() {",
|
||||
@@ -0,0 +1,182 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class ImmutablesSortedSetComparatorTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(ImmutablesSortedSetComparator.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(ImmutablesSortedSetComparator.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.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();",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.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();",
|
||||
" }",
|
||||
"}")
|
||||
.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();",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacementWithImportClash() {
|
||||
refactoringTestHelper
|
||||
.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();",
|
||||
" }",
|
||||
"}")
|
||||
.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();",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class JUnitMethodDeclarationCheckTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(JUnitMethodDeclarationCheck.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(JUnitMethodDeclarationCheck.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.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 {",
|
||||
" @BeforeAll void setUp1() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @BeforeAll public void setUp2() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @BeforeAll protected void setUp3() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @BeforeAll private void setUp4() {}",
|
||||
"",
|
||||
" @BeforeEach void setup5() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @BeforeEach public void setUp6() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @BeforeEach protected void setUp7() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @BeforeEach private void setUp8() {}",
|
||||
"",
|
||||
" @AfterEach void tearDown1() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @AfterEach public void tearDown2() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @AfterEach protected void tearDown3() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @AfterEach private void tearDown4() {}",
|
||||
"",
|
||||
" @AfterAll void tearDown5() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @AfterAll public void tearDown6() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @AfterAll protected void tearDown7() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @AfterAll private void tearDown8() {}",
|
||||
"",
|
||||
" @Test void method1() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @Test void testMethod2() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @Test public void method3() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @Test protected void method4() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @Test private void method5() {}",
|
||||
"",
|
||||
" @ParameterizedTest void method6() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @ParameterizedTest void testMethod7() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @ParameterizedTest public void method8() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @ParameterizedTest protected void method9() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @ParameterizedTest private void method10() {}",
|
||||
"",
|
||||
" @BeforeEach @BeforeAll @AfterEach @AfterAll void testNonTestMethod1() {}",
|
||||
" public void testNonTestMethod2() {}",
|
||||
" protected void testNonTestMethod3() {}",
|
||||
" private void testNonTestMethod4() {}",
|
||||
" @Test void test5() {}",
|
||||
"",
|
||||
" // BUG: Diagnostic contains: (but note that a method named `overload` already exists in this class)",
|
||||
" @Test void testOverload() {}",
|
||||
" void overload() {}",
|
||||
" // BUG: Diagnostic contains: (but note that `arguments` is already statically imported)",
|
||||
" @Test void testArguments() {}",
|
||||
" // BUG: Diagnostic contains: (but note that `public` is a reserved keyword)",
|
||||
" @Test void testPublic() {}",
|
||||
"}")
|
||||
.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 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 testOverload() {}",
|
||||
" @Override void overload() {}",
|
||||
" @Override @Test void testArguments() {}",
|
||||
" @Override @Test void testPublic() {}",
|
||||
"}")
|
||||
.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() {}",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @AfterAll private void tearDown() {}",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @Test final void testMethod2() {}",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/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 {",
|
||||
" @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 testOverload() {}",
|
||||
" void overload() {}",
|
||||
" @Test protected void testArguments() {}",
|
||||
" @Test private void testClass() {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/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 {",
|
||||
" @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 testOverload() {}",
|
||||
" void overload() {}",
|
||||
" @Test void testArguments() {}",
|
||||
" @Test void testClass() {}",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class JUnitMethodDeclarationTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(JUnitMethodDeclaration.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(JUnitMethodDeclaration.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.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 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 `overload` already exists in this",
|
||||
" // class)",
|
||||
" 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 a reserved keyword)",
|
||||
" void testPublic() {}",
|
||||
"}")
|
||||
.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 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 testOverload() {}",
|
||||
"",
|
||||
" @Override",
|
||||
" void overload() {}",
|
||||
"",
|
||||
" @Override",
|
||||
" @Test",
|
||||
" void testArguments() {}",
|
||||
"",
|
||||
" @Override",
|
||||
" @Test",
|
||||
" void testPublic() {}",
|
||||
"}")
|
||||
.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() {}",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/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 testOverload() {}",
|
||||
"",
|
||||
" void overload() {}",
|
||||
"",
|
||||
" @Test",
|
||||
" protected void testArguments() {}",
|
||||
"",
|
||||
" @Test",
|
||||
" private void testClass() {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/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 testOverload() {}",
|
||||
"",
|
||||
" void overload() {}",
|
||||
"",
|
||||
" @Test",
|
||||
" void testArguments() {}",
|
||||
"",
|
||||
" @Test",
|
||||
" void testClass() {}",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class LexicographicalAnnotationAttributeListingCheckTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(
|
||||
LexicographicalAnnotationAttributeListingCheck.class, getClass());
|
||||
private final CompilationTestHelper restrictedCompilationTestHelper =
|
||||
CompilationTestHelper.newInstance(
|
||||
LexicographicalAnnotationAttributeListingCheck.class, getClass())
|
||||
.setArgs(
|
||||
ImmutableList.of(
|
||||
"-XepOpt:LexicographicalAnnotationAttributeListing:Includes=pkg.A.Foo,pkg.A.Bar",
|
||||
"-XepOpt:LexicographicalAnnotationAttributeListing:Excludes=pkg.A.Bar#value"));
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(
|
||||
LexicographicalAnnotationAttributeListingCheck.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static java.math.RoundingMode.UP;",
|
||||
"import static java.math.RoundingMode.DOWN;",
|
||||
"",
|
||||
"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;",
|
||||
"",
|
||||
"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 Dummy {}",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
// XXX: Note that in the output below in one instance redundant `value =` assignments are
|
||||
// introduced. Avoiding that might make the code too complex. Instead, users can have the
|
||||
// `CanonicalAnnotationSyntaxCheck` correct the situation in a subsequent run.
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"import static java.math.RoundingMode.UP;",
|
||||
"import static java.math.RoundingMode.DOWN;",
|
||||
"",
|
||||
"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(
|
||||
"out/A.java",
|
||||
"import static java.math.RoundingMode.UP;",
|
||||
"import static java.math.RoundingMode.DOWN;",
|
||||
"",
|
||||
"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);
|
||||
}
|
||||
|
||||
@Test
|
||||
void filtering() {
|
||||
/* Some violations are not flagged because they are not in- or excluded. */
|
||||
restrictedCompilationTestHelper
|
||||
.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();",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class LexicographicalAnnotationAttributeListingTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(
|
||||
LexicographicalAnnotationAttributeListing.class, getClass());
|
||||
private final CompilationTestHelper restrictedCompilationTestHelper =
|
||||
CompilationTestHelper.newInstance(LexicographicalAnnotationAttributeListing.class, getClass())
|
||||
.setArgs(
|
||||
ImmutableList.of(
|
||||
"-XepOpt:LexicographicalAnnotationAttributeListing:Includes=pkg.A.Foo,pkg.A.Bar",
|
||||
"-XepOpt:LexicographicalAnnotationAttributeListing:Excludes=pkg.A.Bar#value"));
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(
|
||||
LexicographicalAnnotationAttributeListing.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.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 {}",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
// XXX: Note that in the output below in one instance redundant `value =` assignments are
|
||||
// introduced. Avoiding that might make the code too complex. Instead, users can have the
|
||||
// `CanonicalAnnotationSyntax` checker correct the situation in a subsequent run.
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/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();",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/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();",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void filtering() {
|
||||
/* Some violations are not flagged because they are not in- or excluded. */
|
||||
restrictedCompilationTestHelper
|
||||
.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();",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.common.base.Predicates.containsPattern;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class LexicographicalAnnotationListingCheckTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(LexicographicalAnnotationListingCheck.class, getClass())
|
||||
.expectErrorMessage(
|
||||
"X", containsPattern("Sort annotations lexicographically where possible"));
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(
|
||||
LexicographicalAnnotationListingCheck.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import java.lang.annotation.Repeatable;",
|
||||
"",
|
||||
"interface A {",
|
||||
" @Repeatable(Foos.class)",
|
||||
" @interface Foo {",
|
||||
" String[] value() default {};",
|
||||
" int[] ints() default {};",
|
||||
" Bar[] anns() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Bar {",
|
||||
" String[] value() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Baz {",
|
||||
" String[] str() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Foos {",
|
||||
" Foo[] value();",
|
||||
" }",
|
||||
"",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @Foo @Bar A unsortedSimpleCase();",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @Foo() @Bar() A unsortedWithParens();",
|
||||
" @Foo() A onlyOneAnnotation();",
|
||||
" @Bar @Foo() A sortedAnnotationsOneWithParens();",
|
||||
"",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @Foo @Baz @Bar A threeUnsortedAnnotationsSameInitialLetter();",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @Bar @Foo() @Baz A firstOrderedWithTwoUnsortedAnnotations();",
|
||||
" @Bar @Baz @Foo() A threeSortedAnnotations();",
|
||||
"",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @Foo({\"b\"}) @Bar({\"a\"}) A unsortedWithStringAttributes();",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @Baz(str = {\"a\", \"b\"}) @Foo(ints = {1, 0}) @Bar A unsortedWithAttributes();",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @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 matches: X",
|
||||
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")}) @Bar @Foo(ints = {1, 2}) A unsortedRepeatableAnnotation();",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"import java.lang.annotation.Repeatable;",
|
||||
"interface A {",
|
||||
" @Repeatable(Foos.class)",
|
||||
" @interface Foo {",
|
||||
" String[] value() default {};",
|
||||
" int[] ints() default {};",
|
||||
" Bar[] anns() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Bar {",
|
||||
" String[] value() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Baz {",
|
||||
" String[] str() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Foos {",
|
||||
" Foo[] value();",
|
||||
" }",
|
||||
"",
|
||||
" @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();",
|
||||
"",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"import java.lang.annotation.Repeatable;",
|
||||
"interface A {",
|
||||
" @Repeatable(Foos.class)",
|
||||
" @interface Foo {",
|
||||
" String[] value() default {};",
|
||||
" int[] ints() default {};",
|
||||
" Bar[] anns() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Bar {",
|
||||
" String[] value() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Baz {",
|
||||
" String[] str() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Foos {",
|
||||
" Foo[] value();",
|
||||
" }",
|
||||
" @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();",
|
||||
"",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.common.base.Predicates.containsPattern;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class LexicographicalAnnotationListingTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(LexicographicalAnnotationListing.class, getClass())
|
||||
.expectErrorMessage(
|
||||
"X", containsPattern("Sort annotations lexicographically where possible"));
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(
|
||||
LexicographicalAnnotationListing.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import java.lang.annotation.Repeatable;",
|
||||
"",
|
||||
"interface A {",
|
||||
" @Repeatable(Foos.class)",
|
||||
" @interface Foo {",
|
||||
" String[] value() default {};",
|
||||
"",
|
||||
" int[] ints() default {};",
|
||||
"",
|
||||
" Bar[] anns() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Bar {",
|
||||
" String[] value() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Baz {",
|
||||
" String[] str() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Foos {",
|
||||
" Foo[] value();",
|
||||
" }",
|
||||
"",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @Foo",
|
||||
" @Bar",
|
||||
" A unsortedSimpleCase();",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @Foo()",
|
||||
" @Bar()",
|
||||
" A unsortedWithParens();",
|
||||
"",
|
||||
" @Foo()",
|
||||
" A onlyOneAnnotation();",
|
||||
"",
|
||||
" @Bar",
|
||||
" @Foo()",
|
||||
" A sortedAnnotationsOneWithParens();",
|
||||
"",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @Foo",
|
||||
" @Baz",
|
||||
" @Bar",
|
||||
" A threeUnsortedAnnotationsSameInitialLetter();",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @Bar",
|
||||
" @Foo()",
|
||||
" @Baz",
|
||||
" A firstOrderedWithTwoUnsortedAnnotations();",
|
||||
"",
|
||||
" @Bar",
|
||||
" @Baz",
|
||||
" @Foo()",
|
||||
" A threeSortedAnnotations();",
|
||||
"",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @Foo({\"b\"})",
|
||||
" @Bar({\"a\"})",
|
||||
" A unsortedWithStringAttributes();",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @Baz(str = {\"a\", \"b\"})",
|
||||
" @Foo(ints = {1, 0})",
|
||||
" @Bar",
|
||||
" A unsortedWithAttributes();",
|
||||
" // BUG: Diagnostic matches: X",
|
||||
" @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 matches: X",
|
||||
" @Foo(anns = {@Bar(\"b\"), @Bar(\"a\")})",
|
||||
" @Bar",
|
||||
" @Foo(ints = {1, 2})",
|
||||
" A unsortedRepeatableAnnotation();",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"import java.lang.annotation.Repeatable;",
|
||||
"",
|
||||
"interface A {",
|
||||
" @Repeatable(Foos.class)",
|
||||
" @interface Foo {",
|
||||
" String[] value() default {};",
|
||||
"",
|
||||
" int[] ints() default {};",
|
||||
"",
|
||||
" Bar[] anns() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Bar {",
|
||||
" String[] value() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Baz {",
|
||||
" String[] str() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Foos {",
|
||||
" Foo[] value();",
|
||||
" }",
|
||||
"",
|
||||
" @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();",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"import java.lang.annotation.Repeatable;",
|
||||
"",
|
||||
"interface A {",
|
||||
" @Repeatable(Foos.class)",
|
||||
" @interface Foo {",
|
||||
" String[] value() default {};",
|
||||
"",
|
||||
" int[] ints() default {};",
|
||||
"",
|
||||
" Bar[] anns() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Bar {",
|
||||
" String[] value() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Baz {",
|
||||
" String[] str() default {};",
|
||||
" }",
|
||||
"",
|
||||
" @interface Foos {",
|
||||
" Foo[] value();",
|
||||
" }",
|
||||
"",
|
||||
" @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();",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class MethodReferenceUsageCheckTest {
|
||||
final class MethodReferenceUsageTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(MethodReferenceUsageCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(MethodReferenceUsage.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(MethodReferenceUsageCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(MethodReferenceUsage.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
@@ -17,37 +17,62 @@ public final class MethodReferenceUsageCheckTest {
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import com.google.common.collect.Streams;",
|
||||
"import java.util.Map;",
|
||||
"import java.util.HashMap;",
|
||||
"import java.util.stream.Stream;",
|
||||
"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(); };",
|
||||
" private final Runnable thrower =",
|
||||
" () -> {",
|
||||
" throw new RuntimeException();",
|
||||
" };",
|
||||
"",
|
||||
" void unaryExternalStaticFunctionCalls() {",
|
||||
" s.forEach(String::valueOf);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s.forEach(v -> String.valueOf(v));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s.forEach((v) -> { String.valueOf(v); });",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s.forEach((Integer v) -> { { String.valueOf(v); } });",
|
||||
" s.forEach(v -> { String.valueOf(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)));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s.map((Integer v) -> { return String.valueOf(v); });",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s.map((final Integer v) -> { return (String.valueOf(v)); });",
|
||||
" s.map(v -> { String.valueOf(v); return 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));",
|
||||
@@ -59,14 +84,34 @@ public final class MethodReferenceUsageCheckTest {
|
||||
" // BUG: Diagnostic contains:",
|
||||
" m.forEach((k, v) -> m.put(k, v));",
|
||||
" m.forEach((k, v) -> m.put(v, k));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" m.forEach((Integer k, Integer v) -> { m.put(k, v); });",
|
||||
" m.forEach((k, v) -> { m.put(k, k); });",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" m.forEach((final Integer k, final Integer v) -> { { m.put(k, v); } });",
|
||||
" m.forEach((k, v) -> { { m.put(v, v); } });",
|
||||
" 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); });",
|
||||
" m.forEach(",
|
||||
" (k, v) -> {",
|
||||
" m.put(k, v);",
|
||||
" m.put(k, v);",
|
||||
" });",
|
||||
"",
|
||||
" Streams.zip(s, s, m::put);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
@@ -75,20 +120,45 @@ public final class MethodReferenceUsageCheckTest {
|
||||
" // 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)));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Streams.zip(s, s, (final Integer a, final Integer b) -> { return m.put(a, b); });",
|
||||
" Streams.zip(s, s, (a, b) -> { return m.put(b, b); });",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Streams.zip(s, s, (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); });",
|
||||
" 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(Integer::toString)` is ambiguous
|
||||
" s.map(i -> i.toString());",
|
||||
" s.map(i -> s.toString());",
|
||||
"",
|
||||
@@ -108,24 +178,36 @@ public final class MethodReferenceUsageCheckTest {
|
||||
" s.forEach(this::ivoid1);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" s.forEach(v -> ivoid1(v));",
|
||||
" // 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));",
|
||||
" // 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));",
|
||||
" // 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));",
|
||||
" // 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));",
|
||||
@@ -140,28 +222,43 @@ public final class MethodReferenceUsageCheckTest {
|
||||
" m.forEach(this::ivoid2);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" m.forEach((k, v) -> ivoid2(k, v));",
|
||||
" // 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));",
|
||||
" // 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));",
|
||||
" // 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));",
|
||||
" // 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); });",
|
||||
" receiver(",
|
||||
" i -> {",
|
||||
" Integer.toString(i);",
|
||||
" });",
|
||||
" }",
|
||||
"",
|
||||
" void assortedOtherEdgeCases() {",
|
||||
@@ -175,23 +272,47 @@ public final class MethodReferenceUsageCheckTest {
|
||||
" TernaryOp o7 = (a, b, c) -> b.concat(c);",
|
||||
" }",
|
||||
"",
|
||||
" void receiver(IntFunction<?> op) { }",
|
||||
" void receiver(IntConsumer op) { }",
|
||||
" void receiver(IntFunction<?> 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; }",
|
||||
" void receiver(IntConsumer op) {}",
|
||||
"",
|
||||
" 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; }",
|
||||
" 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);",
|
||||
@@ -210,9 +331,6 @@ public final class MethodReferenceUsageCheckTest {
|
||||
"import java.util.Collections;",
|
||||
"import java.util.List;",
|
||||
"import java.util.Map;",
|
||||
// Don't import `java.util.Set`; it should be added.
|
||||
"import java.util.function.IntConsumer;",
|
||||
"import java.util.function.IntFunction;",
|
||||
"import java.util.function.IntSupplier;",
|
||||
"import java.util.function.Supplier;",
|
||||
"import java.util.stream.Stream;",
|
||||
@@ -245,12 +363,19 @@ public final class MethodReferenceUsageCheckTest {
|
||||
" Stream.of((Map<?, ?>) null).map(Map::keySet).map(s -> s.size());",
|
||||
" }",
|
||||
"",
|
||||
" @Override int iint0() { return 0; }",
|
||||
" @Override",
|
||||
" int iint0() {",
|
||||
" return 0;",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" int iint0() { return 0; }",
|
||||
" int iint0() {",
|
||||
" return 0;",
|
||||
" }",
|
||||
"",
|
||||
" static int sint0() { return 0; }",
|
||||
" static int sint0() {",
|
||||
" return 0;",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
@@ -260,8 +385,6 @@ public final class MethodReferenceUsageCheckTest {
|
||||
"import java.util.List;",
|
||||
"import java.util.Map;",
|
||||
"import java.util.Set;",
|
||||
"import java.util.function.IntConsumer;",
|
||||
"import java.util.function.IntFunction;",
|
||||
"import java.util.function.IntSupplier;",
|
||||
"import java.util.function.Supplier;",
|
||||
"import java.util.stream.Stream;",
|
||||
@@ -294,12 +417,19 @@ public final class MethodReferenceUsageCheckTest {
|
||||
" Stream.of((Map<?, ?>) null).map(Map::keySet).map(Set::size);",
|
||||
" }",
|
||||
"",
|
||||
" @Override int iint0() { return 0; }",
|
||||
" @Override",
|
||||
" int iint0() {",
|
||||
" return 0;",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" int iint0() { return 0; }",
|
||||
" int iint0() {",
|
||||
" return 0;",
|
||||
" }",
|
||||
"",
|
||||
" static int sint0() { return 0; }",
|
||||
" static int sint0() {",
|
||||
" return 0;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
@@ -5,9 +5,9 @@ import static com.google.common.base.Predicates.containsPattern;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class MissingRefasterAnnotationCheckTest {
|
||||
final class MissingRefasterAnnotationTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(MissingRefasterAnnotationCheck.class, getClass())
|
||||
CompilationTestHelper.newInstance(MissingRefasterAnnotation.class, getClass())
|
||||
.expectErrorMessage(
|
||||
"X",
|
||||
containsPattern(
|
||||
@@ -5,11 +5,11 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class MockitoStubbingCheckTest {
|
||||
final class MockitoStubbingTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(MockitoStubbingCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(MockitoStubbing.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(MockitoStubbingCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(MockitoStubbing.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
@@ -42,8 +42,10 @@ public final class MockitoStubbingCheckTest {
|
||||
" 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\"));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" doAnswer(inv -> null).when(biConsumer).accept(ArgumentMatchers.eq(0), ArgumentMatchers.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()));",
|
||||
" }",
|
||||
@@ -71,7 +73,9 @@ public final class MockitoStubbingCheckTest {
|
||||
" 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(ArgumentMatchers.eq(0), ArgumentMatchers.eq(\"foo\"));",
|
||||
" doAnswer(inv -> null).when(biConsumer).accept(eq(hashCode()), eq(toString()));",
|
||||
" }",
|
||||
"}")
|
||||
@@ -0,0 +1,42 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class NestedOptionalsTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(NestedOptionals.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.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();",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class NonEmptyMonoTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(NonEmptyMono.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(NonEmptyMono.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.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();",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.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));",
|
||||
" }",
|
||||
"}")
|
||||
.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();",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class PrimitiveComparisonCheckTest {
|
||||
final class PrimitiveComparisonTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(PrimitiveComparisonCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(PrimitiveComparison.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(PrimitiveComparisonCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(PrimitiveComparison.class, getClass());
|
||||
|
||||
// XXX: There are no tests for multiple replacements within the same expression:
|
||||
// - Error Prone doesn't currently support this, it seems.
|
||||
@@ -99,10 +99,21 @@ public final class PrimitiveComparisonCheckTest {
|
||||
" cmp().thenComparingDouble(o -> Byte.valueOf((byte) 0));",
|
||||
" }",
|
||||
"",
|
||||
" private Comparator<Object> cmp() { return null; }",
|
||||
" private byte toPrimitive(Object o) { return 0; }",
|
||||
" private Byte toBoxed(Object o) { return 0; }",
|
||||
" private Function<Object, Byte> toBoxed() { return o -> 0; }",
|
||||
" private Comparator<Object> cmp() {",
|
||||
" return null;",
|
||||
" }",
|
||||
"",
|
||||
" private byte toPrimitive(Object o) {",
|
||||
" return 0;",
|
||||
" }",
|
||||
"",
|
||||
" private Byte toBoxed(Object o) {",
|
||||
" return 0;",
|
||||
" }",
|
||||
"",
|
||||
" private Function<Object, Byte> toBoxed() {",
|
||||
" return o -> 0;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
@@ -191,11 +202,25 @@ public final class PrimitiveComparisonCheckTest {
|
||||
" cmp().thenComparingDouble(o -> Integer.valueOf(0));",
|
||||
" }",
|
||||
"",
|
||||
" private Comparator<Object> cmp() { return null; }",
|
||||
" private int toPrimitive(Object o) { return 0; }",
|
||||
" private Integer toBoxed(Object o) { return 0; }",
|
||||
" private Function<Object, Integer> toBoxed() { return o -> 0; }",
|
||||
" private ToIntFunction<Object> toPrimitive() { return o -> 0; }",
|
||||
" private Comparator<Object> cmp() {",
|
||||
" return null;",
|
||||
" }",
|
||||
"",
|
||||
" private int toPrimitive(Object o) {",
|
||||
" return 0;",
|
||||
" }",
|
||||
"",
|
||||
" private Integer toBoxed(Object o) {",
|
||||
" return 0;",
|
||||
" }",
|
||||
"",
|
||||
" private Function<Object, Integer> toBoxed() {",
|
||||
" return o -> 0;",
|
||||
" }",
|
||||
"",
|
||||
" private ToIntFunction<Object> toPrimitive() {",
|
||||
" return o -> 0;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
@@ -268,11 +293,25 @@ public final class PrimitiveComparisonCheckTest {
|
||||
" cmp().thenComparingDouble(o -> Long.valueOf(0));",
|
||||
" }",
|
||||
"",
|
||||
" private Comparator<Object> cmp() { return null; }",
|
||||
" private long toPrimitive(Object o) { return 0L; }",
|
||||
" private Long toBoxed(Object o) { return 0L; }",
|
||||
" private Function<Object, Long> toBoxed() { return o -> 0L; }",
|
||||
" private ToLongFunction<Object> toPrimitive() { return o -> 0L; }",
|
||||
" private Comparator<Object> cmp() {",
|
||||
" return null;",
|
||||
" }",
|
||||
"",
|
||||
" private long toPrimitive(Object o) {",
|
||||
" return 0L;",
|
||||
" }",
|
||||
"",
|
||||
" private Long toBoxed(Object o) {",
|
||||
" return 0L;",
|
||||
" }",
|
||||
"",
|
||||
" private Function<Object, Long> toBoxed() {",
|
||||
" return o -> 0L;",
|
||||
" }",
|
||||
"",
|
||||
" private ToLongFunction<Object> toPrimitive() {",
|
||||
" return o -> 0L;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
@@ -326,10 +365,21 @@ public final class PrimitiveComparisonCheckTest {
|
||||
" cmp().thenComparingDouble(o -> Float.valueOf(0));",
|
||||
" }",
|
||||
"",
|
||||
" private Comparator<Object> cmp() { return null; }",
|
||||
" private float toPrimitive(Object o) { return 0.0f; }",
|
||||
" private Float toBoxed(Object o) { return 0.0f; }",
|
||||
" private Function<Object, Float> toBoxed() { return o -> 0.0f; }",
|
||||
" private Comparator<Object> cmp() {",
|
||||
" return null;",
|
||||
" }",
|
||||
"",
|
||||
" private float toPrimitive(Object o) {",
|
||||
" return 0.0f;",
|
||||
" }",
|
||||
"",
|
||||
" private Float toBoxed(Object o) {",
|
||||
" return 0.0f;",
|
||||
" }",
|
||||
"",
|
||||
" private Function<Object, Float> toBoxed() {",
|
||||
" return o -> 0.0f;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
@@ -386,11 +436,25 @@ public final class PrimitiveComparisonCheckTest {
|
||||
" cmp().thenComparingDouble(o -> Double.valueOf(0));",
|
||||
" }",
|
||||
"",
|
||||
" private Comparator<Object> cmp() { return null; }",
|
||||
" private double toPrimitive(Object o) { return 0.0; }",
|
||||
" private Double toBoxed(Object o) { return 0.0; }",
|
||||
" private Function<Object, Double> toBoxed() { return o -> 0.0; }",
|
||||
" private ToDoubleFunction<Object> toPrimitive() { return o -> 0.0; }",
|
||||
" private Comparator<Object> cmp() {",
|
||||
" return null;",
|
||||
" }",
|
||||
"",
|
||||
" private double toPrimitive(Object o) {",
|
||||
" return 0.0;",
|
||||
" }",
|
||||
"",
|
||||
" private Double toBoxed(Object o) {",
|
||||
" return 0.0;",
|
||||
" }",
|
||||
"",
|
||||
" private Function<Object, Double> toBoxed() {",
|
||||
" return o -> 0.0;",
|
||||
" }",
|
||||
"",
|
||||
" private ToDoubleFunction<Object> toPrimitive() {",
|
||||
" return o -> 0.0;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
@@ -420,8 +484,13 @@ public final class PrimitiveComparisonCheckTest {
|
||||
" cmp().thenComparing(toStr(), cmp());",
|
||||
" }",
|
||||
"",
|
||||
" private Comparator<Object> cmp() { return null; }",
|
||||
" private Function<Object, String> toStr() { return String::valueOf; }",
|
||||
" private Comparator<Object> cmp() {",
|
||||
" return null;",
|
||||
" }",
|
||||
"",
|
||||
" private Function<Object, String> toStr() {",
|
||||
" return String::valueOf;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
@@ -692,22 +761,30 @@ public final class PrimitiveComparisonCheckTest {
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
" Comparator<A> bCmp = Comparator.<A, A>comparing(o -> o).thenComparingInt(o -> Byte.valueOf((byte) 0));",
|
||||
" Comparator<A> cCmp = Comparator.<A, A>comparing(o -> o).thenComparingInt(o -> Character.valueOf((char) 0));",
|
||||
" Comparator<A> sCmp = Comparator.<A, A>comparing(o -> o).thenComparingInt(o -> Short.valueOf((short) 0));",
|
||||
" Comparator<A> bCmp =",
|
||||
" Comparator.<A, A>comparing(o -> o).thenComparingInt(o -> Byte.valueOf((byte) 0));",
|
||||
" Comparator<A> cCmp =",
|
||||
" Comparator.<A, A>comparing(o -> o).thenComparingInt(o -> Character.valueOf((char) 0));",
|
||||
" Comparator<A> sCmp =",
|
||||
" Comparator.<A, A>comparing(o -> o).thenComparingInt(o -> Short.valueOf((short) 0));",
|
||||
" Comparator<A> iCmp = Comparator.<A, A>comparing(o -> o).thenComparingInt(o -> Integer.valueOf(0));",
|
||||
" Comparator<A> lCmp = Comparator.<A, A>comparing(o -> o).thenComparingLong(o -> Long.valueOf(0));",
|
||||
" Comparator<A> fCmp = Comparator.<A, A>comparing(o -> o).thenComparingDouble(o -> Float.valueOf(0));",
|
||||
" Comparator<A> dCmp = Comparator.<A, A>comparing(o -> o).thenComparingDouble(o -> Double.valueOf(0));",
|
||||
" Comparator<A> fCmp =",
|
||||
" Comparator.<A, A>comparing(o -> o).thenComparingDouble(o -> Float.valueOf(0));",
|
||||
" Comparator<A> dCmp =",
|
||||
" Comparator.<A, A>comparing(o -> o).thenComparingDouble(o -> Double.valueOf(0));",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
" Comparator<A> bCmp = Comparator.<A, A>comparing(o -> o).thenComparing(o -> Byte.valueOf((byte) 0));",
|
||||
" Comparator<A> cCmp = Comparator.<A, A>comparing(o -> o).thenComparing(o -> Character.valueOf((char) 0));",
|
||||
" Comparator<A> sCmp = Comparator.<A, A>comparing(o -> o).thenComparing(o -> Short.valueOf((short) 0));",
|
||||
" Comparator<A> bCmp =",
|
||||
" Comparator.<A, A>comparing(o -> o).thenComparing(o -> Byte.valueOf((byte) 0));",
|
||||
" Comparator<A> cCmp =",
|
||||
" Comparator.<A, A>comparing(o -> o).thenComparing(o -> Character.valueOf((char) 0));",
|
||||
" Comparator<A> sCmp =",
|
||||
" Comparator.<A, A>comparing(o -> o).thenComparing(o -> Short.valueOf((short) 0));",
|
||||
" Comparator<A> iCmp = Comparator.<A, A>comparing(o -> o).thenComparing(o -> Integer.valueOf(0));",
|
||||
" Comparator<A> lCmp = Comparator.<A, A>comparing(o -> o).thenComparing(o -> Long.valueOf(0));",
|
||||
" Comparator<A> fCmp = Comparator.<A, A>comparing(o -> o).thenComparing(o -> Float.valueOf(0));",
|
||||
@@ -715,4 +792,39 @@ public final class PrimitiveComparisonCheckTest {
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
// XXX: This still fails: all replacements start at the same place...
|
||||
@Test
|
||||
void testReplacementOfMultipleSubexpressions() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"in/A.java",
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
" Comparator<A> cmp = Comparator.<A, A>comparing(o -> o)",
|
||||
" .thenComparingInt(o -> Byte.valueOf((byte) 0))",
|
||||
" .thenComparingInt(o -> Character.valueOf((char) 0))",
|
||||
" .thenComparingInt(o -> Short.valueOf((short) 0))",
|
||||
" .thenComparingInt(o -> Integer.valueOf(0))",
|
||||
" .thenComparingLong(o -> Long.valueOf(0))",
|
||||
" .thenComparingDouble(o -> Float.valueOf(0))",
|
||||
" .thenComparingDouble(o -> Double.valueOf(0));",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
" Comparator<A> cmp = Comparator.<A, A>comparing(o -> o) ",
|
||||
" .thenComparing(o -> Byte.valueOf((byte) 0))",
|
||||
" .thenComparing(o -> Character.valueOf((char) 0))",
|
||||
" .thenComparing(o -> Short.valueOf((short) 0))",
|
||||
" .thenComparing(o -> Integer.valueOf(0))",
|
||||
" .thenComparing(o -> Long.valueOf(0))",
|
||||
" .thenComparing(o -> Float.valueOf(0))",
|
||||
" .thenComparing(o -> Double.valueOf(0));",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,16 @@ import org.junit.jupiter.api.Test;
|
||||
// XXX: The tests below show that `String.valueOf((String) null)` may be simplified, but
|
||||
// `String.valueOf(null)` may not. That is because the latter matches `String#valueOf(char[])`. We
|
||||
// could special-case `null` arguments, but that doesn't seem worth the trouble.
|
||||
public final class RedundantStringConversionCheckTest {
|
||||
final class RedundantStringConversionTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(RedundantStringConversionCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass());
|
||||
private final CompilationTestHelper customizedCompilationTestHelper =
|
||||
CompilationTestHelper.newInstance(RedundantStringConversionCheck.class, getClass())
|
||||
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
|
||||
.setArgs(
|
||||
ImmutableList.of(
|
||||
"-XepOpt:RedundantStringConversion:ExtraConversionMethods=java.lang.Enum#name(),A#name(),A.B#toString(int)"));
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(RedundantStringConversionCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(RedundantStringConversion.class, getClass());
|
||||
|
||||
@Test
|
||||
void identificationOfIdentityTransformation() {
|
||||
@@ -124,7 +124,7 @@ public final class RedundantStringConversionCheckTest {
|
||||
" s + String.valueOf((String) null),",
|
||||
" s + String.valueOf(null),",
|
||||
" s + String.valueOf(new char[0]),",
|
||||
"",
|
||||
" //",
|
||||
" 42 + this.toString(),",
|
||||
" 42 + super.toString(),",
|
||||
" 42 + i.toString(),",
|
||||
@@ -147,7 +147,7 @@ public final class RedundantStringConversionCheckTest {
|
||||
" String.valueOf((String) null) + s,",
|
||||
" String.valueOf(null) + s,",
|
||||
" String.valueOf(new char[0]) + s,",
|
||||
"",
|
||||
" //",
|
||||
" this.toString() + 42,",
|
||||
" super.toString() + 42,",
|
||||
" i.toString() + 42,",
|
||||
@@ -175,10 +175,7 @@ public final class RedundantStringConversionCheckTest {
|
||||
"",
|
||||
" int[] m2() {",
|
||||
" return new int[] {",
|
||||
" 1 + 1,",
|
||||
" 1 - 1,",
|
||||
" 1 * 1,",
|
||||
" 1 / 1,",
|
||||
" 1 + 1, 1 - 1, 1 * 1, 1 / 1,",
|
||||
" };",
|
||||
" }",
|
||||
"}")
|
||||
@@ -284,9 +281,9 @@ public final class RedundantStringConversionCheckTest {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static com.google.common.base.Preconditions.checkState;",
|
||||
"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;",
|
||||
"",
|
||||
@@ -5,11 +5,11 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class RefasterAnyOfUsageCheckTest {
|
||||
final class RefasterAnyOfUsageTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(RefasterAnyOfUsageCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(RefasterAnyOfUsage.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(RefasterAnyOfUsageCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(RefasterAnyOfUsage.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
@@ -1,191 +0,0 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
|
||||
import static java.util.function.Function.identity;
|
||||
import static java.util.function.Predicate.not;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
public final class RefasterCheckTest {
|
||||
/** The names of all Refaster template groups defined in this module. */
|
||||
private static final ImmutableSet<String> TEMPLATE_GROUPS =
|
||||
ImmutableSet.of(
|
||||
"AssertJ",
|
||||
"AssertJBigDecimal",
|
||||
"AssertJBigInteger",
|
||||
"AssertJBoolean",
|
||||
"AssertJByte",
|
||||
"AssertJCharSequence",
|
||||
"AssertJDouble",
|
||||
"AssertJEnumerable",
|
||||
"AssertJFloat",
|
||||
"AssertJInteger",
|
||||
"AssertJLong",
|
||||
"AssertJNumber",
|
||||
"AssertJMap",
|
||||
"AssertJObject",
|
||||
"AssertJOptional",
|
||||
"AssertJShort",
|
||||
"AssertJString",
|
||||
"AssertJThrowingCallable",
|
||||
"Assorted",
|
||||
"BigDecimal",
|
||||
"Collection",
|
||||
"Comparator",
|
||||
"DoubleStream",
|
||||
"Equality",
|
||||
"ImmutableList",
|
||||
"ImmutableListMultimap",
|
||||
"ImmutableMap",
|
||||
"ImmutableMultiset",
|
||||
"ImmutableSet",
|
||||
"ImmutableSetMultimap",
|
||||
"ImmutableSortedMap",
|
||||
"ImmutableSortedMultiset",
|
||||
"ImmutableSortedSet",
|
||||
"IntStream",
|
||||
"JUnit",
|
||||
"LongStream",
|
||||
"MapEntry",
|
||||
"Mockito",
|
||||
"Multimap",
|
||||
"Null",
|
||||
"Optional",
|
||||
"Primitive",
|
||||
"Reactor",
|
||||
"RxJava2Adapter",
|
||||
"Stream",
|
||||
"String",
|
||||
"TestNGToAssertJ",
|
||||
"Time",
|
||||
"WebClient");
|
||||
/**
|
||||
* Matches the parts of the fully-qualified name of a template class that should be removed in
|
||||
* order to produce the associated {@link #TEMPLATE_GROUPS template group name}.
|
||||
*/
|
||||
private static final Pattern TEMPLATE_FQCN_TRIM_FOR_GROUP_NAME =
|
||||
Pattern.compile(".*\\.|Templates\\$.*");
|
||||
/**
|
||||
* A mapping from template group names to associated template names.
|
||||
*
|
||||
* <p>In effect, the values correspond to nested classes that represent individual Refaster
|
||||
* templates, while the keys correspond to the associated top-level "aggregator" classes.
|
||||
*/
|
||||
private static final ImmutableSetMultimap<String, String> TEMPLATES_BY_GROUP =
|
||||
indexTemplateNamesByGroup(RefasterCheck.ALL_CODE_TRANSFORMERS.get().keySet());
|
||||
|
||||
/** Returns every known template group name as a parameterized test argument. */
|
||||
@SuppressWarnings("UnusedMethod" /* Used as a `@MethodSource`. */)
|
||||
private static Stream<Arguments> templateGroupsUnderTest() {
|
||||
// XXX: Drop the filter once we have added tests for AssertJ!
|
||||
return TEMPLATES_BY_GROUP.keySet().stream().filter(not("AssertJ"::equals)).map(Arguments::of);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns every known (template group name, template name) pair as a parameterized test argument.
|
||||
*/
|
||||
@SuppressWarnings("UnusedMethod" /* Used as a `@MethodSource`. */)
|
||||
private static Stream<Arguments> templatesUnderTest() {
|
||||
// XXX: Drop the filter once we have added tests for AssertJ!
|
||||
return TEMPLATES_BY_GROUP.entries().stream()
|
||||
.filter(e -> !"AssertJ".equals(e.getKey()))
|
||||
.map(e -> arguments(e.getKey(), e.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that {@link RefasterCheck#loadAllCodeTransformers} finds at least one code transformer
|
||||
* for all of the {@link #TEMPLATE_GROUPS}.
|
||||
*
|
||||
* <p>This test is just as much about ensuring that {@link #TEMPLATE_GROUPS} is exhaustive, so
|
||||
* that in turn {@link #replacement}'s coverage is exhaustive.
|
||||
*/
|
||||
@Test
|
||||
void loadAllCodeTransformers() {
|
||||
assertThat(TEMPLATES_BY_GROUP.keySet()).hasSameElementsAs(TEMPLATE_GROUPS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies for each of the {@link #TEMPLATE_GROUPS} that the associated code transformers have
|
||||
* the desired effect.
|
||||
*/
|
||||
@MethodSource("templateGroupsUnderTest")
|
||||
@ParameterizedTest
|
||||
void replacement(String group) {
|
||||
verifyRefactoring(group, namePattern(group));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that all loaded Refaster templates are covered by at least one test.
|
||||
*
|
||||
* <p>Note that this doesn't guarantee full coverage: this test cannot ascertain that all {@link
|
||||
* com.google.errorprone.refaster.Refaster#anyOf} branches are tested. Idem for {@link
|
||||
* com.google.errorprone.refaster.annotation.BeforeTemplate} methods in case there are multiple .
|
||||
*/
|
||||
@MethodSource("templatesUnderTest")
|
||||
@ParameterizedTest
|
||||
void coverage(String group, String template) {
|
||||
assertThatCode(() -> verifyRefactoring(group, namePattern(group, template)))
|
||||
.withFailMessage(
|
||||
"Template %s does not affect the tests for group %s; is it tested?", template, group)
|
||||
.isInstanceOf(AssertionError.class)
|
||||
.hasMessageFindingMatch("^(diff|expected):");
|
||||
}
|
||||
|
||||
private static ImmutableSetMultimap<String, String> indexTemplateNamesByGroup(
|
||||
ImmutableSet<String> templateNames) {
|
||||
return templateNames.stream()
|
||||
.collect(
|
||||
toImmutableSetMultimap(
|
||||
n -> TEMPLATE_FQCN_TRIM_FOR_GROUP_NAME.matcher(n).replaceAll(""), identity()));
|
||||
}
|
||||
|
||||
private static String namePattern(String groupName, String excludedTemplate) {
|
||||
return "(?!" + Pattern.quote(excludedTemplate) + ')' + namePattern(groupName);
|
||||
}
|
||||
|
||||
private static String namePattern(String groupName) {
|
||||
return Pattern.compile(Pattern.quote(groupName)) + "Templates.*";
|
||||
}
|
||||
|
||||
private void verifyRefactoring(String groupName, String templateNamePattern) {
|
||||
createRestrictedRefactoringTestHelper(templateNamePattern)
|
||||
.addInput(groupName + "TemplatesTestInput.java")
|
||||
.addOutput(groupName + "TemplatesTestOutput.java")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
private BugCheckerRefactoringTestHelper createRestrictedRefactoringTestHelper(
|
||||
String namePattern) {
|
||||
return BugCheckerRefactoringTestHelper.newInstance(
|
||||
RefasterCheck.class, getRefasterTemplateCollectionClass())
|
||||
.setArgs("-XepOpt:Refaster:NamePattern=" + namePattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an arbitrary Refaster template collection class inside the {@code
|
||||
* tech.picnic.errorprone.refastertemplates} package, enabling {@link
|
||||
* BugCheckerRefactoringTestHelper} to load test resources from said package.
|
||||
*/
|
||||
private Class<?> getRefasterTemplateCollectionClass() {
|
||||
try {
|
||||
return Class.forName(
|
||||
"tech.picnic.errorprone.refastertemplates.AssortedTemplates",
|
||||
/* initialize= */ false,
|
||||
getClass().getClassLoader());
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException("Failed to load Refaster template collection class", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class RequestMappingAnnotationCheckTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(RequestMappingAnnotationCheck.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import java.io.InputStream;",
|
||||
"import java.time.ZoneId;",
|
||||
"import java.util.Locale;",
|
||||
"import java.util.TimeZone;",
|
||||
"import javax.servlet.http.HttpServletRequest;",
|
||||
"import javax.servlet.http.HttpServletResponse;",
|
||||
"import org.springframework.http.HttpMethod;",
|
||||
"import org.springframework.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.RequestBody;",
|
||||
"import org.springframework.web.bind.annotation.RequestHeader;",
|
||||
"import org.springframework.web.bind.annotation.RequestMapping;",
|
||||
"import org.springframework.web.bind.annotation.RequestMethod;",
|
||||
"import org.springframework.web.bind.annotation.RequestParam;",
|
||||
"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 properRequestBody(@RequestBody String body);",
|
||||
" @PostMapping A properRequestHeader(@RequestHeader String header);",
|
||||
" @PutMapping A properRequestParam(@RequestParam String param);",
|
||||
" @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 properNativeWebRequest(NativeWebRequest request);",
|
||||
" @RequestMapping A properWebRequest(WebRequest request);",
|
||||
" @RequestMapping A properServerWebExchange(ServerWebExchange exchange);",
|
||||
" @RequestMapping A properServerUriBuilder(UriBuilder builder);",
|
||||
" @RequestMapping A properServerUriComponentsBuilder(UriComponentsBuilder builder);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @DeleteMapping A delete(String param);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @GetMapping A get(String param);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @PatchMapping A patch(String param);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @PostMapping A post(String param);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @PutMapping A put(String param);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @RequestMapping A requestMultiple(String param, String param2);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @RequestMapping A requestFirstParamViolation(String param, @PathVariable String param2);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @RequestMapping A requestSecondParamViolation(@RequestBody String param, String param2);",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class RequestMappingAnnotationTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(RequestMappingAnnotation.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import java.io.InputStream;",
|
||||
"import java.time.ZoneId;",
|
||||
"import java.util.Locale;",
|
||||
"import java.util.TimeZone;",
|
||||
"import javax.servlet.http.HttpServletRequest;",
|
||||
"import javax.servlet.http.HttpServletResponse;",
|
||||
"import org.springframework.http.HttpMethod;",
|
||||
"import org.springframework.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.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 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 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();
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,9 @@ package tech.picnic.errorprone.bugpatterns;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class RequestParamTypeCheckTest {
|
||||
final class RequestParamTypeTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(RequestParamTypeCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(RequestParamType.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
@@ -28,23 +28,36 @@ final class RequestParamTypeCheckTest {
|
||||
"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);",
|
||||
" @PostMapping",
|
||||
" A properRequestParam(@RequestBody String body);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @GetMapping A get(@RequestParam ImmutableBiMap<String, String> param);",
|
||||
" @GetMapping",
|
||||
" A properRequestParam(@RequestParam int param);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @PostMapping A post(@Nullable @RequestParam ImmutableList<String> param);",
|
||||
" @GetMapping",
|
||||
" A properRequestParam(@RequestParam List<String> param);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @PutMapping A put(@RequestBody String body, @RequestParam ImmutableSet<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:",
|
||||
" @DeleteMapping A delete(@RequestBody String body, @RequestParam ImmutableMap<String, String> param);",
|
||||
" 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);",
|
||||
"}")
|
||||
@@ -4,14 +4,12 @@ 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;
|
||||
|
||||
public final class ScheduledTransactionTraceCheckTest {
|
||||
final class ScheduledTransactionTraceTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(ScheduledTransactionTraceCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(ScheduledTransactionTrace.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(ScheduledTransactionTraceCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(ScheduledTransactionTrace.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
@@ -45,10 +43,7 @@ public final class ScheduledTransactionTraceCheckTest {
|
||||
.doTest();
|
||||
}
|
||||
|
||||
// XXX: Enable this test for all JREs once https://github.com/google/error-prone/pull/2820 is
|
||||
// merged and released.
|
||||
@Test
|
||||
@DisabledForJreRange(min = JRE.JAVA_12)
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
@@ -5,11 +5,11 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class Slf4jLogStatementCheckTest {
|
||||
final class Slf4JLogStatementTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(Slf4jLogStatementCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(Slf4jLogStatement.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(Slf4jLogStatementCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(Slf4jLogStatement.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
@@ -44,7 +44,8 @@ public final class Slf4jLogStatementCheckTest {
|
||||
" LOG.info(marker, s, o, t);",
|
||||
"",
|
||||
" LOG.warn(FMT0);",
|
||||
" // BUG: Diagnostic contains: Log statement contains 0 placeholders, but specifies 1 matching argument(s)",
|
||||
" // 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:",
|
||||
@@ -56,13 +57,15 @@ public final class Slf4jLogStatementCheckTest {
|
||||
" // BUG: Diagnostic contains:",
|
||||
" LOG.trace(marker, FMT0, o, t);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains: Log statement contains 1 placeholders, but specifies 0 matching argument(s)",
|
||||
" // 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)",
|
||||
" // 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);",
|
||||
@@ -5,11 +5,11 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class SpringMvcAnnotationCheckTest {
|
||||
final class SpringMvcAnnotationTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(SpringMvcAnnotationCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(SpringMvcAnnotation.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(SpringMvcAnnotationCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(SpringMvcAnnotation.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
@@ -19,9 +19,9 @@ public final class SpringMvcAnnotationCheckTest {
|
||||
"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 static org.springframework.web.bind.annotation.RequestMethod.PATCH;",
|
||||
"",
|
||||
"import org.springframework.web.bind.annotation.DeleteMapping;",
|
||||
"import org.springframework.web.bind.annotation.GetMapping;",
|
||||
@@ -32,28 +32,53 @@ public final class SpringMvcAnnotationCheckTest {
|
||||
"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();",
|
||||
" @RequestMapping",
|
||||
" A simple();",
|
||||
"",
|
||||
" @DeleteMapping A properDelete();",
|
||||
" @GetMapping A properGet();",
|
||||
" @PatchMapping A properPatch();",
|
||||
" @PostMapping A properPost();",
|
||||
" @PutMapping A properPut();",
|
||||
" @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();
|
||||
}
|
||||
@@ -71,11 +96,25 @@ public final class SpringMvcAnnotationCheckTest {
|
||||
"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();",
|
||||
" @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(
|
||||
"out/A.java",
|
||||
@@ -92,11 +131,22 @@ public final class SpringMvcAnnotationCheckTest {
|
||||
"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();",
|
||||
" @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);
|
||||
}
|
||||
@@ -7,28 +7,28 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class StaticImportCheckTest {
|
||||
final class StaticImportTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(StaticImportCheck.class, getClass());
|
||||
CompilationTestHelper.newInstance(StaticImport.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(StaticImportCheck.class, getClass());
|
||||
BugCheckerRefactoringTestHelper.newInstance(StaticImport.class, getClass());
|
||||
|
||||
@Test
|
||||
void candidateMethodsAreNotRedundant() {
|
||||
assertThat(StaticImportCheck.STATIC_IMPORT_CANDIDATE_MEMBERS.keySet())
|
||||
.doesNotContainAnyElementsOf(StaticImportCheck.STATIC_IMPORT_CANDIDATE_TYPES);
|
||||
assertThat(StaticImport.STATIC_IMPORT_CANDIDATE_MEMBERS.keySet())
|
||||
.doesNotContainAnyElementsOf(StaticImport.STATIC_IMPORT_CANDIDATE_TYPES);
|
||||
}
|
||||
|
||||
@Test
|
||||
void exemptedMembersAreNotVacuous() {
|
||||
assertThat(StaticImportCheck.STATIC_IMPORT_EXEMPTED_MEMBERS.keySet())
|
||||
.isSubsetOf(StaticImportCheck.STATIC_IMPORT_CANDIDATE_TYPES);
|
||||
assertThat(StaticImport.STATIC_IMPORT_EXEMPTED_MEMBERS.keySet())
|
||||
.isSubsetOf(StaticImport.STATIC_IMPORT_CANDIDATE_TYPES);
|
||||
}
|
||||
|
||||
@Test
|
||||
void exemptedMembersAreNotRedundant() {
|
||||
assertThat(StaticImportCheck.STATIC_IMPORT_EXEMPTED_MEMBERS.values())
|
||||
.doesNotContainAnyElementsOf(StaticImportCheck.STATIC_IMPORT_EXEMPTED_IDENTIFIERS);
|
||||
assertThat(StaticImport.STATIC_IMPORT_EXEMPTED_MEMBERS.values())
|
||||
.doesNotContainAnyElementsOf(StaticImport.STATIC_IMPORT_EXEMPTED_IDENTIFIERS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -51,9 +51,8 @@ public final class StaticImportCheckTest {
|
||||
"import java.nio.charset.StandardCharsets;",
|
||||
"import java.time.ZoneOffset;",
|
||||
"import java.util.Optional;",
|
||||
"import java.util.function.Predicate;",
|
||||
"import java.util.UUID;",
|
||||
"import org.springframework.boot.test.context.SpringBootTest;",
|
||||
"import java.util.function.Predicate;",
|
||||
"import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;",
|
||||
"import org.springframework.http.MediaType;",
|
||||
"",
|
||||
@@ -135,10 +134,10 @@ public final class StaticImportCheckTest {
|
||||
"import java.util.Objects;",
|
||||
"import java.util.regex.Pattern;",
|
||||
"import org.junit.jupiter.params.provider.Arguments;",
|
||||
"import org.springframework.format.annotation.DateTimeFormat;",
|
||||
"import org.springframework.format.annotation.DateTimeFormat.ISO;",
|
||||
"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 {",
|
||||
@@ -181,15 +180,14 @@ public final class StaticImportCheckTest {
|
||||
" @DateTimeFormat(iso = ISO.TIME) String time) {}",
|
||||
"",
|
||||
" @BugPattern(",
|
||||
" name = \"TestBugPattern\",",
|
||||
" summary = \"\",",
|
||||
" linkType = BugPattern.LinkType.NONE,",
|
||||
" severity = SeverityLevel.SUGGESTION,",
|
||||
" tags = BugPattern.StandardTags.SIMPLIFICATION)",
|
||||
" summary = \"\",",
|
||||
" linkType = BugPattern.LinkType.NONE,",
|
||||
" severity = SeverityLevel.SUGGESTION,",
|
||||
" tags = BugPattern.StandardTags.SIMPLIFICATION)",
|
||||
" static final class TestBugPattern {}",
|
||||
"",
|
||||
" @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)",
|
||||
" final class Test {}",
|
||||
" @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)",
|
||||
" final class Test {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"out/A.java",
|
||||
@@ -250,10 +248,7 @@ public final class StaticImportCheckTest {
|
||||
" Object o = UTF_8;",
|
||||
"",
|
||||
" ImmutableSet.of(",
|
||||
" MediaType.ALL,",
|
||||
" APPLICATION_XHTML_XML,",
|
||||
" TEXT_HTML,",
|
||||
" MediaType.valueOf(\"image/webp\"));",
|
||||
" MediaType.ALL, APPLICATION_XHTML_XML, TEXT_HTML, MediaType.valueOf(\"image/webp\"));",
|
||||
"",
|
||||
" Pattern.compile(\"\", CASE_INSENSITIVE);",
|
||||
" }",
|
||||
@@ -268,16 +263,11 @@ public final class StaticImportCheckTest {
|
||||
" @DateTimeFormat(iso = DATE_TIME) String dateTime,",
|
||||
" @DateTimeFormat(iso = TIME) String time) {}",
|
||||
"",
|
||||
" @BugPattern(",
|
||||
" name = \"TestBugPattern\",",
|
||||
" summary = \"\",",
|
||||
" linkType = NONE,",
|
||||
" severity = SUGGESTION,",
|
||||
" tags = SIMPLIFICATION)",
|
||||
" @BugPattern(summary = \"\", linkType = NONE, severity = SUGGESTION, tags = SIMPLIFICATION)",
|
||||
" static final class TestBugPattern {}",
|
||||
"",
|
||||
" @SpringBootTest(webEnvironment = RANDOM_PORT)",
|
||||
" final class Test {}",
|
||||
" @SpringBootTest(webEnvironment = RANDOM_PORT)",
|
||||
" final class Test {}",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
@@ -5,9 +5,9 @@ import static com.google.common.base.Predicates.containsPattern;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class TimeZoneUsageCheckTest {
|
||||
final class TimeZoneUsageTest {
|
||||
private final CompilationTestHelper compilationHelper =
|
||||
CompilationTestHelper.newInstance(TimeZoneUsageCheck.class, getClass())
|
||||
CompilationTestHelper.newInstance(TimeZoneUsage.class, getClass())
|
||||
.expectErrorMessage(
|
||||
"X",
|
||||
containsPattern(
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.bugpatterns.util;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class AnnotationAttributeMatcherTest {
|
||||
final class AnnotationAttributeMatcherTest {
|
||||
@Test
|
||||
void withoutListings() {
|
||||
AnnotationAttributeMatcher matcher =
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
package tech.picnic.errorprone.bugpatterns.util;
|
||||
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
@@ -15,12 +15,9 @@ import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public final class MethodMatcherFactoryTest {
|
||||
final class MethodMatcherFactoryTest {
|
||||
/** A {@link BugChecker} which flags method invocations matched by {@link #TEST_MATCHER}. */
|
||||
@BugPattern(
|
||||
name = "MatchedMethodsFlagger",
|
||||
severity = SUGGESTION,
|
||||
summary = "Flags methods matched by the test matcher.")
|
||||
@BugPattern(severity = SUGGESTION, summary = "Flags methods matched by the test matcher.")
|
||||
public static final class MatchedMethodsFlagger extends BugChecker
|
||||
implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -68,13 +65,21 @@ public final class MethodMatcherFactoryTest {
|
||||
"",
|
||||
"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(
|
||||
@@ -83,13 +88,21 @@ public final class MethodMatcherFactoryTest {
|
||||
"",
|
||||
"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(
|
||||
@@ -98,13 +111,21 @@ public final class MethodMatcherFactoryTest {
|
||||
"",
|
||||
"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(
|
||||
@@ -113,13 +134,21 @@ public final class MethodMatcherFactoryTest {
|
||||
"",
|
||||
"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(
|
||||
@@ -0,0 +1,81 @@
|
||||
package tech.picnic.errorprone.refastertemplates;
|
||||
|
||||
import static java.util.function.Predicate.not;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
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 tech.picnic.errorprone.refaster.test.RefasterTemplateCollection;
|
||||
|
||||
final class RefasterTemplatesTest {
|
||||
/** The names of all Refaster template groups defined in this module. */
|
||||
private static final ImmutableSet<Class<?>> TEMPLATE_COLLECTIONS =
|
||||
ImmutableSet.of(
|
||||
AssertJTemplates.class,
|
||||
AssertJBigDecimalTemplates.class,
|
||||
AssertJBigIntegerTemplates.class,
|
||||
AssertJBooleanTemplates.class,
|
||||
AssertJByteTemplates.class,
|
||||
AssertJCharSequenceTemplates.class,
|
||||
AssertJDoubleTemplates.class,
|
||||
AssertJEnumerableTemplates.class,
|
||||
AssertJFloatTemplates.class,
|
||||
AssertJIntegerTemplates.class,
|
||||
AssertJLongTemplates.class,
|
||||
AssertJNumberTemplates.class,
|
||||
AssertJMapTemplates.class,
|
||||
AssertJObjectTemplates.class,
|
||||
AssertJOptionalTemplates.class,
|
||||
AssertJShortTemplates.class,
|
||||
AssertJStringTemplates.class,
|
||||
AssertJThrowingCallableTemplates.class,
|
||||
AssortedTemplates.class,
|
||||
BigDecimalTemplates.class,
|
||||
CollectionTemplates.class,
|
||||
ComparatorTemplates.class,
|
||||
DoubleStreamTemplates.class,
|
||||
EqualityTemplates.class,
|
||||
ImmutableListTemplates.class,
|
||||
ImmutableListMultimapTemplates.class,
|
||||
ImmutableMapTemplates.class,
|
||||
ImmutableMultisetTemplates.class,
|
||||
ImmutableSetTemplates.class,
|
||||
ImmutableSetMultimapTemplates.class,
|
||||
ImmutableSortedMapTemplates.class,
|
||||
ImmutableSortedMultisetTemplates.class,
|
||||
ImmutableSortedSetTemplates.class,
|
||||
IntStreamTemplates.class,
|
||||
JUnitTemplates.class,
|
||||
LongStreamTemplates.class,
|
||||
MapEntryTemplates.class,
|
||||
MockitoTemplates.class,
|
||||
MultimapTemplates.class,
|
||||
NullTemplates.class,
|
||||
OptionalTemplates.class,
|
||||
PrimitiveTemplates.class,
|
||||
ReactorTemplates.class,
|
||||
RxJava2AdapterTemplates.class,
|
||||
StreamTemplates.class,
|
||||
StringTemplates.class,
|
||||
TestNGToAssertJTemplates.class,
|
||||
TimeTemplates.class,
|
||||
WebClientTemplates.class);
|
||||
|
||||
// XXX: Create a JUnit extension to automatically discover the template collections in a given
|
||||
// context to make sure the list is exhaustive.
|
||||
private static Stream<Arguments> validateTemplateCollectionTestCases() {
|
||||
// XXX: Drop the filter once we have added tests for AssertJ! We can then also replace this
|
||||
// method with `@ValueSource(classes = {...})`.
|
||||
return TEMPLATE_COLLECTIONS.stream()
|
||||
.filter(not(AssertJTemplates.class::equals))
|
||||
.map(Arguments::arguments);
|
||||
}
|
||||
|
||||
@MethodSource("validateTemplateCollectionTestCases")
|
||||
@ParameterizedTest
|
||||
void validateTemplateCollection(Class<?> clazz) {
|
||||
RefasterTemplateCollection.validate(clazz);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import static org.assertj.core.data.Percentage.withPercentage;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.math.BigDecimal;
|
||||
import org.assertj.core.api.AbstractBigDecimalAssert;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterTemplateTestCase;
|
||||
|
||||
final class AssertJBigDecimalTemplatesTest implements RefasterTemplateTestCase {
|
||||
@Override
|
||||
|
||||
@@ -7,6 +7,7 @@ import static org.assertj.core.data.Percentage.withPercentage;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.math.BigDecimal;
|
||||
import org.assertj.core.api.AbstractBigDecimalAssert;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterTemplateTestCase;
|
||||
|
||||
final class AssertJBigDecimalTemplatesTest implements RefasterTemplateTestCase {
|
||||
@Override
|
||||
|
||||
@@ -7,6 +7,7 @@ import static org.assertj.core.data.Percentage.withPercentage;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.math.BigInteger;
|
||||
import org.assertj.core.api.AbstractBigIntegerAssert;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterTemplateTestCase;
|
||||
|
||||
final class AssertJBigIntegerTemplatesTest implements RefasterTemplateTestCase {
|
||||
@Override
|
||||
|
||||
@@ -7,6 +7,7 @@ import static org.assertj.core.data.Percentage.withPercentage;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.math.BigInteger;
|
||||
import org.assertj.core.api.AbstractBigIntegerAssert;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterTemplateTestCase;
|
||||
|
||||
final class AssertJBigIntegerTemplatesTest implements RefasterTemplateTestCase {
|
||||
@Override
|
||||
|
||||
@@ -4,14 +4,15 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.assertj.core.api.AbstractBooleanAssert;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterTemplateTestCase;
|
||||
|
||||
final class AssertJBooleanTemplatesTest implements RefasterTemplateTestCase {
|
||||
AbstractBooleanAssert<?> testAbstractBooleanAssertIsEqualTo() {
|
||||
return assertThat(true).isNotEqualTo(!false);
|
||||
return assertThat(true).isNotEqualTo(!Boolean.FALSE);
|
||||
}
|
||||
|
||||
AbstractBooleanAssert<?> testAbstractBooleanAssertIsNotEqualTo() {
|
||||
return assertThat(true).isEqualTo(!false);
|
||||
return assertThat(true).isEqualTo(!Boolean.FALSE);
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractBooleanAssert<?>> testAbstractBooleanAssertIsTrue() {
|
||||
@@ -23,7 +24,7 @@ final class AssertJBooleanTemplatesTest implements RefasterTemplateTestCase {
|
||||
}
|
||||
|
||||
AbstractBooleanAssert<?> testAssertThatBooleanIsTrue() {
|
||||
return assertThat(!true).isFalse();
|
||||
return assertThat(!Boolean.TRUE).isFalse();
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractBooleanAssert<?>> testAbstractBooleanAssertIsFalse() {
|
||||
@@ -35,6 +36,6 @@ final class AssertJBooleanTemplatesTest implements RefasterTemplateTestCase {
|
||||
}
|
||||
|
||||
AbstractBooleanAssert<?> testAssertThatBooleanIsFalse() {
|
||||
return assertThat(!true).isTrue();
|
||||
return assertThat(!Boolean.TRUE).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,15 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.assertj.core.api.AbstractBooleanAssert;
|
||||
import tech.picnic.errorprone.refaster.test.RefasterTemplateTestCase;
|
||||
|
||||
final class AssertJBooleanTemplatesTest implements RefasterTemplateTestCase {
|
||||
AbstractBooleanAssert<?> testAbstractBooleanAssertIsEqualTo() {
|
||||
return assertThat(true).isEqualTo(false);
|
||||
return assertThat(true).isEqualTo(Boolean.FALSE);
|
||||
}
|
||||
|
||||
AbstractBooleanAssert<?> testAbstractBooleanAssertIsNotEqualTo() {
|
||||
return assertThat(true).isNotEqualTo(false);
|
||||
return assertThat(true).isNotEqualTo(Boolean.FALSE);
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractBooleanAssert<?>> testAbstractBooleanAssertIsTrue() {
|
||||
@@ -23,7 +24,7 @@ final class AssertJBooleanTemplatesTest implements RefasterTemplateTestCase {
|
||||
}
|
||||
|
||||
AbstractBooleanAssert<?> testAssertThatBooleanIsTrue() {
|
||||
return assertThat(true).isTrue();
|
||||
return assertThat(Boolean.TRUE).isTrue();
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractBooleanAssert<?>> testAbstractBooleanAssertIsFalse() {
|
||||
@@ -35,6 +36,6 @@ final class AssertJBooleanTemplatesTest implements RefasterTemplateTestCase {
|
||||
}
|
||||
|
||||
AbstractBooleanAssert<?> testAssertThatBooleanIsFalse() {
|
||||
return assertThat(true).isFalse();
|
||||
return assertThat(Boolean.TRUE).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user