Compare commits

..

78 Commits

Author SHA1 Message Date
Rick Ossendrijver
191fc4f4b8 Debugging this state... 2022-02-04 17:17:24 +01:00
Rick Ossendrijver
27be77f30f Improve some optional things and try to prepare for Stephan 2022-02-02 14:06:09 +01:00
Rick Ossendrijver
32e6086df8 Some tries with nullable thingies 2022-02-02 13:39:19 +01:00
Rick Ossendrijver
942fabc590 Add templates to at least match the nullable but requires work 2022-02-02 13:39:19 +01:00
Rick Ossendrijver
61e1435f18 Add IsNonNull matcher first part 2022-02-02 13:39:19 +01:00
Rick Ossendrijver
793a70c29b Configure static import policy for AssertThatSetContainsExactlyOneElement Refaster template (#32) 2022-01-24 10:02:34 +01:00
Rick Ossendrijver
63b4fae185 Introduce FluxFlatMapUsageCheck (#26)
And two (semi-)related Refaster templates.
2022-01-15 12:39:34 +01:00
Stephan Schroevers
73f8f056b4 Introduce ScheduledTransactionTraceCheck (#31) 2022-01-10 10:21:20 +01:00
Rick Ossendrijver
c1638066cd Define assorted StaticImportCheck exemptions
While there, require that most `org.springframework.http.MediaType` members are
statically imported.
2022-01-03 10:20:46 +01:00
Rick Ossendrijver
08e99fb54e Use matching lambda parameter names in Refaster before- and after-templates (#24) 2022-01-01 14:04:37 +01:00
Rick Ossendrijver
00012c6aa8 Extend StaticImportCheck test coverage (#21) 2021-11-14 14:39:15 +01:00
Rick Ossendrijver
193589d193 Require static import of SpringBootTest.WebEnvironment constants (#19) 2021-11-14 14:26:17 +01:00
Stephan Schroevers
1a1588d413 Upgrade Error Prone 2.9.0 -> 2.10.0 2021-11-14 13:50:26 +01:00
Stephan Schroevers
6656bd8186 Assorted upgrades 2021-11-14 13:50:26 +01:00
Rick Ossendrijver
26a9b46de7 Add Refaster rules for StepVerifier creation (#18) 2021-11-14 13:48:39 +01:00
Phil Werli
17e74f778b Add Refaster rules for redundant Flux#concat invocations (#20) 2021-11-13 18:04:49 +01:00
Stephan Schroevers
e1bdb098de Upgrade AssertJ Core 3.20.2 -> 3.21.0
See:
- https://assertj.github.io/doc/#assertj-core-release-notes
- https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.20.2...assertj-core-3.21.0
2021-09-25 13:53:58 +02:00
Stephan Schroevers
0be162c837 Assorted upgrades 2021-09-25 13:53:31 +02:00
Stephan Schroevers
edfb2a70e8 Upgrade Guava 30.1.1-jre -> 31.0-jre
And drop Refaster rules for APIs that are now `@Deprecated` or have an
`@InlineMe` annotation.

See:
- https://guava.dev/releases/31.0-jre/api/diffs/
- https://github.com/google/guava/releases/tag/v31.0
- https://github.com/google/guava/compare/v30.1.1...v31.0
2021-09-25 13:17:10 +02:00
Stephan Schroevers
c1e5dc4339 Assorted upgrades 2021-09-19 21:07:40 +02:00
Stephan Schroevers
7218006b3c Assorted upgrades 2021-09-03 07:42:57 +02:00
Rick Ossendrijver
a3f599ba1b Introduce WebClientTemplates (#16) 2021-08-30 21:54:28 +02:00
Stephan Schroevers
f819a3e42f Assorted upgrades 2021-08-20 08:50:15 +02:00
Stephan Schroevers
7fc865c769 Light RefasterRuleResourceCompilerTaskListener cleanup 2021-08-19 22:43:10 +02:00
Stephan Schroevers
0287060d96 Fix and optimize FormatStringConcatenationCheck 2021-08-19 22:42:41 +02:00
Stephan Schroevers
ac4e81f2c7 Support running the build using JDK 16+ 2021-08-14 19:29:55 +02:00
Stephan Schroevers
350f028781 Sync with PSM parent 2021-08-14 19:13:31 +02:00
Stephan Schroevers
299ce7b800 Assorted upgrades 2021-08-14 19:13:31 +02:00
Stephan Schroevers
f3a929a3bb Upgrade fmt-maven-plugin, reformat 2021-08-04 10:59:23 +02:00
Stephan Schroevers
625a55add5 Add extra Refaster rule, document possible FormatStringConcatenation extension 2021-08-04 10:58:16 +02:00
Stephan Schroevers
ef59340987 Assorted upgrades 2021-08-04 10:58:05 +02:00
Stephan Schroevers
f2aea38b17 Introduce AbstractOptionalAssertContainsSame Refaster template 2021-08-03 13:53:44 +02:00
Stephan Schroevers
5cd8863eb5 Slightly optimize MethodReferenceUsage check 2021-08-01 19:10:46 +02:00
Stephan Schroevers
5b57b4720b Introduce MockitoStubbing check 2021-08-01 18:29:54 +02:00
Stephan Schroevers
5224571407 Assorted FormatStringConcatenationCheck improvements 2021-08-01 15:56:32 +02:00
Stephan Schroevers
c8189e9431 Document RefasterCheck follow-up task 2021-08-01 12:41:00 +02:00
Stephan Schroevers
0fe42f1ea6 Introduce FormatStringConcatenation check 2021-08-01 12:32:08 +02:00
Stephan Schroevers
78b29107a3 Upgrade dependencies 2021-07-29 12:09:19 +02:00
Stephan Schroevers
7ae7d5105a Document StreamMapFirst gotcha, courtesy of Nathan 2021-07-29 11:51:11 +02:00
Stephan Schroevers
0976b33d61 Support Refaster templates without an @AfterTemplate method 2021-07-29 11:48:51 +02:00
Stephan Schroevers
4b6f81c4fa imeZoneUsageCheck: dDon't flag likely overrides 2021-07-27 17:44:04 +02:00
Stephan Schroevers
1e879c175e Don't flag empty test (helper) methods 2021-07-27 17:43:08 +02:00
Stephan Schroevers
d014fed400 Deduplicate Refaster templates 2021-07-24 23:20:26 +02:00
Stephan Schroevers
1357f60fbc Introduce refaster-support module
This new module is meant to expose helper logic for use with Refaster
templates. For a start, one can now use e.g.
`@NotMatches(IsArray.class)`.
2021-07-24 23:20:25 +02:00
Stephan Schroevers
03abbbf99c Introduce RefasterAnyOfUsageCheck 2021-07-24 17:08:08 +02:00
Stephan Schroevers
9b845782c2 Cleanup and add package-info.java files 2021-07-24 16:55:46 +02:00
Stephan Schroevers
08ce33fb19 Upgrade, fix typo 2021-07-24 12:18:00 +02:00
Stephan Schroevers
182724bc8e Upgrade dependencies 2021-07-23 08:00:18 +02:00
Stephan Schroevers
e9d361713a Upgrade Error Prone, deduplicate version configuration 2021-07-23 07:55:47 +02:00
Stephan Schroevers
b754880556 Assorted upgrades 2021-07-18 14:59:30 +02:00
Stephan Schroevers
affd5c7b93 Support self-application of the checks 2021-07-18 14:59:30 +02:00
Stephan Schroevers
d86611e66a TimeZoneUsageCheck should not flag Instant.now(clock)
Users _should_ rewrite that to `clock.instant()`, and indeed we have a
Refaster check for that.
2021-07-18 11:04:36 +02:00
Stephan Schroevers
2d6100a679 Further nudge to AssertJ 2021-07-18 10:46:47 +02:00
Stephan Schroevers
912797a55a Bintray is no more; Palantir now deploys to Maven Central 2021-07-18 10:46:47 +02:00
Stephan Schroevers
d097f31124 Assorted upgrades 2021-06-26 10:45:33 +02:00
Stephan Schroevers
eb05582f79 Assorted upgrades 2021-06-19 16:33:24 +02:00
Stephan Schroevers
964fc35b71 Upgrades 2021-05-29 18:04:57 +02:00
Stephan Schroevers
ddc05bf88c Assorted upgrades 2021-05-15 13:07:54 +02:00
Stephan Schroevers
24afa6a755 Some improvements from oss-parent 2021-04-24 00:00:10 +02:00
Stephan Schroevers
8e97121bdf Upgrade dependency 2021-04-23 22:37:19 +02:00
Stephan Schroevers
567c81a93d Upgrade to Error Prone 2.6.0 2021-04-23 22:20:11 +02:00
Stephan Schroevers
c0bfac7b4c Upgrades, some syncing with other parent 2021-04-22 15:10:58 +02:00
Rick Ossendrijver
28138f35eb Assorted upgrades (#15) 2021-04-19 09:51:25 +02:00
Stephan Schroevers
a9b691b856 Assorted upgrades 2021-04-06 17:35:38 +02:00
Ivan Fedorov
acfe87fbc4 Flag Ordering#explicit invocations listing a subset of an enum's values (#14) 2021-03-30 08:19:08 +02:00
Stephan Schroevers
091a6eee7a Fix the fork configuration 2021-03-30 08:04:01 +02:00
Stephan Schroevers
3c06e3ead3 Some consistency changes 2021-03-28 17:28:00 +02:00
Stephan Schroevers
3ecab2f4b9 Reduce memory assigned to Surefire
We may need to reintroduce this change in the future, but for now we can
keep the configuration in sync with other parent POMs.
2021-03-28 15:05:09 +02:00
Stephan Schroevers
4b3e79667d Address deprecations in the upcoming release 2021-03-28 15:05:09 +02:00
Stephan Schroevers
6505535525 Upgrades 2021-03-27 15:28:20 +01:00
Stephan Schroevers
e48bbf3a44 Upgrade to Error Prone 2.5.1 2021-03-27 15:03:50 +01:00
Stephan Schroevers
3391468746 Assorted upgrades 2021-03-20 19:03:37 +01:00
Stephan Schroevers
10172c426d Assorted upgrades 2021-03-14 10:15:00 +01:00
Stephan Schroevers
f2737b4fe9 Fix MissingRefasterAnnotationCheck 2021-03-11 16:37:22 +01:00
Anna Dvorkin
f9a1c82d68 PRP-10968 Flag discouraged time zone-dependent APIs (#9) 2021-03-01 23:08:19 +01:00
Halil İbrahim Şener
cbc886d0c2 PSM-442 Flag likely-wrong @JsonCreator usages (#2) 2021-03-01 18:50:58 +01:00
Rick Ossendrijver
e4e3aded84 Flag methods that appear to lack a Refaster annotation (#13) 2021-02-28 16:29:51 +01:00
Rick Ossendrijver
e84d0e1059 Flag unordered annotation declarations (#11) 2021-02-28 14:42:44 +01:00
120 changed files with 3630 additions and 762 deletions

10
.mvn/jvm.config Normal file
View File

@@ -0,0 +1,10 @@
--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
--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED

View File

@@ -11,10 +11,11 @@ install:
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 Error Prone checks
# available from other artifact repositories.
# 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 -s settings.xml
- ./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

View File

@@ -47,11 +47,20 @@
`annotationProcessorPaths` configuration below. -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-support</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.auto</groupId>
<artifactId>auto-common</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service-annotations</artifactId>
@@ -72,6 +81,11 @@
<artifactId>guava</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.newrelic.agent.java</groupId>
<artifactId>newrelic-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
@@ -167,10 +181,25 @@
<artifactId>spring-context</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>test</scope>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
@@ -201,12 +230,28 @@
<artifactId>refaster-resource-compiler</artifactId>
<version>${project.version}</version>
</path>
<path>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-support</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs combine.children="append">
<arg>-Xplugin:RefasterRuleResourceCompiler</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<ignoredUnusedDeclaredDependencies>
<!-- XXX: Figure out why the plugin thinks this
dependency is unused. -->
<ignoredUnusedDeclaredDependency>${project.groupId}:refaster-support</ignoredUnusedDeclaredDependency>
</ignoredUnusedDeclaredDependencies>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>

View File

@@ -0,0 +1,68 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.matchers.Matchers.isType;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.LinkType;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.BugPattern.StandardTags;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.AnnotationTreeMatcher;
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.AnnotationTree;
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;
import java.util.Map;
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 = LinkType.NONE,
severity = SeverityLevel.WARNING,
tags = StandardTags.LIKELY_ERROR)
public final class AmbiguousJsonCreatorCheck 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");
@Override
public Description matchAnnotation(AnnotationTree tree, VisitorState state) {
if (!IS_JSON_CREATOR_ANNOTATION.matches(tree, state)) {
return Description.NO_MATCH;
}
ClassTree clazz = state.findEnclosing(ClassTree.class);
if (clazz == null || clazz.getKind() != Tree.Kind.ENUM) {
return Description.NO_MATCH;
}
MethodTree method = state.findEnclosing(MethodTree.class);
if (method == null || method.getParameters().size() != 1) {
return Description.NO_MATCH;
}
boolean customMode =
ASTHelpers.getAnnotationMirror(tree).getElementValues().entrySet().stream()
.filter(entry -> entry.getKey().getSimpleName().contentEquals("mode"))
.map(Map.Entry::getValue)
.map(AnnotationValue::getValue)
.filter(Symbol.VarSymbol.class::isInstance)
.map(Symbol.VarSymbol.class::cast)
.anyMatch(varSymbol -> !varSymbol.getSimpleName().contentEquals("DEFAULT"));
return customMode
? Description.NO_MATCH
: describeMatch(
tree, SuggestedFix.replace(tree, "@JsonCreator(mode = JsonCreator.Mode.DELEGATING)"));
}
}

View File

@@ -17,9 +17,11 @@ 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.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)
@@ -31,7 +33,7 @@ import com.sun.tools.javac.code.Symbol.MethodSymbol;
tags = StandardTags.SIMPLIFICATION)
public final class EmptyMethodCheck extends BugChecker implements MethodTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<Tree> HAS_PERMITTED_ANNOTATION =
private static final Matcher<Tree> PERMITTED_ANNOTATION =
annotations(
AT_LEAST_ONE,
anyOf(isType("java.lang.Override"), isType("org.aspectj.lang.annotation.Pointcut")));
@@ -41,7 +43,8 @@ public final class EmptyMethodCheck extends BugChecker implements MethodTreeMatc
if (tree.getBody() == null
|| !tree.getBody().getStatements().isEmpty()
|| ASTHelpers.containsComments(tree, state)
|| HAS_PERMITTED_ANNOTATION.matches(tree, state)) {
|| PERMITTED_ANNOTATION.matches(tree, state)
|| isInPossibleTestHelperClass(state)) {
return Description.NO_MATCH;
}
@@ -52,4 +55,11 @@ public final class EmptyMethodCheck extends BugChecker implements MethodTreeMatc
return describeMatch(tree, SuggestedFix.delete(tree));
}
private static boolean isInPossibleTestHelperClass(VisitorState state) {
return Optional.ofNullable(ASTHelpers.findEnclosingNode(state.getPath(), ClassTree.class))
.map(ClassTree::getSimpleName)
.filter(name -> name.toString().contains("Test"))
.isPresent();
}
}

View File

@@ -0,0 +1,91 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
import static java.util.stream.Collectors.collectingAndThen;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.LinkType;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.BugPattern.StandardTags;
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.matchers.Matcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Type;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
/**
* A {@link BugChecker} which flags {@link Ordering#explicit(Object, Object[])}} invocations listing
* a subset of an enum type's values.
*/
@AutoService(BugChecker.class)
@BugPattern(
name = "ExplicitEnumOrdering",
summary = "Make sure `Ordering#explicit` lists all of an enum's values",
linkType = LinkType.NONE,
severity = SeverityLevel.WARNING,
tags = StandardTags.FRAGILE_CODE)
public final class ExplicitEnumOrderingCheck extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> EXPLICIT_ORDERING =
staticMethod().onClass(Ordering.class.getName()).named("explicit");
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (!EXPLICIT_ORDERING.matches(tree, state)) {
return Description.NO_MATCH;
}
ImmutableSet<String> missingEnumValues = getMissingEnumValues(tree.getArguments());
if (missingEnumValues.isEmpty()) {
return Description.NO_MATCH;
}
return buildDescription(tree)
.setMessage(
String.format(
"Explicit ordering lacks some enum values: %s",
String.join(", ", missingEnumValues)))
.build();
}
private static ImmutableSet<String> getMissingEnumValues(
List<? extends ExpressionTree> expressions) {
return expressions.stream()
.map(ASTHelpers::getSymbol)
.filter(Symbol::isEnum)
.collect(
collectingAndThen(
toImmutableSetMultimap(Symbol::asType, Symbol::toString),
ExplicitEnumOrderingCheck::getMissingEnumValues));
}
private static ImmutableSet<String> getMissingEnumValues(
ImmutableSetMultimap<Type, String> valuesByType) {
return Multimaps.asMap(valuesByType).entrySet().stream()
.flatMap(e -> getMissingEnumValues(e.getKey(), e.getValue()))
.collect(toImmutableSet());
}
private static Stream<String> getMissingEnumValues(Type enumType, Set<String> values) {
Symbol.TypeSymbol typeSymbol = enumType.asElement();
return Sets.difference(ASTHelpers.enumValues(typeSymbol), values).stream()
.map(v -> String.format("%s.%s", typeSymbol.getSimpleName(), v));
}
}

View File

@@ -0,0 +1,86 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
import com.google.auto.service.AutoService;
import com.google.common.collect.Iterables;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.LinkType;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.BugPattern.StandardTags;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MemberReferenceTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.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.MemberReferenceTree;
import com.sun.source.tree.MethodInvocationTree;
import java.util.function.Function;
import java.util.function.Supplier;
import reactor.core.publisher.Flux;
/**
* A {@link BugChecker} which flags usages of {@link Flux#flatMap(Function)} and {@link
* Flux#flatMapSequential(Function)}.
*
* <p>{@link Flux#flatMap(Function)} and {@link Flux#flatMapSequential(Function)} eagerly perform up
* to {@link reactor.util.concurrent.Queues#SMALL_BUFFER_SIZE} subscriptions. Additionally, the
* former interleaves values as they are emitted, yielding nondeterministic results. In most cases
* {@link Flux#concatMap(Function)} should be preferred, as it produces consistent results and
* avoids potentially saturating the thread pool on which subscription happens. If {@code
* concatMap}'s single-subscription semantics are undesirable one should invoke a {@code flatMap} or
* {@code flatMapSequential} overload with an explicit concurrency level.
*
* <p>NB: The rarely-used overload {@link Flux#flatMap(Function, Function, Supplier)} is not flagged
* by this check because there is no clear alternative to point to.
*/
@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 = LinkType.NONE,
severity = SeverityLevel.ERROR,
tags = StandardTags.LIKELY_ERROR)
public final class FluxFlatMapUsageCheck extends BugChecker
implements MethodInvocationTreeMatcher, MemberReferenceTreeMatcher {
private static final long serialVersionUID = 1L;
private static final String MAX_CONCURRENCY_ARG_NAME = "MAX_CONCURRENCY";
private static final Matcher<ExpressionTree> FLUX_FLATMAP =
instanceMethod()
.onDescendantOf("reactor.core.publisher.Flux")
.namedAnyOf("flatMap", "flatMapSequential")
.withParameters(Function.class.getName());
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (!FLUX_FLATMAP.matches(tree, state)) {
return Description.NO_MATCH;
}
return buildDescription(tree)
.addFix(SuggestedFixes.renameMethodInvocation(tree, "concatMap", state))
.addFix(
SuggestedFix.builder()
.postfixWith(
Iterables.getOnlyElement(tree.getArguments()), ", " + MAX_CONCURRENCY_ARG_NAME)
.build())
.build();
}
@Override
public Description matchMemberReference(MemberReferenceTree tree, VisitorState state) {
if (!FLUX_FLATMAP.matches(tree, state)) {
return Description.NO_MATCH;
}
// Method references are expected to occur very infrequently; generating both variants of
// suggested fixes is not worth the trouble.
return describeMatch(tree);
}
}

View File

@@ -0,0 +1,253 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.anyMethod;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.instanceMethod;
import static com.google.errorprone.matchers.Matchers.not;
import static com.google.errorprone.matchers.Matchers.staticMethod;
import static java.util.stream.Collectors.joining;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.LinkType;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.BugPattern.StandardTags;
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.BinaryTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.util.SimpleTreeVisitor;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* A {@link BugChecker} which flags string concatenations that produce a format string; in such
* cases the string concatenation should instead be deferred to the invoked method.
*
* @implNote This checker is based on the implementation of {@link
* com.google.errorprone.bugpatterns.flogger.FloggerStringConcatenation}.
*/
// XXX: Support arbitrary `@FormatMethod`-annotated methods.
// XXX: For (explicit or delegated) invocations of `java.util.Formatter` _strictly speaking_ we
// should introduce special handling of `Formattable` arguments, as this check would replace a
// `Formattable#toString` invocation with a `Formattable#formatTo` invocation. But likely that
// should be considered a bug fix, too.
// XXX: Introduce a separate check which adds/removes the `Locale` parameter to `String.format`
// invocations, as necessary.
@AutoService(BugChecker.class)
@BugPattern(
name = "FormatStringConcatenation",
summary = "Defer string concatenation to the invoked method",
linkType = LinkType.NONE,
severity = SeverityLevel.WARNING,
tags = StandardTags.SIMPLIFICATION)
public final class FormatStringConcatenationCheck extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
/**
* AssertJ exposes varargs {@code fail} methods with a {@link Throwable}-accepting overload, the
* latter of which should not be flagged.
*/
private static final Matcher<ExpressionTree> ASSERTJ_FAIL_WITH_THROWABLE_METHOD =
anyMethod()
.anyClass()
.withAnyName()
.withParameters(String.class.getName(), Throwable.class.getName());
// XXX: Drop some of these methods if we use Refaster to replace some with others.
private static final Matcher<ExpressionTree> ASSERTJ_FORMAT_METHOD =
anyOf(
instanceMethod()
.onDescendantOf("org.assertj.core.api.AbstractAssert")
.namedAnyOf("overridingErrorMessage", "withFailMessage"),
allOf(
instanceMethod()
.onDescendantOf("org.assertj.core.api.AbstractSoftAssertions")
.named("fail"),
not(ASSERTJ_FAIL_WITH_THROWABLE_METHOD)),
instanceMethod()
.onDescendantOf("org.assertj.core.api.AbstractStringAssert")
.named("isEqualTo"),
instanceMethod()
.onDescendantOf("org.assertj.core.api.AbstractThrowableAssert")
.namedAnyOf(
"hasMessage",
"hasMessageContaining",
"hasMessageEndingWith",
"hasMessageStartingWith",
"hasRootCauseMessage",
"hasStackTraceContaining"),
instanceMethod()
.onDescendantOf("org.assertj.core.api.Descriptable")
.namedAnyOf("as", "describedAs"),
instanceMethod()
.onDescendantOf("org.assertj.core.api.ThrowableAssertAlternative")
.namedAnyOf(
"withMessage",
"withMessageContaining",
"withMessageEndingWith",
"withMessageStartingWith",
"withStackTraceContaining"),
allOf(
instanceMethod().onDescendantOf("org.assertj.core.api.WithAssertions").named("fail"),
not(ASSERTJ_FAIL_WITH_THROWABLE_METHOD)),
allOf(
staticMethod()
.onClassAny(
"org.assertj.core.api.Assertions",
"org.assertj.core.api.BDDAssertions",
"org.assertj.core.api.Fail")
.named("fail"),
not(ASSERTJ_FAIL_WITH_THROWABLE_METHOD)));
private static final Matcher<ExpressionTree> GUAVA_FORMAT_METHOD =
anyOf(
staticMethod()
.onClass("com.google.common.base.Preconditions")
.namedAnyOf("checkArgument", "checkNotNull", "checkState"),
staticMethod().onClass("com.google.common.base.Verify").named("verify"));
// XXX: Add `PrintWriter`, maybe others.
private static final Matcher<ExpressionTree> JDK_FORMAT_METHOD =
anyOf(
staticMethod().onClass("java.lang.String").named("format"),
instanceMethod().onExactClass("java.util.Formatter").named("format"));
private static final Matcher<ExpressionTree> SLF4J_FORMAT_METHOD =
instanceMethod()
.onDescendantOf("org.slf4j.Logger")
.namedAnyOf("debug", "error", "info", "trace", "warn");
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (hasNonConstantStringConcatenationArgument(tree, 0, state)) {
return flagViolation(tree, ASSERTJ_FORMAT_METHOD, 0, "%s", state)
.or(() -> flagViolation(tree, JDK_FORMAT_METHOD, 0, "%s", state))
.or(() -> flagViolation(tree, SLF4J_FORMAT_METHOD, 0, "{}", state))
.orElse(Description.NO_MATCH);
}
if (hasNonConstantStringConcatenationArgument(tree, 1, state)) {
return flagViolation(tree, GUAVA_FORMAT_METHOD, 1, "%s", state)
.or(() -> flagViolation(tree, JDK_FORMAT_METHOD, 1, "%s", state))
.or(() -> flagViolation(tree, SLF4J_FORMAT_METHOD, 1, "{}", state))
.orElse(Description.NO_MATCH);
}
return Description.NO_MATCH;
}
/**
* Flags the given method invocation if it matches a targeted method and passes a non-compile time
* constant string concatenation as a format string.
*/
private Optional<Description> flagViolation(
MethodInvocationTree tree,
Matcher<ExpressionTree> matcher,
int formatStringParam,
String formatSpecifier,
VisitorState state) {
if (!matcher.matches(tree, state)) {
/* The invoked method is not targeted by this check. */
return Optional.empty();
}
List<? extends ExpressionTree> arguments = tree.getArguments();
if (arguments.size() > formatStringParam + 1) {
/*
* This method invocation uses explicit string concatenation but _also_ already relies on
* format specifiers: flag but don't suggest a fix.
*/
return Optional.of(describeMatch(tree));
}
ExpressionTree formatStringArg = arguments.get(formatStringParam);
ReplacementArgumentsConstructor replacementConstructor =
new ReplacementArgumentsConstructor(formatSpecifier);
formatStringArg.accept(replacementConstructor, state);
return Optional.of(
describeMatch(
tree,
SuggestedFix.replace(
formatStringArg, replacementConstructor.getReplacementArguments(state))));
}
private static boolean hasNonConstantStringConcatenationArgument(
MethodInvocationTree tree, int argPosition, VisitorState state) {
List<? extends ExpressionTree> arguments = tree.getArguments();
if (arguments.size() <= argPosition) {
/* This method doesn't accept enough parameters. */
return false;
}
ExpressionTree argument = ASTHelpers.stripParentheses(arguments.get(argPosition));
return argument instanceof BinaryTree
&& isStringTyped(argument, state)
&& ASTHelpers.constValue(argument, String.class) == null;
}
private static boolean isStringTyped(ExpressionTree tree, VisitorState state) {
return ASTHelpers.isSameType(ASTHelpers.getType(tree), state.getSymtab().stringType, state);
}
private static class ReplacementArgumentsConstructor
extends SimpleTreeVisitor<Void, VisitorState> {
private final StringBuilder formatString = new StringBuilder();
private final List<Tree> formatArguments = new ArrayList<>();
private final String formatSpecifier;
ReplacementArgumentsConstructor(String formatSpecifier) {
this.formatSpecifier = formatSpecifier;
}
@Override
public Void visitBinary(BinaryTree tree, VisitorState state) {
if (tree.getKind() == Kind.PLUS && isStringTyped(tree, state)) {
tree.getLeftOperand().accept(this, state);
tree.getRightOperand().accept(this, state);
} else {
appendExpression(tree);
}
return null;
}
@Override
public Void visitParenthesized(ParenthesizedTree tree, VisitorState state) {
return tree.getExpression().accept(this, state);
}
@Override
protected Void defaultAction(Tree tree, VisitorState state) {
appendExpression(tree);
return null;
}
private void appendExpression(Tree tree) {
if (tree instanceof LiteralTree) {
formatString.append(((LiteralTree) tree).getValue());
} else {
formatString.append(formatSpecifier);
formatArguments.add(tree);
}
}
private String getReplacementArguments(VisitorState state) {
return state.getConstantExpression(formatString.toString())
+ ", "
+ formatArguments.stream()
.map(tree -> Util.treeToString(tree, state))
.collect(joining(", "));
}
}
}

View File

@@ -43,15 +43,15 @@ public final class JUnitMethodDeclarationCheck extends BugChecker implements Met
private static final String TEST_PREFIX = "test";
private static final ImmutableSet<Modifier> ILLEGAL_MODIFIERS =
ImmutableSet.of(Modifier.PRIVATE, Modifier.PROTECTED, Modifier.PUBLIC);
private static final MultiMatcher<MethodTree, AnnotationTree> IS_OVERRIDE_METHOD =
private static final MultiMatcher<MethodTree, AnnotationTree> OVERRIDE_METHOD =
annotations(AT_LEAST_ONE, isType("java.lang.Override"));
private static final MultiMatcher<MethodTree, AnnotationTree> IS_TEST_METHOD =
private static final MultiMatcher<MethodTree, AnnotationTree> TEST_METHOD =
annotations(
AT_LEAST_ONE,
anyOf(
isType("org.junit.jupiter.api.Test"),
hasMetaAnnotation("org.junit.jupiter.api.TestTemplate")));
private static final MultiMatcher<MethodTree, AnnotationTree> IS_SETUP_OR_TEARDOWN_METHOD =
private static final MultiMatcher<MethodTree, AnnotationTree> SETUP_OR_TEARDOWN_METHOD =
annotations(
AT_LEAST_ONE,
anyOf(
@@ -64,12 +64,12 @@ public final class JUnitMethodDeclarationCheck extends BugChecker implements Met
public Description matchMethod(MethodTree tree, VisitorState state) {
// XXX: Perhaps we should also skip analysis of non-`private` non-`final` methods in abstract
// classes?
if (IS_OVERRIDE_METHOD.matches(tree, state)) {
if (OVERRIDE_METHOD.matches(tree, state)) {
return Description.NO_MATCH;
}
boolean isTestMethod = IS_TEST_METHOD.matches(tree, state);
if (!isTestMethod && !IS_SETUP_OR_TEARDOWN_METHOD.matches(tree, state)) {
boolean isTestMethod = TEST_METHOD.matches(tree, state);
if (!isTestMethod && !SETUP_OR_TEARDOWN_METHOD.matches(tree, state)) {
return Description.NO_MATCH;
}
@@ -102,7 +102,7 @@ public final class JUnitMethodDeclarationCheck extends BugChecker implements Met
private static Matcher<AnnotationTree> hasMetaAnnotation(String annotationClassName) {
TypePredicate typePredicate = hasAnnotation(annotationClassName);
return (tree, state) -> {
Symbol sym = ASTHelpers.getDeclaredSymbol(tree);
Symbol sym = ASTHelpers.getSymbol(tree);
return sym != null && typePredicate.apply(sym.type, state);
};
}

View File

@@ -1,5 +1,6 @@
package tech.picnic.errorprone.bugpatterns;
import static java.util.Comparator.comparing;
import static java.util.Comparator.naturalOrder;
import static java.util.stream.Collectors.joining;
@@ -31,7 +32,6 @@ import com.sun.source.tree.Tree.Kind;
import com.sun.source.util.TreeScanner;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
@@ -153,7 +153,7 @@ public final class LexicographicalAnnotationAttributeListingCheck extends BugChe
// XXX: Perhaps we should use `Collator` with `.setStrength(Collator.PRIMARY)` and
// `getCollationKey`. Not clear whether that's worth the hassle at this point.
return ImmutableList.sortedCopyOf(
Comparator.comparing(
comparing(
e -> getStructure(e, state),
Comparators.lexicographical(
Comparators.lexicographical(

View File

@@ -0,0 +1,79 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.Comparator.comparing;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.LinkType;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.BugPattern.StandardTags;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
import com.google.errorprone.fixes.Fix;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.MethodTree;
import java.util.List;
import java.util.Optional;
/**
* A {@link BugChecker} that flags annotations that are not lexicographically sorted.
*
* <p>The idea behind this checker is that maintaining a sorted sequence simplifies conflict
* resolution, and can even avoid it if two branches add the same annotation.
*/
@AutoService(BugChecker.class)
@BugPattern(
name = "LexicographicalAnnotationListing",
summary = "Sort annotations lexicographically where possible",
linkType = LinkType.NONE,
severity = SeverityLevel.SUGGESTION,
tags = StandardTags.STYLE)
public final class LexicographicalAnnotationListingCheck extends BugChecker
implements MethodTreeMatcher {
private static final long serialVersionUID = 1L;
@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
List<? extends AnnotationTree> originalOrdering = tree.getModifiers().getAnnotations();
if (originalOrdering.size() < 2) {
return Description.NO_MATCH;
}
ImmutableList<? extends AnnotationTree> sortedAnnotations = sort(originalOrdering, state);
if (originalOrdering.equals(sortedAnnotations)) {
return Description.NO_MATCH;
}
Optional<Fix> fix = tryFixOrdering(originalOrdering, sortedAnnotations, state);
Description.Builder description = buildDescription(tree);
fix.ifPresent(description::addFix);
return description.build();
}
private static ImmutableList<? extends AnnotationTree> sort(
List<? extends AnnotationTree> annotations, VisitorState state) {
return annotations.stream()
.sorted(comparing(annotation -> Util.treeToString(annotation, state)))
.collect(toImmutableList());
}
private static Optional<Fix> tryFixOrdering(
List<? extends AnnotationTree> originalAnnotations,
ImmutableList<? extends AnnotationTree> sortedAnnotations,
VisitorState state) {
return Streams.zip(
originalAnnotations.stream(),
sortedAnnotations.stream(),
(original, replacement) ->
SuggestedFix.builder().replace(original, Util.treeToString(replacement, state)))
.reduce(SuggestedFix.Builder::merge)
.map(SuggestedFix.Builder::build);
}
}

View File

@@ -47,6 +47,8 @@ import javax.lang.model.element.Name;
// black-and-white. Maybe we can more closely approximate it?
// XXX: With Java 9's introduction of `Predicate.not`, we could write many lambda expressions to
// `not(some::reference)`.
// XXX: This check is extremely inefficient due to its reliance on `SuggestedFixes.compilesWithFix`.
// Palantir's `LambdaMethodReference` check seems to suffer a similar issue at this time.
@AutoService(BugChecker.class)
@BugPattern(
name = "MethodReferenceUsage",
@@ -67,7 +69,10 @@ public final class MethodReferenceUsageCheck extends BugChecker
*/
return constructMethodRef(tree, tree.getBody())
.map(SuggestedFix.Builder::build)
.filter(fix -> SuggestedFixes.compilesWithFix(fix, state))
.filter(
fix ->
SuggestedFixes.compilesWithFix(
fix, state, ImmutableList.of(), /* onlyInSameCompilationUnit= */ true))
.map(fix -> describeMatch(tree, fix))
.orElse(Description.NO_MATCH);
}

View File

@@ -15,6 +15,7 @@ import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.MultiMatcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodTree;
@@ -30,7 +31,7 @@ import com.sun.source.tree.Tree;
tags = StandardTags.LIKELY_ERROR)
public final class MissingRefasterAnnotationCheck extends BugChecker implements ClassTreeMatcher {
private static final long serialVersionUID = 1L;
private static final MultiMatcher<Tree, AnnotationTree> HAS_REFASTER_ANNOTATION =
private static final MultiMatcher<Tree, AnnotationTree> REFASTER_ANNOTATION =
annotations(
AT_LEAST_ONE,
anyOf(
@@ -44,7 +45,8 @@ public final class MissingRefasterAnnotationCheck extends BugChecker implements
tree.getMembers().stream()
.filter(member -> member.getKind() == Tree.Kind.METHOD)
.map(MethodTree.class::cast)
.map(method -> HAS_REFASTER_ANNOTATION.matches(method, state))
.filter(method -> !ASTHelpers.isGeneratedConstructor(method))
.map(method -> REFASTER_ANNOTATION.matches(method, state))
.distinct()
.count();

View File

@@ -0,0 +1,58 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.matchers.Matchers.staticMethod;
import com.google.auto.service.AutoService;
import com.google.common.collect.Iterables;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.LinkType;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.BugPattern.StandardTags;
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.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import java.util.List;
/**
* A {@link BugChecker} which flags method invocations for which all arguments are wrapped using
* {@link org.mockito.Mockito#eq}; this is redundant.
*/
@AutoService(BugChecker.class)
@BugPattern(
name = "MockitoStubbing",
summary = "Don't unnecessarily use Mockito's `eq(...)`",
linkType = LinkType.NONE,
severity = SeverityLevel.SUGGESTION,
tags = StandardTags.SIMPLIFICATION)
public final class MockitoStubbingCheck 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");
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
List<? extends ExpressionTree> arguments = tree.getArguments();
if (arguments.isEmpty() || !arguments.stream().allMatch(arg -> isEqInvocation(arg, state))) {
return Description.NO_MATCH;
}
SuggestedFix.Builder suggestedFix = SuggestedFix.builder();
for (ExpressionTree arg : arguments) {
suggestedFix.replace(
arg,
Util.treeToString(
Iterables.getOnlyElement(((MethodInvocationTree) arg).getArguments()), state));
}
return describeMatch(tree, suggestedFix.build());
}
private static boolean isEqInvocation(ExpressionTree tree, VisitorState state) {
return tree instanceof MethodInvocationTree && MOCKITO_EQ_METHOD.matches(tree, state);
}
}

View File

@@ -2,7 +2,7 @@ package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.isNonNull;
import static com.google.errorprone.matchers.Matchers.isNonNullUsingDataflow;
import static com.google.errorprone.matchers.Matchers.isSameType;
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
import static com.google.errorprone.matchers.Matchers.not;
@@ -64,7 +64,8 @@ public final class RedundantStringConversionCheck extends BugChecker
private static final Matcher<ExpressionTree> MARKER = isSubtypeOf("org.slf4j.Marker");
private static final Matcher<ExpressionTree> STRING = isSameType(String.class);
private static final Matcher<ExpressionTree> THROWABLE = isSubtypeOf(Throwable.class);
private static final Matcher<ExpressionTree> NON_NULL_STRING = allOf(STRING, isNonNull());
private static final Matcher<ExpressionTree> NON_NULL_STRING =
allOf(STRING, isNonNullUsingDataflow());
private static final Matcher<ExpressionTree> NOT_FORMATTABLE =
not(isSubtypeOf(Formattable.class));
private static final Matcher<ExpressionTree> WELL_KNOWN_STRING_CONVERSION_METHODS =

View File

@@ -0,0 +1,57 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.LinkType;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.BugPattern.StandardTags;
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.refaster.Refaster;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
/**
* A {@link BugChecker} which flags unnecessary {@link Refaster#anyOf(Object[])} usages.
*
* <p>Note that this logic can't be implemented as a Refaster template, as the {@link Refaster}
* class is treated specially.
*/
@AutoService(BugChecker.class)
@BugPattern(
name = "RefasterAnyOfUsage",
summary = "`Refaster#anyOf` should be passed at least two parameters",
linkType = LinkType.NONE,
severity = SeverityLevel.SUGGESTION,
tags = StandardTags.SIMPLIFICATION)
public final class RefasterAnyOfUsageCheck 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");
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (REFASTER_ANY_OF.matches(tree, state)) {
switch (tree.getArguments().size()) {
case 0:
// We can't safely fix this case; dropping the expression may produce non-compilable code.
return describeMatch(tree);
case 1:
return describeMatch(
tree,
SuggestedFix.replace(tree, Util.treeToString(tree.getArguments().get(0), state)));
default:
/* Handled below. */
}
}
return Description.NO_MATCH;
}
}

View File

@@ -2,10 +2,12 @@ package tech.picnic.errorprone.bugpatterns;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableRangeSet.toImmutableRangeSet;
import static java.util.Objects.requireNonNullElseGet;
import static java.util.function.Predicate.not;
import com.google.auto.service.AutoService;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
@@ -41,8 +43,8 @@ import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Stream;
@@ -62,9 +64,13 @@ import java.util.stream.Stream;
severity = SeverityLevel.SUGGESTION,
tags = StandardTags.SIMPLIFICATION)
public final class RefasterCheck extends BugChecker implements CompilationUnitTreeMatcher {
private static final long serialVersionUID = 1L;
private static final String REFASTER_TEMPLATE_SUFFIX = ".refaster";
private static final String INCLUDED_TEMPLATES_PATTERN_FLAG = "Refaster:NamePattern";
private static final long serialVersionUID = 1L;
@VisibleForTesting
static final Supplier<ImmutableListMultimap<String, CodeTransformer>> ALL_CODE_TRANSFORMERS =
Suppliers.memoize(RefasterCheck::loadAllCodeTransformers);
private final CodeTransformer codeTransformer;
@@ -79,20 +85,23 @@ public final class RefasterCheck extends BugChecker implements CompilationUnitTr
* @param flags Any provided command line flags.
*/
public RefasterCheck(ErrorProneFlags flags) {
codeTransformer = createCompositeCodeTransformer(flags, loadAllCodeTransformers());
}
@VisibleForTesting
RefasterCheck(
ErrorProneFlags flags, ImmutableListMultimap<String, CodeTransformer> allTransformers) {
codeTransformer = createCompositeCodeTransformer(flags, allTransformers);
codeTransformer = createCompositeCodeTransformer(flags);
}
@Override
public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) {
/* First, collect all matches. */
List<Description> matches = new ArrayList<>();
codeTransformer.apply(state.getPath(), new SubContext(state.context), matches::add);
try {
codeTransformer.apply(state.getPath(), new SubContext(state.context), matches::add);
} catch (LinkageError e) {
// XXX: This `try/catch` block handles the issue described and resolved in
// https://github.com/google/error-prone/pull/2456. Drop this block once that change is
// released.
// XXX: Find a way to identify that we're running Picnic's Error Prone fork and disable this
// fallback if so, as it might hide other bugs.
return Description.NO_MATCH;
}
/* Then apply them. */
applyMatches(matches, ((JCCompilationUnit) tree).endPositions, state);
@@ -152,8 +161,8 @@ public final class RefasterCheck extends BugChecker implements CompilationUnitTr
return description.fixes.stream().flatMap(fix -> fix.getReplacements(endPositions).stream());
}
private static CodeTransformer createCompositeCodeTransformer(
ErrorProneFlags flags, ImmutableListMultimap<String, CodeTransformer> allTransformers) {
private static CodeTransformer createCompositeCodeTransformer(ErrorProneFlags flags) {
ImmutableListMultimap<String, CodeTransformer> allTransformers = ALL_CODE_TRANSFORMERS.get();
return CompositeCodeTransformer.compose(
flags
.get(INCLUDED_TEMPLATES_PATTERN_FLAG)
@@ -170,8 +179,7 @@ public final class RefasterCheck extends BugChecker implements CompilationUnitTr
.collect(toImmutableList());
}
@VisibleForTesting
static ImmutableListMultimap<String, CodeTransformer> loadAllCodeTransformers() {
private static ImmutableListMultimap<String, CodeTransformer> loadAllCodeTransformers() {
ImmutableListMultimap.Builder<String, CodeTransformer> transformers =
ImmutableListMultimap.builder();
@@ -189,8 +197,8 @@ public final class RefasterCheck extends BugChecker implements CompilationUnitTr
private static ImmutableSet<ResourceInfo> getClassPathResources() {
try {
return ClassPath.from(
Objects.requireNonNullElseGet(
RefasterCheck.class.getClassLoader(), () -> ClassLoader.getSystemClassLoader()))
requireNonNullElseGet(
RefasterCheck.class.getClassLoader(), ClassLoader::getSystemClassLoader))
.getResources();
} catch (IOException e) {
throw new UncheckedIOException("Failed to scan classpath for resources", e);
@@ -212,7 +220,9 @@ public final class RefasterCheck extends BugChecker implements CompilationUnitTr
private static Optional<CodeTransformer> loadCodeTransformer(ResourceInfo resource) {
try (InputStream in = resource.url().openStream();
ObjectInputStream ois = new ObjectInputStream(in)) {
return Optional.of((CodeTransformer) ois.readObject());
@SuppressWarnings("BanSerializableRead" /* Part of the Refaster API. */)
CodeTransformer codeTransformer = (CodeTransformer) ois.readObject();
return Optional.of(codeTransformer);
} catch (NoSuchElementException e) {
/* For some reason we can't load the resource. Skip it. */
// XXX: Should we log this?

View File

@@ -13,7 +13,6 @@ import static com.google.errorprone.matchers.Matchers.not;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.LinkType;
import com.google.errorprone.BugPattern.ProvidesFix;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.BugPattern.StandardTags;
import com.google.errorprone.VisitorState;
@@ -36,8 +35,7 @@ import com.sun.source.tree.Tree;
summary = "Make sure all `@RequestMapping` method parameters are annotated",
linkType = LinkType.NONE,
severity = SeverityLevel.WARNING,
tags = StandardTags.LIKELY_ERROR,
providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION)
tags = StandardTags.LIKELY_ERROR)
public final class RequestMappingAnnotationCheck extends BugChecker implements MethodTreeMatcher {
private static final long serialVersionUID = 1L;
private static final String ANN_PACKAGE_PREFIX = "org.springframework.web.bind.annotation.";

View File

@@ -0,0 +1,88 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.matchers.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
import static com.google.errorprone.matchers.Matchers.annotations;
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
import static com.google.errorprone.matchers.Matchers.isType;
import com.google.auto.common.AnnotationMirrors;
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.BugPattern.LinkType;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.BugPattern.StandardTags;
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.google.errorprone.matchers.MultiMatcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
/**
* A {@link BugChecker} which flags methods with Spring's {@code @Scheduled} annotation that lack
* New Relic Agent's {@code @Trace(dispatcher = true)}.
*/
@AutoService(BugChecker.class)
@BugPattern(
name = "ScheduledTransactionTrace",
summary = "Scheduled operation must start a new New Relic transaction",
linkType = LinkType.NONE,
severity = SeverityLevel.ERROR,
tags = StandardTags.LIKELY_ERROR)
public final class ScheduledTransactionTraceCheck 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 =
hasAnnotation("org.springframework.scheduling.annotation.Scheduled");
private static final MultiMatcher<Tree, AnnotationTree> TRACE_ANNOTATION =
annotations(AT_LEAST_ONE, isType(TRACE_ANNOTATION_FQCN));
@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
if (!IS_SCHEDULED.matches(tree, state)) {
return Description.NO_MATCH;
}
ImmutableList<AnnotationTree> traceAnnotations =
TRACE_ANNOTATION.multiMatchResult(tree, state).matchingNodes();
if (traceAnnotations.isEmpty()) {
/* This method completely lacks the `@Trace` annotation; add it. */
return describeMatch(
tree,
SuggestedFix.builder()
.addImport(TRACE_ANNOTATION_FQCN)
.prefixWith(tree, "@Trace(dispatcher = true)")
.build());
}
AnnotationTree traceAnnotation = Iterables.getOnlyElement(traceAnnotations);
if (isCorrectAnnotation(traceAnnotation)) {
return Description.NO_MATCH;
}
/*
* The `@Trace` annotation is present but does not specify `dispatcher = true`. Add or update
* the `dispatcher` annotation element.
*/
return describeMatch(
traceAnnotation,
SuggestedFixes.updateAnnotationArgumentValues(
traceAnnotation, "dispatcher", ImmutableList.of("true"))
.build());
}
private static boolean isCorrectAnnotation(AnnotationTree traceAnnotation) {
return Boolean.TRUE.equals(
AnnotationMirrors.getAnnotationValue(
ASTHelpers.getAnnotationMirror(traceAnnotation), "dispatcher")
.getValue());
}
}

View File

@@ -1,5 +1,7 @@
package tech.picnic.errorprone.bugpatterns;
import static java.util.Objects.requireNonNull;
import com.google.auto.service.AutoService;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
@@ -17,15 +19,18 @@ import com.google.errorprone.fixes.Fix;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.fixes.SuggestedFixes;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree;
import java.util.Objects;
import com.sun.tools.javac.code.Type;
import java.util.Optional;
/** A {@link BugChecker} which flags methods that can and should be statically imported. */
/**
* A {@link BugChecker} which flags methods and constants that can and should be statically
* imported.
*/
// XXX: Tricky cases:
// - `org.springframework.http.MediaType` (do except for `ALL`?)
// - `org.springframework.http.HttpStatus` (not always an improvement, and `valueOf` must
// certainly be excluded)
// - `com.google.common.collect.Tables`
@@ -41,15 +46,19 @@ import java.util.Optional;
@AutoService(BugChecker.class)
@BugPattern(
name = "StaticImport",
summary = "Method should be statically imported",
summary = "Identifier should be statically imported",
linkType = LinkType.NONE,
severity = SeverityLevel.SUGGESTION,
tags = StandardTags.SIMPLIFICATION)
public final class StaticImportCheck extends BugChecker implements MemberSelectTreeMatcher {
private static final long serialVersionUID = 1L;
/**
* Types whose members should be statically imported, unless exempted by {@link
* #STATIC_IMPORT_EXEMPTED_MEMBERS} or {@link #STATIC_IMPORT_EXEMPTED_IDENTIFIERS}.
*/
@VisibleForTesting
static final ImmutableSet<String> STATIC_IMPORT_CANDIDATE_CLASSES =
static final ImmutableSet<String> STATIC_IMPORT_CANDIDATE_TYPES =
ImmutableSet.of(
"com.google.common.base.Preconditions",
"com.google.common.base.Predicates",
@@ -79,14 +88,17 @@ public final class StaticImportCheck extends BugChecker implements MemberSelectT
"org.mockito.Answers",
"org.mockito.ArgumentMatchers",
"org.mockito.Mockito",
"org.springframework.boot.test.context.SpringBootTest.WebEnvironment",
"org.springframework.format.annotation.DateTimeFormat.ISO",
"org.springframework.http.HttpHeaders",
"org.springframework.http.HttpMethod",
"org.springframework.http.MediaType",
"org.testng.Assert",
"reactor.function.TupleUtils");
/** Type members that should be statically imported. */
@VisibleForTesting
static final ImmutableSetMultimap<String, String> STATIC_IMPORT_CANDIDATE_METHODS =
static final ImmutableSetMultimap<String, String> STATIC_IMPORT_CANDIDATE_MEMBERS =
ImmutableSetMultimap.<String, String>builder()
.putAll(
"com.google.common.collect.ImmutableListMultimap",
@@ -121,9 +133,41 @@ public final class StaticImportCheck extends BugChecker implements MemberSelectT
.putAll("com.google.common.collect.Comparators", "emptiesFirst", "emptiesLast")
.build();
/**
* Type members that should never be statically imported.
*
* <p>Identifiers listed by {@link #STATIC_IMPORT_EXEMPTED_IDENTIFIERS} should be omitted from
* this collection.
*/
@VisibleForTesting
static final ImmutableSetMultimap<String, String> STATIC_IMPORT_EXEMPTED_MEMBERS =
ImmutableSetMultimap.<String, String>builder()
.put("com.mongodb.client.model.Filters", "empty")
.put("org.springframework.http.MediaType", "ALL")
.build();
/**
* Identifiers that should never be statically imported.
*
* <p>This should be a superset of the identifiers flagged by {@link
* com.google.errorprone.bugpatterns.BadImport}.
*/
@VisibleForTesting
static final ImmutableSet<String> STATIC_IMPORT_EXEMPTED_IDENTIFIERS =
ImmutableSet.of(
"builder",
"create",
"copyOf",
"from",
"getDefaultInstance",
"INSTANCE",
"newBuilder",
"of",
"valueOf");
@Override
public Description matchMemberSelect(MemberSelectTree tree, VisitorState state) {
if (!isCandidate(state)) {
if (!isCandidateContext(state) || !isCandidate(tree)) {
return Description.NO_MATCH;
}
@@ -138,10 +182,9 @@ public final class StaticImportCheck extends BugChecker implements MemberSelectT
.orElse(Description.NO_MATCH);
}
private static boolean isCandidate(VisitorState state) {
private static boolean isCandidateContext(VisitorState state) {
Tree parentTree =
Objects.requireNonNull(
state.getPath().getParentPath(), "MemberSelectTree lacks enclosing node")
requireNonNull(state.getPath().getParentPath(), "MemberSelectTree lacks enclosing node")
.getLeaf();
switch (parentTree.getKind()) {
case IMPORT:
@@ -154,6 +197,17 @@ public final class StaticImportCheck extends BugChecker implements MemberSelectT
}
}
private static boolean isCandidate(MemberSelectTree tree) {
String identifier = tree.getIdentifier().toString();
if (STATIC_IMPORT_EXEMPTED_IDENTIFIERS.contains(identifier)) {
return false;
}
Type type = ASTHelpers.getType(tree.getExpression());
return type != null
&& !STATIC_IMPORT_EXEMPTED_MEMBERS.containsEntry(type.toString(), identifier);
}
private static Optional<String> getCandidateSimpleName(StaticImportInfo importInfo) {
String canonicalName = importInfo.canonicalName();
return importInfo
@@ -161,8 +215,8 @@ public final class StaticImportCheck extends BugChecker implements MemberSelectT
.toJavaUtil()
.filter(
name ->
STATIC_IMPORT_CANDIDATE_CLASSES.contains(canonicalName)
|| STATIC_IMPORT_CANDIDATE_METHODS.containsEntry(canonicalName, name));
STATIC_IMPORT_CANDIDATE_TYPES.contains(canonicalName)
|| STATIC_IMPORT_CANDIDATE_MEMBERS.containsEntry(canonicalName, name));
}
private static Optional<Fix> tryStaticImport(

View File

@@ -0,0 +1,70 @@
package tech.picnic.errorprone.bugpatterns;
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.instanceMethod;
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
import static com.google.errorprone.matchers.Matchers.not;
import static com.google.errorprone.matchers.Matchers.staticMethod;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.LinkType;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.BugPattern.StandardTags;
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.matchers.Matcher;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import java.time.Clock;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
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 = LinkType.NONE,
severity = SeverityLevel.WARNING,
tags = StandardTags.FRAGILE_CODE)
public final class TimeZoneUsageCheck extends BugChecker implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> BANNED_TIME_METHOD =
anyOf(
allOf(
instanceMethod()
.onDescendantOf(Clock.class.getName())
.namedAnyOf("getZone", "withZone"),
not(enclosingClass(isSubtypeOf(Clock.class)))),
staticMethod()
.onClass(Clock.class.getName())
.namedAnyOf(
"system",
"systemDefaultZone",
"systemUTC",
"tickMillis",
"tickMinutes",
"tickSeconds"),
staticMethod()
.onClassAny(
LocalDate.class.getName(),
LocalDateTime.class.getName(),
LocalTime.class.getName())
.named("now"),
staticMethod().onClassAny(Instant.class.getName()).named("now").withParameters());
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
return BANNED_TIME_METHOD.matches(tree, state)
? buildDescription(tree).build()
: Description.NO_MATCH;
}
}

View File

@@ -1,7 +1,4 @@
/** Picnic Error Prone Contrib checks. */
@CheckReturnValue
@ParametersAreNonnullByDefault
@com.google.errorprone.annotations.CheckReturnValue
@javax.annotation.ParametersAreNonnullByDefault
package tech.picnic.errorprone.bugpatterns;
import com.google.errorprone.annotations.CheckReturnValue;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -97,12 +97,24 @@ final class AssertJOptionalTemplates {
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
AbstractOptionalAssert<?, T> after(AbstractOptionalAssert<?, T> optionalAssert, T value) {
return optionalAssert.hasValue(value);
}
}
static final class AbstractOptionalAssertContainsSame<T> {
@BeforeTemplate
AbstractAssert<?, ?> before(AbstractOptionalAssert<?, T> optionalAssert, T value) {
return Refaster.anyOf(
optionalAssert.get().isSameAs(value), optionalAssert.isPresent().isSameAs(value));
}
@AfterTemplate
AbstractOptionalAssert<?, T> after(AbstractOptionalAssert<?, T> optionalAssert, T value) {
return optionalAssert.containsSame(value);
}
}
static final class AssertThatOptionalHasValueMatching<T> {
@BeforeTemplate
AbstractOptionalAssert<?, T> before(Optional<T> optional, Predicate<? super T> predicate) {

View File

@@ -16,6 +16,7 @@ import com.google.errorprone.refaster.ImportPolicy;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.NotMatches;
import com.google.errorprone.refaster.annotation.Repeated;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.ArrayList;
@@ -38,6 +39,7 @@ import java.util.stream.Collector;
import java.util.stream.Stream;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AbstractBooleanAssert;
import org.assertj.core.api.AbstractCollectionAssert;
import org.assertj.core.api.AbstractComparableAssert;
import org.assertj.core.api.AbstractDoubleAssert;
import org.assertj.core.api.AbstractIntegerAssert;
@@ -51,6 +53,7 @@ import org.assertj.core.api.ObjectEnumerableAssert;
import org.assertj.core.api.OptionalDoubleAssert;
import org.assertj.core.api.OptionalIntAssert;
import org.assertj.core.api.OptionalLongAssert;
import tech.picnic.errorprone.refaster.util.IsArray;
/** Refaster templates related to AssertJ expressions and statements. */
// XXX: Most `AbstractIntegerAssert` rules can also be applied for other primitive types. Generate
@@ -338,8 +341,14 @@ final class AssertJTemplates {
ImmutableList.of(element),
Arrays.asList(element),
ImmutableSet.of(element),
ImmutableMultiset.of(element))),
iterAssert.containsExactlyInAnyOrder(element));
ImmutableMultiset.of(element))));
}
@BeforeTemplate
@SuppressWarnings("unchecked")
ObjectEnumerableAssert<?, S> before2(
ObjectEnumerableAssert<?, S> iterAssert, @NotMatches(IsArray.class) T element) {
return iterAssert.containsExactlyInAnyOrder(element);
}
@AfterTemplate
@@ -349,16 +358,28 @@ final class AssertJTemplates {
}
}
static final class AssertThatSetContainsExactlyOneElement<S, T extends S> {
@BeforeTemplate
ObjectEnumerableAssert<?, S> before(Set<S> set, T element) {
return assertThat(set).containsOnly(element);
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
ObjectEnumerableAssert<?, S> after(Set<S> set, T element) {
return assertThat(set).containsExactly(element);
}
}
static final class ObjectEnumerableContainsOneDistinctElement<S, T extends S> {
@BeforeTemplate
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T element) {
return Refaster.anyOf(
iterAssert.hasSameElementsAs(
Refaster.anyOf(
ImmutableList.of(element),
Arrays.asList(element),
ImmutableSet.of(element),
ImmutableMultiset.of(element))));
return iterAssert.hasSameElementsAs(
Refaster.anyOf(
ImmutableList.of(element),
Arrays.asList(element),
ImmutableSet.of(element),
ImmutableMultiset.of(element)));
}
@AfterTemplate
@@ -371,13 +392,12 @@ final class AssertJTemplates {
static final class ObjectEnumerableIsSubsetOfOneElement<S, T extends S> {
@BeforeTemplate
ObjectEnumerableAssert<?, S> before(ObjectEnumerableAssert<?, S> iterAssert, T element) {
return Refaster.anyOf(
iterAssert.isSubsetOf(
Refaster.anyOf(
ImmutableList.of(element),
Arrays.asList(element),
ImmutableSet.of(element),
ImmutableMultiset.of(element))));
return iterAssert.isSubsetOf(
Refaster.anyOf(
ImmutableList.of(element),
Arrays.asList(element),
ImmutableSet.of(element),
ImmutableMultiset.of(element)));
}
@AfterTemplate
@@ -395,8 +415,8 @@ final class AssertJTemplates {
@BeforeTemplate
void before(Iterable<E> iterable) {
Refaster.anyOf(
assertThat(iterable).hasSize(0),
assertThat(iterable.iterator().hasNext()).isFalse(),
assertThat(Iterables.size(iterable)).isEqualTo(0),
assertThat(Iterables.size(iterable)).isEqualTo(0L),
assertThat(Iterables.size(iterable)).isNotPositive());
}
@@ -405,7 +425,6 @@ final class AssertJTemplates {
void before(Collection<E> iterable) {
Refaster.anyOf(
assertThat(iterable.isEmpty()).isTrue(),
assertThat(iterable.size()).isEqualTo(0),
assertThat(iterable.size()).isEqualTo(0L),
assertThat(iterable.size()).isNotPositive());
}
@@ -512,7 +531,7 @@ final class AssertJTemplates {
static final class AssertThatSetsAreEqual<S, T extends S> {
@BeforeTemplate
IterableAssert<S> before(Set<S> set1, Set<T> set2) {
AbstractCollectionAssert<?, ?, S, ?> before(Set<S> set1, Set<T> set2) {
return Refaster.anyOf(
assertThat(set1).isEqualTo(set2),
assertThat(set1).containsExactlyInAnyOrderElementsOf(set2));
@@ -520,7 +539,7 @@ final class AssertJTemplates {
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
IterableAssert<S> after(Set<S> set1, Set<T> set2) {
AbstractCollectionAssert<?, ?, S, ?> after(Set<S> set1, Set<T> set2) {
return assertThat(set1).hasSameElementsAs(set2);
}
}
@@ -531,13 +550,13 @@ final class AssertJTemplates {
static final class AssertThatMultisetsAreEqual<S, T extends S> {
@BeforeTemplate
IterableAssert<S> before(Multiset<S> multiset1, Multiset<T> multiset2) {
AbstractCollectionAssert<?, ?, S, ?> before(Multiset<S> multiset1, Multiset<T> multiset2) {
return assertThat(multiset1).isEqualTo(multiset2);
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
IterableAssert<S> after(Multiset<S> multiset1, Multiset<T> multiset2) {
AbstractCollectionAssert<?, ?, S, ?> after(Multiset<S> multiset1, Multiset<T> multiset2) {
return assertThat(multiset1).containsExactlyInAnyOrderElementsOf(multiset2);
}
}
@@ -602,8 +621,8 @@ final class AssertJTemplates {
@BeforeTemplate
void before(Map<K, V> map) {
Refaster.anyOf(
assertThat(map).hasSize(0),
assertThat(map.isEmpty()).isTrue(),
assertThat(map.size()).isEqualTo(0),
assertThat(map.size()).isEqualTo(0L),
assertThat(map.size()).isNotPositive());
}
@@ -775,18 +794,21 @@ final class AssertJTemplates {
// XXX: This rule assumes the `collector` doesn't completely discard certain values.
static final class AssertThatStreamContainsAnyOfVarArgs<S, T extends S, U extends T> {
@BeforeTemplate
@SuppressWarnings("AssertThatStreamContainsAnyOf" /* Varargs converted to array. */)
IterableAssert<T> before(
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector)).containsAnyOf(Refaster.asVarargs(elements));
}
@BeforeTemplate
@SuppressWarnings("AssertThatStreamContainsAnyOf" /* Varargs converted to array. */)
ListAssert<T> before2(
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector)).containsAnyOf(Refaster.asVarargs(elements));
}
@AfterTemplate
@SuppressWarnings("ObjectEnumerableContainsOneElement" /* Not a true singleton. */)
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
ListAssert<S> after(Stream<S> stream, @Repeated U elements) {
return assertThat(stream).containsAnyOf(elements);
@@ -838,12 +860,14 @@ final class AssertJTemplates {
// XXX: This rule assumes the `collector` doesn't completely discard certain values.
static final class AssertThatStreamContainsVarArgs<S, T extends S, U extends T> {
@BeforeTemplate
@SuppressWarnings("AssertThatStreamContains" /* Varargs converted to array. */)
IterableAssert<T> before(
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector)).contains(Refaster.asVarargs(elements));
}
@BeforeTemplate
@SuppressWarnings("AssertThatStreamContains" /* Varargs converted to array. */)
ListAssert<T> before2(
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector)).contains(Refaster.asVarargs(elements));
@@ -889,6 +913,7 @@ final class AssertJTemplates {
// XXX: This rule assumes the `collector` doesn't completely discard certain values.
static final class AssertThatStreamContainsExactlyVarargs<S, T extends S, U extends T> {
@BeforeTemplate
@SuppressWarnings("AssertThatStreamContainsExactly" /* Varargs converted to array. */)
ListAssert<T> before(
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector)).containsExactly(Refaster.asVarargs(elements));
@@ -911,7 +936,7 @@ final class AssertJTemplates {
}
@BeforeTemplate
IterableAssert<T> before2(
AbstractCollectionAssert<?, ?, T, ?> before2(
Stream<S> stream, Collector<S, ?, ? extends Multiset<T>> collector, Iterable<U> iterable) {
return assertThat(stream.collect(collector)).containsExactlyInAnyOrderElementsOf(iterable);
}
@@ -932,7 +957,7 @@ final class AssertJTemplates {
}
@BeforeTemplate
IterableAssert<T> before2(
AbstractCollectionAssert<?, ?, T, ?> before2(
Stream<S> stream, Collector<S, ?, ? extends Multiset<T>> collector, U[] array) {
return assertThat(stream.collect(collector)).containsExactlyInAnyOrder(array);
}
@@ -947,6 +972,7 @@ final class AssertJTemplates {
// XXX: This rule assumes the `collector` doesn't completely discard certain values.
static final class AssertThatStreamContainsExactlyInAnyOrderVarArgs<S, T extends S, U extends T> {
@BeforeTemplate
@SuppressWarnings("AssertThatStreamContainsExactlyInAnyOrder" /* Varargs converted to array. */)
ListAssert<T> before(
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector))
@@ -954,13 +980,15 @@ final class AssertJTemplates {
}
@BeforeTemplate
IterableAssert<T> before2(
@SuppressWarnings("AssertThatStreamContainsExactlyInAnyOrder" /* Varargs converted to array. */)
AbstractCollectionAssert<?, ?, T, ?> before2(
Stream<S> stream, Collector<S, ?, ? extends Multiset<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector))
.containsExactlyInAnyOrder(Refaster.asVarargs(elements));
}
@AfterTemplate
@SuppressWarnings("ObjectEnumerableContainsExactlyOneElement" /* Not a true singleton. */)
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
ListAssert<S> after(Stream<S> stream, @Repeated U elements) {
return assertThat(stream).containsExactlyInAnyOrder(elements);
@@ -991,12 +1019,14 @@ final class AssertJTemplates {
// XXX: This rule assumes the `collector` doesn't completely discard certain values.
static final class AssertThatStreamContainsSequenceVarArgs<S, T extends S, U extends T> {
@BeforeTemplate
@SuppressWarnings("AssertThatStreamContainsSequence" /* Varargs converted to array. */)
ListAssert<T> before(
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector)).containsSequence(Refaster.asVarargs(elements));
}
@AfterTemplate
@SuppressWarnings("ObjectEnumerableContainsOneElement" /* Not a true singleton. */)
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
ListAssert<S> after(Stream<S> stream, @Repeated U elements) {
return assertThat(stream).containsSequence(elements);
@@ -1027,6 +1057,7 @@ final class AssertJTemplates {
// XXX: This rule assumes the `collector` doesn't completely discard certain values.
static final class AssertThatStreamContainsSubsequenceVarArgs<S, T extends S, U extends T> {
@BeforeTemplate
@SuppressWarnings("AssertThatStreamContainsSubsequence" /* Varargs converted to array. */)
ListAssert<T> before(
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector))
@@ -1034,6 +1065,7 @@ final class AssertJTemplates {
}
@AfterTemplate
@SuppressWarnings("ObjectEnumerableContainsOneElement" /* Not a true singleton. */)
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
ListAssert<S> after(Stream<S> stream, @Repeated U elements) {
return assertThat(stream).containsSubsequence(elements);
@@ -1085,12 +1117,14 @@ final class AssertJTemplates {
// XXX: This rule assumes the `collector` doesn't completely discard certain values.
static final class AssertThatStreamDoesNotContainVarArgs<S, T extends S, U extends T> {
@BeforeTemplate
@SuppressWarnings("AssertThatStreamDoesNotContain" /* Varargs converted to array. */)
IterableAssert<T> before(
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector)).doesNotContain(Refaster.asVarargs(elements));
}
@BeforeTemplate
@SuppressWarnings("AssertThatStreamDoesNotContain" /* Varargs converted to array. */)
ListAssert<T> before2(
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector)).doesNotContain(Refaster.asVarargs(elements));
@@ -1127,6 +1161,7 @@ final class AssertJTemplates {
// XXX: This rule assumes the `collector` doesn't completely discard certain values.
static final class AssertThatStreamDoesNotContainSequenceVarArgs<S, T extends S, U extends T> {
@BeforeTemplate
@SuppressWarnings("AssertThatStreamDoesNotContainSequence" /* Varargs converted to array. */)
ListAssert<T> before(
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector))
@@ -1134,6 +1169,7 @@ final class AssertJTemplates {
}
@AfterTemplate
@SuppressWarnings("ObjectEnumerableDoesNotContainOneElement" /* Not a true singleton. */)
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
ListAssert<S> after(Stream<S> stream, @Repeated U elements) {
return assertThat(stream).doesNotContainSequence(elements);
@@ -1185,12 +1221,14 @@ final class AssertJTemplates {
// XXX: This rule assumes the `collector` doesn't completely discard certain values.
static final class AssertThatStreamContainsOnlyVarArgs<S, T extends S, U extends T> {
@BeforeTemplate
@SuppressWarnings("AssertThatStreamContainsOnly" /* Varargs converted to array. */)
IterableAssert<T> before(
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector)).containsOnly(Refaster.asVarargs(elements));
}
@BeforeTemplate
@SuppressWarnings("AssertThatStreamContainsOnly" /* Varargs converted to array. */)
ListAssert<T> before2(
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector)).containsOnly(Refaster.asVarargs(elements));
@@ -1239,12 +1277,14 @@ final class AssertJTemplates {
// XXX: This rule assumes the `collector` doesn't completely discard certain values.
static final class AssertThatStreamIsSubsetOfVarArgs<S, T extends S, U extends T> {
@BeforeTemplate
@SuppressWarnings("AssertThatStreamIsSubsetOf" /* Varargs converted to array. */)
IterableAssert<T> before(
Stream<S> stream, Collector<S, ?, ? extends Iterable<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector)).isSubsetOf(Refaster.asVarargs(elements));
}
@BeforeTemplate
@SuppressWarnings("AssertThatStreamIsSubsetOf" /* Varargs converted to array. */)
ListAssert<T> before2(
Stream<S> stream, Collector<S, ?, ? extends List<T>> collector, @Repeated U elements) {
return assertThat(stream.collect(collector)).isSubsetOf(Refaster.asVarargs(elements));

View File

@@ -1,9 +1,10 @@
package tech.picnic.errorprone.refastertemplates;
import static com.google.common.base.Preconditions.checkElementIndex;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Sets.toImmutableEnumSet;
import static java.util.Objects.checkIndex;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@@ -38,12 +39,12 @@ final class AssortedTemplates {
static final class CheckIndex {
@BeforeTemplate
int before(int index, int size) {
return Preconditions.checkElementIndex(index, size);
return checkElementIndex(index, size);
}
@AfterTemplate
int after(int index, int size) {
return Objects.checkIndex(index, size);
return checkIndex(index, size);
}
}
@@ -62,14 +63,14 @@ final class AssortedTemplates {
}
static final class MapGetOrNull<K, V, L> {
@Nullable
@BeforeTemplate
@Nullable
V before(Map<K, V> map, L key) {
return map.getOrDefault(key, null);
}
@Nullable
@AfterTemplate
@Nullable
V after(Map<K, V> map, L key) {
return map.get(key);
}
@@ -107,8 +108,8 @@ final class AssortedTemplates {
Streams.stream(iterator).findAny().orElse(defaultValue));
}
@Nullable
@AfterTemplate
@Nullable
T after(Iterator<T> iterator, T defaultValue) {
return Iterators.getNext(iterator, defaultValue);
}
@@ -256,11 +257,6 @@ final class AssortedTemplates {
//
// @BeforeTemplate
// void before(Supplier<T> supplier) {
// anyStatement(supplier::get);
// }
//
// @BeforeTemplate
// void before2(Supplier<T> supplier) {
// anyStatement(() -> supplier.get());
// }
//

View File

@@ -1,5 +1,10 @@
package tech.picnic.errorprone.refastertemplates;
import static java.util.Comparator.comparing;
import static java.util.Comparator.comparingDouble;
import static java.util.Comparator.comparingInt;
import static java.util.Comparator.comparingLong;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.reverseOrder;
import static java.util.function.Function.identity;
@@ -29,14 +34,13 @@ final class ComparatorTemplates {
@BeforeTemplate
Comparator<T> before() {
return Refaster.anyOf(
Comparator.comparing(Refaster.anyOf(identity(), v -> v)),
Comparator.<T>reverseOrder().reversed());
comparing(Refaster.anyOf(identity(), v -> v)), Comparator.<T>reverseOrder().reversed());
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
Comparator<T> after() {
return Comparator.naturalOrder();
return naturalOrder();
}
}
@@ -58,7 +62,7 @@ final class ComparatorTemplates {
// XXX: Drop the `Refaster.anyOf` if/when we decide to rewrite one to the other.
@BeforeTemplate
Comparator<T> before(Comparator<T> cmp) {
return Comparator.comparing(Refaster.anyOf(identity(), v -> v), cmp);
return comparing(Refaster.anyOf(identity(), v -> v), cmp);
}
@AfterTemplate
@@ -72,7 +76,7 @@ final class ComparatorTemplates {
static final class ThenComparing<S, T extends Comparable<? super T>> {
@BeforeTemplate
Comparator<S> before(Comparator<S> cmp, Function<? super S, ? extends T> function) {
return cmp.thenComparing(Comparator.comparing(function));
return cmp.thenComparing(comparing(function));
}
@AfterTemplate
@@ -85,7 +89,7 @@ final class ComparatorTemplates {
static final class ThenComparingReversed<S, T extends Comparable<? super T>> {
@BeforeTemplate
Comparator<S> before(Comparator<S> cmp, Function<? super S, ? extends T> function) {
return cmp.thenComparing(Comparator.comparing(function).reversed());
return cmp.thenComparing(comparing(function).reversed());
}
@AfterTemplate
@@ -100,7 +104,7 @@ final class ComparatorTemplates {
@BeforeTemplate
Comparator<S> before(
Comparator<S> cmp, Function<? super S, ? extends T> function, Comparator<? super T> cmp2) {
return cmp.thenComparing(Comparator.comparing(function, cmp2));
return cmp.thenComparing(comparing(function, cmp2));
}
@AfterTemplate
@@ -115,7 +119,7 @@ final class ComparatorTemplates {
@BeforeTemplate
Comparator<S> before(
Comparator<S> cmp, Function<? super S, ? extends T> function, Comparator<? super T> cmp2) {
return cmp.thenComparing(Comparator.comparing(function, cmp2).reversed());
return cmp.thenComparing(comparing(function, cmp2).reversed());
}
@AfterTemplate
@@ -129,7 +133,7 @@ final class ComparatorTemplates {
static final class ThenComparingDouble<T> {
@BeforeTemplate
Comparator<T> before(Comparator<T> cmp, ToDoubleFunction<? super T> function) {
return cmp.thenComparing(Comparator.comparingDouble(function));
return cmp.thenComparing(comparingDouble(function));
}
@AfterTemplate
@@ -142,7 +146,7 @@ final class ComparatorTemplates {
static final class ThenComparingInt<T> {
@BeforeTemplate
Comparator<T> before(Comparator<T> cmp, ToIntFunction<? super T> function) {
return cmp.thenComparing(Comparator.comparingInt(function));
return cmp.thenComparing(comparingInt(function));
}
@AfterTemplate
@@ -155,7 +159,7 @@ final class ComparatorTemplates {
static final class ThenComparingLong<T> {
@BeforeTemplate
Comparator<T> before(Comparator<T> cmp, ToLongFunction<? super T> function) {
return cmp.thenComparing(Comparator.comparingLong(function));
return cmp.thenComparing(comparingLong(function));
}
@AfterTemplate
@@ -178,7 +182,7 @@ final class ComparatorTemplates {
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
Comparator<T> after(Comparator<T> cmp) {
return cmp.thenComparing(Comparator.naturalOrder());
return cmp.thenComparing(naturalOrder());
}
}

View File

@@ -233,10 +233,7 @@ final class DoubleStreamTemplates {
static final class DoubleStreamAllMatch {
@BeforeTemplate
boolean before(DoubleStream stream, DoublePredicate predicate) {
return Refaster.anyOf(
stream.noneMatch(predicate.negate()),
!stream.anyMatch(predicate.negate()),
stream.filter(predicate.negate()).findAny().isEmpty());
return stream.noneMatch(predicate.negate());
}
@AfterTemplate
@@ -251,10 +248,7 @@ final class DoubleStreamTemplates {
@BeforeTemplate
boolean before(DoubleStream stream) {
return Refaster.anyOf(
stream.noneMatch(e -> !test(e)),
!stream.anyMatch(e -> !test(e)),
stream.filter(e -> !test(e)).findAny().isEmpty());
return stream.noneMatch(e -> !test(e));
}
@AfterTemplate

View File

@@ -4,7 +4,6 @@ import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.AlsoNegation;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.NoAutoboxing;
import java.util.Objects;
import java.util.function.Predicate;
@@ -12,26 +11,9 @@ import java.util.function.Predicate;
final class EqualityTemplates {
private EqualityTemplates() {}
/** Prefer primitive/reference-based quality for primitives and enums. */
static final class PrimitiveOrReferenceEquality {
@NoAutoboxing
@BeforeTemplate
boolean before(boolean a, boolean b) {
return Objects.equals(a, b);
}
@NoAutoboxing
@BeforeTemplate
boolean before(long a, long b) {
return Objects.equals(a, b);
}
@NoAutoboxing
@BeforeTemplate
boolean before(double a, double b) {
return Objects.equals(a, b);
}
/** Prefer reference-based quality for enums. */
// Primitive value comparisons are not listed, because Error Prone flags those out of the box.
static final class PrimitiveOrReferenceEquality<T extends Enum<T>> {
/**
* Enums can be compared by reference. It is safe to do so even in the face of refactorings,
* because if the type is ever converted to a non-enum, then Error-Prone will complain about any
@@ -40,13 +22,13 @@ final class EqualityTemplates {
// 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.
@BeforeTemplate
<T extends Enum<T>> boolean before(T a, T b) {
boolean before(T a, T b) {
return Refaster.anyOf(a.equals(b), Objects.equals(a, b));
}
@AlsoNegation
@AfterTemplate
boolean after(boolean a, boolean b) {
@AlsoNegation
boolean after(T a, T b) {
return a == b;
}
}

View File

@@ -56,10 +56,7 @@ final class ImmutableListMultimapTemplates {
static final class EmptyImmutableListMultimap<K, V> {
@BeforeTemplate
ImmutableMultimap<K, V> before() {
return Refaster.anyOf(
ImmutableListMultimap.<K, V>builder().build(),
ImmutableMultimap.<K, V>builder().build(),
ImmutableMultimap.of());
return Refaster.anyOf(ImmutableListMultimap.<K, V>builder().build(), ImmutableMultimap.of());
}
@AfterTemplate
@@ -80,7 +77,6 @@ final class ImmutableListMultimapTemplates {
ImmutableMultimap<K, V> before(K key, V value) {
return Refaster.anyOf(
ImmutableListMultimap.<K, V>builder().put(key, value).build(),
ImmutableMultimap.<K, V>builder().put(key, value).build(),
ImmutableMultimap.of(key, value));
}
@@ -99,9 +95,8 @@ final class ImmutableListMultimapTemplates {
ImmutableMultimap<K, V> before(Map.Entry<? extends K, ? extends V> entry) {
return Refaster.anyOf(
ImmutableListMultimap.<K, V>builder().put(entry).build(),
Stream.of(entry).collect(toImmutableListMultimap(Map.Entry::getKey, Map.Entry::getValue)),
ImmutableMultimap.<K, V>builder().put(entry).build(),
ImmutableMultimap.of(entry.getKey(), entry.getValue()));
Stream.of(entry)
.collect(toImmutableListMultimap(Map.Entry::getKey, Map.Entry::getValue)));
}
@AfterTemplate
@@ -118,8 +113,7 @@ final class ImmutableListMultimapTemplates {
ImmutableListMultimap.copyOf(iterable.entries()),
ImmutableListMultimap.<K, V>builder().putAll(iterable).build(),
ImmutableMultimap.copyOf(iterable),
ImmutableMultimap.copyOf(iterable.entries()),
ImmutableMultimap.<K, V>builder().putAll(iterable).build());
ImmutableMultimap.copyOf(iterable.entries()));
}
@BeforeTemplate
@@ -129,7 +123,6 @@ final class ImmutableListMultimapTemplates {
ImmutableListMultimap.<K, V>builder().putAll(iterable).build(),
Streams.stream(iterable)
.collect(toImmutableListMultimap(Map.Entry::getKey, Map.Entry::getValue)),
ImmutableMultimap.<K, V>builder().putAll(iterable).build(),
ImmutableMultimap.copyOf(iterable));
}

View File

@@ -67,8 +67,8 @@ final class ImmutableListTemplates {
}
@AfterTemplate
ImmutableList<T> after(T secondName) {
return ImmutableList.of(secondName);
ImmutableList<T> after(T element) {
return ImmutableList.of(element);
}
}
@@ -118,7 +118,6 @@ final class ImmutableListTemplates {
ImmutableList<T> before(Stream<T> stream) {
return Refaster.anyOf(
ImmutableList.copyOf(stream.iterator()),
ImmutableList.copyOf(stream::iterator),
stream.collect(collectingAndThen(toList(), ImmutableList::copyOf)));
}
@@ -129,19 +128,6 @@ final class ImmutableListTemplates {
}
}
/** Don't call {@link ImmutableList#asList()}; it is a no-op. */
static final class ImmutableListAsList<T> {
@BeforeTemplate
ImmutableList<T> before(ImmutableList<T> list) {
return list.asList();
}
@AfterTemplate
ImmutableList<T> after(ImmutableList<T> list) {
return list;
}
}
/** Prefer {@link ImmutableList#sortedCopyOf(Iterable)} over more contrived alternatives. */
static final class ImmutableListSortedCopyOf<T extends Comparable<? super T>> {
@BeforeTemplate

View File

@@ -92,7 +92,6 @@ final class ImmutableMultisetTemplates {
ImmutableMultiset<T> before(Stream<T> stream) {
return Refaster.anyOf(
ImmutableMultiset.copyOf(stream.iterator()),
ImmutableMultiset.copyOf(stream::iterator),
stream.collect(collectingAndThen(toList(), ImmutableMultiset::copyOf)));
}

View File

@@ -162,7 +162,7 @@ final class ImmutableSetMultimapTemplates {
@AfterTemplate
ImmutableSetMultimap<K, V2> after(Multimap<K, V1> multimap) {
return ImmutableSetMultimap.copyOf(
Multimaps.transformValues(multimap, v -> valueTransformation(v)));
Multimaps.transformValues(multimap, e -> valueTransformation(e)));
}
}

View File

@@ -114,7 +114,6 @@ final class ImmutableSetTemplates {
ImmutableSet<T> before(Stream<T> stream) {
return Refaster.anyOf(
ImmutableSet.copyOf(stream.iterator()),
ImmutableSet.copyOf(stream::iterator),
stream.distinct().collect(toImmutableSet()),
stream.collect(collectingAndThen(toList(), ImmutableSet::copyOf)),
stream.collect(collectingAndThen(toSet(), ImmutableSet::copyOf)));

View File

@@ -136,7 +136,6 @@ final class ImmutableSortedMultisetTemplates {
ImmutableSortedMultiset<T> before(Stream<T> stream) {
return Refaster.anyOf(
ImmutableSortedMultiset.copyOf(stream.iterator()),
ImmutableSortedMultiset.copyOf(stream::iterator),
stream.collect(collectingAndThen(toList(), ImmutableSortedMultiset::copyOf)));
}

View File

@@ -136,7 +136,6 @@ final class ImmutableSortedSetTemplates {
ImmutableSortedSet<T> before(Stream<T> stream) {
return Refaster.anyOf(
ImmutableSortedSet.copyOf(stream.iterator()),
ImmutableSortedSet.copyOf(stream::iterator),
stream.collect(collectingAndThen(toList(), ImmutableSortedSet::copyOf)));
}

View File

@@ -246,10 +246,7 @@ final class IntStreamTemplates {
static final class IntStreamAllMatch {
@BeforeTemplate
boolean before(IntStream stream, IntPredicate predicate) {
return Refaster.anyOf(
stream.noneMatch(predicate.negate()),
!stream.anyMatch(predicate.negate()),
stream.filter(predicate.negate()).findAny().isEmpty());
return stream.noneMatch(predicate.negate());
}
@AfterTemplate
@@ -264,10 +261,7 @@ final class IntStreamTemplates {
@BeforeTemplate
boolean before(IntStream stream) {
return Refaster.anyOf(
stream.noneMatch(e -> !test(e)),
!stream.anyMatch(e -> !test(e)),
stream.filter(e -> !test(e)).findAny().isEmpty());
return stream.noneMatch(e -> !test(e));
}
@AfterTemplate

View File

@@ -246,10 +246,7 @@ final class LongStreamTemplates {
static final class LongStreamAllMatch {
@BeforeTemplate
boolean before(LongStream stream, LongPredicate predicate) {
return Refaster.anyOf(
stream.noneMatch(predicate.negate()),
!stream.anyMatch(predicate.negate()),
stream.filter(predicate.negate()).findAny().isEmpty());
return stream.noneMatch(predicate.negate());
}
@AfterTemplate
@@ -264,10 +261,7 @@ final class LongStreamTemplates {
@BeforeTemplate
boolean before(LongStream stream) {
return Refaster.anyOf(
stream.noneMatch(e -> !test(e)),
!stream.anyMatch(e -> !test(e)),
stream.filter(e -> !test(e)).findAny().isEmpty());
return stream.noneMatch(e -> !test(e));
}
@AfterTemplate

View File

@@ -1,5 +1,10 @@
package tech.picnic.errorprone.refastertemplates;
import static java.util.Comparator.comparing;
import static java.util.Comparator.naturalOrder;
import static java.util.Map.Entry.comparingByKey;
import static java.util.Map.Entry.comparingByValue;
import com.google.common.collect.Maps;
import com.google.errorprone.refaster.ImportPolicy;
import com.google.errorprone.refaster.Refaster;
@@ -42,15 +47,13 @@ final class MapEntryTemplates {
static final class MapEntryComparingByKey<K extends Comparable<? super K>, V> {
@BeforeTemplate
Comparator<Map.Entry<K, V>> before() {
return Refaster.anyOf(
Comparator.comparing(Map.Entry::getKey),
Map.Entry.comparingByKey(Comparator.naturalOrder()));
return Refaster.anyOf(comparing(Map.Entry::getKey), comparingByKey(naturalOrder()));
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
Comparator<Map.Entry<K, V>> after() {
return Map.Entry.comparingByKey();
return comparingByKey();
}
}
@@ -58,13 +61,13 @@ final class MapEntryTemplates {
static final class MapEntryComparingByKeyWithCustomComparator<K, V> {
@BeforeTemplate
Comparator<Map.Entry<K, V>> before(Comparator<? super K> cmp) {
return Comparator.comparing(Map.Entry::getKey, cmp);
return comparing(Map.Entry::getKey, cmp);
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
Comparator<Map.Entry<K, V>> after(Comparator<? super K> cmp) {
return Map.Entry.comparingByKey(cmp);
return comparingByKey(cmp);
}
}
@@ -73,15 +76,13 @@ final class MapEntryTemplates {
static final class MapEntryComparingByValue<K, V extends Comparable<? super V>> {
@BeforeTemplate
Comparator<Map.Entry<K, V>> before() {
return Refaster.anyOf(
Comparator.comparing(Map.Entry::getValue),
Map.Entry.comparingByValue(Comparator.naturalOrder()));
return Refaster.anyOf(comparing(Map.Entry::getValue), comparingByValue(naturalOrder()));
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
Comparator<Map.Entry<K, V>> after() {
return Map.Entry.comparingByValue();
return comparingByValue();
}
}
@@ -89,13 +90,13 @@ final class MapEntryTemplates {
static final class MapEntryComparingByValueWithCustomComparator<K, V> {
@BeforeTemplate
Comparator<Map.Entry<K, V>> before(Comparator<? super V> cmp) {
return Comparator.comparing(Map.Entry::getValue, cmp);
return comparing(Map.Entry::getValue, cmp);
}
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
Comparator<Map.Entry<K, V>> after(Comparator<? super V> cmp) {
return Map.Entry.comparingByValue(cmp);
return comparingByValue(cmp);
}
}
}

View File

@@ -1,5 +1,7 @@
package tech.picnic.errorprone.refastertemplates;
import static java.util.Objects.requireNonNullElse;
import com.google.common.base.MoreObjects;
import com.google.errorprone.refaster.ImportPolicy;
import com.google.errorprone.refaster.annotation.AfterTemplate;
@@ -24,7 +26,7 @@ final class NullTemplates {
@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
T after(T first, T second) {
return Objects.requireNonNullElse(first, second);
return requireNonNullElse(first, second);
}
}

View File

@@ -5,7 +5,9 @@ import com.google.errorprone.refaster.ImportPolicy;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import com.google.errorprone.refaster.annotation.Matches;
import com.google.errorprone.refaster.annotation.MayOptionallyUse;
import com.google.errorprone.refaster.annotation.NotMatches;
import com.google.errorprone.refaster.annotation.Placeholder;
import com.google.errorprone.refaster.annotation.UseImportPolicy;
import java.util.Iterator;
@@ -14,17 +16,20 @@ import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import tech.picnic.errorprone.refaster.util.IsNonNull;
import tech.picnic.errorprone.refaster.util.IsNullable;
/** Refaster templates related to expressions dealing with {@link Optional}s. */
final class OptionalTemplates {
private OptionalTemplates() {}
static final class OptionalOfNullable<T> {
@BeforeTemplate
// XXX: Refaster should be smart enough to also rewrite occurrences in which there are
// parentheses around the null check, but that's currently not the case. Try to fix that.
// XXX: This is a special case of `TernaryOperatorOptionalNegativeFiltering`.
Optional<T> before(T object) {
@BeforeTemplate
@SuppressWarnings("TernaryOperatorOptionalNegativeFiltering" /* Special case. */)
Optional<T> before(@Nullable T object) {
return object == null ? Optional.empty() : Optional.of(object);
}
@@ -91,19 +96,6 @@ final class OptionalTemplates {
}
}
/** Prefer {@link Optional#stream()} over the Guava alternative. */
static final class OptionalToStream<T> {
@BeforeTemplate
Stream<T> before(Optional<T> optional) {
return Streams.stream(optional);
}
@AfterTemplate
Stream<T> after(Optional<T> optional) {
return optional.stream();
}
}
/**
* Don't use the ternary operator to extract the first element of a possibly-empty {@link
* Iterator} as an {@link Optional}.
@@ -121,7 +113,10 @@ final class OptionalTemplates {
}
}
/** Prefer {@link Optional#filter(Predicate)} over usage of the ternary operator. */
/**
* Prefer {@link Optional#filter(Predicate)} over usage of the ternary operator while matching
* non-null expressions.
*/
// 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`?
@@ -130,34 +125,79 @@ final class OptionalTemplates {
abstract boolean test(T value);
@BeforeTemplate
Optional<T> before(T input) {
Optional<T> before(@NotMatches(IsNullable.class) T input) {
return test(input) ? Optional.of(input) : Optional.empty();
}
@AfterTemplate
Optional<T> after(T input) {
return Refaster.emitCommentBefore(
"Or Optional.ofNullable (can't auto-infer).", Optional.of(input).filter(v -> test(v)));
return Optional.of(input).filter(v -> test(v));
}
}
/** Prefer {@link Optional#filter(Predicate)} over usage of the ternary operator. */
/**
* Prefer {@link Optional#filter(Predicate)} over usage of the ternary operator and only match
* nullable expressions.
*/
// 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`?
abstract static class OptionalOfNullableFilterPositive<T> {
@Placeholder
abstract boolean test(T value);
@BeforeTemplate
Optional<T> before(@Matches(IsNullable.class) T input) {
return test(input) ? Optional.of(input) : Optional.empty();
}
@AfterTemplate
Optional<T> after(T input) {
return Optional.ofNullable(input).filter(v -> test(v));
}
}
/**
* Prefer {@link Optional#filter(Predicate)} over usage of the ternary operator and only matching
* non-null expressions.
*/
// // 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`?
abstract static class TernaryOperatorOptionalNegativeFiltering<T> {
@Placeholder
abstract boolean test(T value);
@BeforeTemplate
Optional<T> before(T input) {
Optional<T> before(@Matches(IsNonNull.class) T input) {
return test(input) ? Optional.empty() : Optional.of(input);
}
@AfterTemplate
Optional<T> after(T input) {
return Refaster.emitCommentBefore(
"Or Optional.ofNullable (can't auto-infer).", Optional.of(input).filter(v -> !test(v)));
return Optional.of(input).filter(v -> !test(v));
}
}
/**
* Prefer {@link Optional#filter(Predicate)} over usage of the ternary operator and only matching
* nullable expressions.
*/
// 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`?
abstract static class OptionalOfNullableFilterNegative<T> {
@Placeholder
abstract boolean test(T value);
@BeforeTemplate
Optional<T> before(@Matches(IsNullable.class) T input) {
return test(input) ? Optional.empty() : Optional.of(input);
}
@AfterTemplate
Optional<T> after(T input) {
return Optional.ofNullable(input).filter(v -> !test(v));
}
}

View File

@@ -77,7 +77,7 @@ final class ReactorTemplates {
static final class MonoErrorSupplier<T, E extends Throwable> {
@BeforeTemplate
Mono<T> before(Supplier<E> supplier) {
return Refaster.anyOf(Mono.error(supplier::get), Mono.error(() -> supplier.get()));
return Mono.error(() -> supplier.get());
}
@AfterTemplate
@@ -94,7 +94,7 @@ final class ReactorTemplates {
static final class FluxErrorSupplier<T, E extends Throwable> {
@BeforeTemplate
Flux<T> before(Supplier<E> supplier) {
return Refaster.anyOf(Flux.error(supplier::get), Flux.error(() -> supplier.get()));
return Flux.error(() -> supplier.get());
}
@AfterTemplate
@@ -142,6 +142,35 @@ final class ReactorTemplates {
}
}
/** Prefer {@link Flux#concatMap(Function)} over more contrived alternatives. */
static final class FluxConcatMap<T, S> {
@BeforeTemplate
Flux<S> before(Flux<T> flux, Function<? super T, ? extends Publisher<? extends S>> function) {
return Refaster.anyOf(flux.flatMap(function, 1), flux.flatMapSequential(function, 1));
}
@AfterTemplate
Flux<S> after(Flux<T> flux, Function<? super T, ? extends Publisher<? extends S>> function) {
return flux.concatMap(function);
}
}
/**
* Prefer {@link Flux#concatMapIterable(Function)} over {@link Flux#concatMapIterable(Function)},
* as the former has equivalent semantics but a clearer name.
*/
static final class FluxConcatMapIterable<T, S> {
@BeforeTemplate
Flux<S> before(Flux<T> flux, Function<? super T, ? extends Iterable<? extends S>> function) {
return flux.flatMapIterable(function);
}
@AfterTemplate
Flux<S> after(Flux<T> flux, Function<? super T, ? extends Iterable<? extends S>> function) {
return flux.concatMapIterable(function);
}
}
/**
* Don't use {@link Mono#flatMapMany(Function)} to implicitly convert a {@link Mono} to a {@link
* Flux}.
@@ -161,6 +190,32 @@ final class ReactorTemplates {
}
}
/** Prefer {@link Mono#flux()}} over more contrived alternatives. */
static final class MonoFlux<T> {
@BeforeTemplate
Flux<T> before(Mono<T> mono) {
return Flux.concat(mono);
}
@AfterTemplate
Flux<T> after(Mono<T> mono) {
return mono.flux();
}
}
/** Don't unnecessarily invoke {@link Flux#concat(Publisher)}. */
static final class FluxIdentity<T> {
@BeforeTemplate
Flux<T> before(Flux<T> flux) {
return Flux.concat(flux);
}
@AfterTemplate
Flux<T> after(Flux<T> flux) {
return flux;
}
}
/**
* Prefer a collection using {@link MoreCollectors#toOptional()} over more contrived alternatives.
*/
@@ -194,6 +249,32 @@ final class ReactorTemplates {
}
}
/** Prefer {@link Mono#as(Function)} when creating a {@link StepVerifier}. */
static final class StepVerifierFromMono<T> {
@BeforeTemplate
StepVerifier.FirstStep<? extends T> before(Mono<T> mono) {
return StepVerifier.create(mono);
}
@AfterTemplate
StepVerifier.FirstStep<? extends T> after(Mono<T> mono) {
return mono.as(StepVerifier::create);
}
}
/** Prefer {@link Flux#as(Function)} when creating a {@link StepVerifier}. */
static final class StepVerifierFromFlux<T> {
@BeforeTemplate
StepVerifier.FirstStep<? extends T> before(Flux<T> flux) {
return StepVerifier.create(flux);
}
@AfterTemplate
StepVerifier.FirstStep<? extends T> after(Flux<T> flux) {
return flux.as(StepVerifier::create);
}
}
/** Don't unnecessarily call {@link StepVerifier.Step#expectNext(Object[])}. */
static final class StepVerifierStepExpectNextEmpty<T> {
@BeforeTemplate

View File

@@ -148,6 +148,8 @@ final class StreamTemplates {
*/
// XXX: Consider whether to have a similar rule for `.findAny()`. For parallel streams it
// wouldn't be quite the same....
// XXX: This change is not equivalent for `null`-returning functions, as the original code throws
// an NPE if the first element is `null`, while the latter yields an empty `Optional`.
static final class StreamMapFirst<T, S> {
@BeforeTemplate
Optional<S> before(Stream<T> stream, Function<? super T, S> function) {
@@ -294,10 +296,7 @@ final class StreamTemplates {
static final class StreamAllMatch<T> {
@BeforeTemplate
boolean before(Stream<T> stream, Predicate<? super T> predicate) {
return Refaster.anyOf(
stream.noneMatch(Refaster.anyOf(not(predicate), predicate.negate())),
!stream.anyMatch(Refaster.anyOf(not(predicate), predicate.negate())),
stream.filter(Refaster.anyOf(not(predicate), predicate.negate())).findAny().isEmpty());
return stream.noneMatch(Refaster.anyOf(not(predicate), predicate.negate()));
}
@AfterTemplate
@@ -312,10 +311,7 @@ final class StreamTemplates {
@BeforeTemplate
boolean before(Stream<T> stream) {
return Refaster.anyOf(
stream.noneMatch(e -> !test(e)),
!stream.anyMatch(e -> !test(e)),
stream.filter(e -> !test(e)).findAny().isEmpty());
return stream.noneMatch(e -> !test(e));
}
@AfterTemplate

View File

@@ -1,5 +1,6 @@
package tech.picnic.errorprone.refastertemplates;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.joining;
import com.google.common.base.Joiner;
@@ -10,10 +11,10 @@ import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.AlsoNegation;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Optional;
import javax.annotation.Nullable;
/** Refaster templates related to expressions dealing with {@link String}s. */
// XXX: Should we prefer `s -> !s.isEmpty()` or `not(String::isEmpty)`?
@@ -37,7 +38,7 @@ final class StringTemplates {
/** Prefer {@link Strings#isNullOrEmpty(String)} over the more verbose alternative. */
static final class StringIsNullOrEmpty {
@BeforeTemplate
boolean before(String str) {
boolean before(@Nullable String str) {
return str == null || str.isEmpty();
}
@@ -122,7 +123,7 @@ final class StringTemplates {
static final class Utf8EncodedLength {
@BeforeTemplate
int before(String str) {
return str.getBytes(StandardCharsets.UTF_8).length;
return str.getBytes(UTF_8).length;
}
@AfterTemplate

View File

@@ -16,6 +16,7 @@ import static org.testng.Assert.assertThrows;
import static org.testng.Assert.assertTrue;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.DoNotCall;
import com.google.errorprone.refaster.ImportPolicy;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
@@ -81,6 +82,7 @@ final class TestNGToAssertJTemplates {
}
@AfterTemplate
@DoNotCall
void after() {
throw new AssertionError();
}

View File

@@ -43,10 +43,10 @@ final class TimeTemplates {
static final class UtcConstant {
@BeforeTemplate
ZoneId before() {
// `ZoneId.of("Z")` is not listed, because Error Prone flags it out of the box.
return Refaster.anyOf(
ZoneId.of("GMT"),
ZoneId.of("UTC"),
ZoneId.of("Z"),
ZoneId.of("+0"),
ZoneId.of("-0"),
ZoneOffset.UTC.normalized(),
@@ -62,11 +62,13 @@ final class TimeTemplates {
/** Use {@link Clock#systemUTC()} when possible. */
static final class UtcClock {
@BeforeTemplate
@SuppressWarnings("TimeZoneUsage")
Clock before() {
return Clock.system(ZoneOffset.UTC);
}
@AfterTemplate
@SuppressWarnings("TimeZoneUsage")
Clock after() {
return Clock.systemUTC();
}
@@ -96,8 +98,8 @@ final class TimeTemplates {
return a.compareTo(b) < 0;
}
@AlsoNegation
@AfterTemplate
@AlsoNegation
boolean after(Instant a, Instant b) {
return a.isBefore(b);
}
@@ -113,8 +115,8 @@ final class TimeTemplates {
return a.compareTo(b) > 0;
}
@AlsoNegation
@AfterTemplate
@AlsoNegation
boolean after(Instant a, Instant b) {
return a.isAfter(b);
}
@@ -162,8 +164,8 @@ final class TimeTemplates {
return a.compareTo(b) < 0;
}
@AlsoNegation
@AfterTemplate
@AlsoNegation
boolean after(ChronoLocalDate a, ChronoLocalDate b) {
return a.isBefore(b);
}
@@ -179,8 +181,8 @@ final class TimeTemplates {
return a.compareTo(b) > 0;
}
@AlsoNegation
@AfterTemplate
@AlsoNegation
boolean after(ChronoLocalDate a, ChronoLocalDate b) {
return a.isAfter(b);
}
@@ -196,8 +198,8 @@ final class TimeTemplates {
return a.compareTo(b) < 0;
}
@AlsoNegation
@AfterTemplate
@AlsoNegation
boolean after(ChronoLocalDateTime<?> a, ChronoLocalDateTime<?> b) {
return a.isBefore(b);
}
@@ -213,8 +215,8 @@ final class TimeTemplates {
return a.compareTo(b) > 0;
}
@AlsoNegation
@AfterTemplate
@AlsoNegation
boolean after(ChronoLocalDateTime<?> a, ChronoLocalDateTime<?> b) {
return a.isAfter(b);
}
@@ -230,8 +232,8 @@ final class TimeTemplates {
return a.compareTo(b) < 0;
}
@AlsoNegation
@AfterTemplate
@AlsoNegation
boolean after(ChronoZonedDateTime<?> a, ChronoZonedDateTime<?> b) {
return a.isBefore(b);
}
@@ -247,8 +249,8 @@ final class TimeTemplates {
return a.compareTo(b) > 0;
}
@AlsoNegation
@AfterTemplate
@AlsoNegation
boolean after(ChronoZonedDateTime<?> a, ChronoZonedDateTime<?> b) {
return a.isAfter(b);
}
@@ -264,8 +266,8 @@ final class TimeTemplates {
return a.compareTo(b) < 0;
}
@AlsoNegation
@AfterTemplate
@AlsoNegation
boolean after(OffsetDateTime a, OffsetDateTime b) {
return a.isBefore(b);
}
@@ -281,8 +283,8 @@ final class TimeTemplates {
return a.compareTo(b) > 0;
}
@AlsoNegation
@AfterTemplate
@AlsoNegation
boolean after(OffsetDateTime a, OffsetDateTime b) {
return a.isAfter(b);
}

View File

@@ -0,0 +1,36 @@
package tech.picnic.errorprone.refastertemplates;
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec;
import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;
/**
* Refaster templates related to expressions dealing with {@link
* org.springframework.web.reactive.function.client.WebClient} and related types.
*/
final class WebClientTemplates {
private WebClientTemplates() {}
/** Prefer {@link RequestBodySpec#bodyValue(Object)} over more contrived alternatives. */
static final class BodyValue<T> {
@BeforeTemplate
RequestHeadersSpec<?> before(RequestBodySpec requestBodySpec, T value) {
return requestBodySpec.body(fromValue(value));
}
@BeforeTemplate
WebTestClient.RequestHeadersSpec<?> before2(
WebTestClient.RequestBodySpec requestBodySpec, T value) {
return requestBodySpec.body(fromValue(value));
}
@AfterTemplate
RequestHeadersSpec<?> after(RequestBodySpec requestBodySpec, T value) {
return requestBodySpec.bodyValue(value);
}
}
}

View File

@@ -1,7 +1,4 @@
/** Picnic Refaster templates. */
@CheckReturnValue
@ParametersAreNonnullByDefault
@com.google.errorprone.annotations.CheckReturnValue
@javax.annotation.ParametersAreNonnullByDefault
package tech.picnic.errorprone.refastertemplates;
import com.google.errorprone.annotations.CheckReturnValue;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -0,0 +1,149 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.common.base.Predicates.containsPattern;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class AmbiguousJsonCreatorCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(AmbiguousJsonCreatorCheck.class, getClass())
.expectErrorMessage(
"X",
containsPattern("`JsonCreator.Mode` should be set for single-argument creators"));
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(AmbiguousJsonCreatorCheck.class, getClass());
@Test
void identification() {
compilationTestHelper
.addSourceLines(
"Container.java",
"import com.fasterxml.jackson.annotation.JsonCreator;",
"import com.fasterxml.jackson.annotation.JsonValue;",
"",
"interface Container {",
" enum A {",
" FOO(1);",
"",
" private final int i;",
"",
" A(int i) {",
" this.i = i;",
" }",
"",
" // BUG: Diagnostic matches: X",
" @JsonCreator",
" public static A of(int i) {",
" return FOO;",
" }",
" }",
"",
" enum B {",
" FOO(1);",
"",
" private final int i;",
"",
" B(int i) {",
" this.i = i;",
" }",
"",
" @JsonCreator(mode = JsonCreator.Mode.DELEGATING)",
" public static B of(int i) {",
" return FOO;",
" }",
" }",
"",
" enum C {",
" FOO(1, \"s\");",
"",
" @JsonValue private final int i;",
" private final String s;",
"",
" C(int i, String s) {",
" this.i = i;",
" this.s = s;",
" }",
"",
" // BUG: Diagnostic matches: X",
" @JsonCreator",
" public static C of(int i) {",
" return FOO;",
" }",
" }",
"",
" enum D {",
" FOO(1, \"s\");",
"",
" private final int i;",
" private final String s;",
"",
" D(int i, String s) {",
" this.i = i;",
" this.s = s;",
" }",
"",
" @JsonCreator",
" public static D of(int i, String s) {",
" return FOO;",
" }",
" }",
"",
" enum E {",
" FOO;",
"",
" // BUG: Diagnostic matches: X",
" @JsonCreator",
" public static E of(String s) {",
" return FOO;",
" }",
" }",
"",
" class F {",
" private final String s;",
"",
" F(String s) {",
" this.s = s;",
" }",
"",
" @JsonCreator",
" public static F of(String s) {",
" return new F(s);",
" }",
" }",
"",
"}")
.doTest();
}
@Test
void replacement() {
refactoringTestHelper
.addInputLines(
"in/A.java",
"import com.fasterxml.jackson.annotation.JsonCreator;",
"",
"enum A {",
" FOO;",
"",
" @JsonCreator",
" public static A of(String s) {",
" return FOO;",
" }",
"}")
.addOutputLines(
"out/A.java",
"import com.fasterxml.jackson.annotation.JsonCreator;",
"",
"enum A {",
" FOO;",
"",
" @JsonCreator(mode = JsonCreator.Mode.DELEGATING)",
" public static A of(String s) {",
" return FOO;",
" }",
"}")
.doTest(BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH);
}
}

View File

@@ -8,14 +8,14 @@ import org.junit.jupiter.api.Test;
public final class AnnotationAttributeMatcherTest {
@Test
public void testWithoutListings() {
void withoutListings() {
AnnotationAttributeMatcher matcher =
AnnotationAttributeMatcher.create(Optional.empty(), ImmutableList.of());
assertThat(matcher.matches("foo", "bar")).isTrue();
}
@Test
public void testWithSingleFullAnnotationWhitelist() {
void withSingleFullAnnotationWhitelist() {
AnnotationAttributeMatcher matcher =
AnnotationAttributeMatcher.create(Optional.of(ImmutableList.of("foo")), ImmutableList.of());
assertThat(matcher.matches("foo", "bar")).isTrue();
@@ -24,7 +24,7 @@ public final class AnnotationAttributeMatcherTest {
}
@Test
public void testWithSingleAnnotationAttributeWhitelist() {
void withSingleAnnotationAttributeWhitelist() {
AnnotationAttributeMatcher matcher =
AnnotationAttributeMatcher.create(
Optional.of(ImmutableList.of("foo#bar")), ImmutableList.of());
@@ -34,7 +34,7 @@ public final class AnnotationAttributeMatcherTest {
}
@Test
public void testWithSingleFullAnnotationBlacklist() {
void withSingleFullAnnotationBlacklist() {
AnnotationAttributeMatcher matcher =
AnnotationAttributeMatcher.create(Optional.empty(), ImmutableList.of("foo"));
assertThat(matcher.matches("foo", "bar")).isFalse();
@@ -43,7 +43,7 @@ public final class AnnotationAttributeMatcherTest {
}
@Test
public void testWithSingleAnnotationAttributeBlacklist() {
void withSingleAnnotationAttributeBlacklist() {
AnnotationAttributeMatcher matcher =
AnnotationAttributeMatcher.create(Optional.empty(), ImmutableList.of("foo#bar"));
assertThat(matcher.matches("foo", "bar")).isFalse();
@@ -52,7 +52,7 @@ public final class AnnotationAttributeMatcherTest {
}
@Test
public void testWithComplicatedConfiguration() {
void withComplicatedConfiguration() {
AnnotationAttributeMatcher matcher =
AnnotationAttributeMatcher.create(
Optional.of(ImmutableList.of("foo", "bar", "baz", "baz#1", "baz#2", "quux#1")),

View File

@@ -9,10 +9,10 @@ public final class AutowiredConstructorCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(AutowiredConstructorCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(new AutowiredConstructorCheck(), getClass());
BugCheckerRefactoringTestHelper.newInstance(AutowiredConstructorCheck.class, getClass());
@Test
public void testIdentification() {
void identification() {
compilationTestHelper
.addSourceLines(
"Container.java",
@@ -62,7 +62,7 @@ public final class AutowiredConstructorCheckTest {
}
@Test
public void testReplacement() {
void replacement() {
refactoringTestHelper
.addInputLines(
"in/Container.java",

View File

@@ -9,10 +9,10 @@ public final class CanonicalAnnotationSyntaxCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(CanonicalAnnotationSyntaxCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(new CanonicalAnnotationSyntaxCheck(), getClass());
BugCheckerRefactoringTestHelper.newInstance(CanonicalAnnotationSyntaxCheck.class, getClass());
@Test
public void testIdentification() {
void identification() {
compilationTestHelper
.addSourceLines(
"pkg/A.java",
@@ -85,7 +85,7 @@ public final class CanonicalAnnotationSyntaxCheckTest {
}
@Test
public void testReplacement() {
void replacement() {
refactoringTestHelper
.addInputLines(
"in/pkg/A.java",

View File

@@ -9,10 +9,10 @@ public final class EmptyMethodCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(EmptyMethodCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(new EmptyMethodCheck(), getClass());
BugCheckerRefactoringTestHelper.newInstance(EmptyMethodCheck.class, getClass());
@Test
public void testIdentification() {
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -33,6 +33,10 @@ public final class EmptyMethodCheckTest {
" interface F {",
" void fun();",
" }",
"",
" final class MyTestClass {",
" void helperMethod() {}",
" }",
"}")
.addSourceLines(
"B.java",
@@ -64,7 +68,7 @@ public final class EmptyMethodCheckTest {
}
@Test
public void testReplacement() {
void replacement() {
refactoringTestHelper
.addInputLines(
"in/A.java",

View File

@@ -0,0 +1,85 @@
package tech.picnic.errorprone.bugpatterns;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class ExplicitEnumOrderingCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(ExplicitEnumOrderingCheck.class, getClass());
@Test
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.time.chrono.IsoEra.BCE;",
"import static java.time.chrono.IsoEra.CE;",
"",
"import com.google.common.collect.Ordering;",
"import com.google.common.collect.ImmutableList;",
"import java.lang.annotation.RetentionPolicy;",
"import java.time.chrono.IsoEra;",
"",
"class A {",
" {",
" // The `List`-accepting overload is currently ignored.",
" Ordering.explicit(ImmutableList.of(RetentionPolicy.SOURCE, RetentionPolicy.CLASS));",
"",
" Ordering.explicit(IsoEra.BCE, IsoEra.CE);",
" // BUG: Diagnostic contains: IsoEra.CE",
" Ordering.explicit(IsoEra.BCE);",
" // BUG: Diagnostic contains: IsoEra.BCE",
" Ordering.explicit(IsoEra.CE);",
"",
" Ordering.explicit(RetentionPolicy.SOURCE, RetentionPolicy.CLASS, RetentionPolicy.RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.CLASS, RetentionPolicy.RUNTIME",
" Ordering.explicit(RetentionPolicy.SOURCE);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE, RetentionPolicy.RUNTIME",
" Ordering.explicit(RetentionPolicy.CLASS);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE, RetentionPolicy.CLASS",
" Ordering.explicit(RetentionPolicy.RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.RUNTIME",
" Ordering.explicit(RetentionPolicy.SOURCE, RetentionPolicy.CLASS);",
" // BUG: Diagnostic contains: RetentionPolicy.CLASS",
" Ordering.explicit(RetentionPolicy.SOURCE, RetentionPolicy.RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE",
" Ordering.explicit(RetentionPolicy.CLASS, RetentionPolicy.RUNTIME);",
"",
" Ordering.explicit(BCE, CE);",
" // BUG: Diagnostic contains: IsoEra.CE",
" Ordering.explicit(BCE);",
" // BUG: Diagnostic contains: IsoEra.BCE",
" Ordering.explicit(CE);",
"",
" Ordering.explicit(SOURCE, CLASS, RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.CLASS, RetentionPolicy.RUNTIME",
" Ordering.explicit(SOURCE);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE, RetentionPolicy.RUNTIME",
" Ordering.explicit(CLASS);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE, RetentionPolicy.CLASS",
" Ordering.explicit(RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.RUNTIME",
" Ordering.explicit(SOURCE, CLASS);",
" // BUG: Diagnostic contains: RetentionPolicy.CLASS",
" Ordering.explicit(SOURCE, RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE",
" Ordering.explicit(CLASS, RUNTIME);",
"",
" Ordering.explicit(RetentionPolicy.SOURCE, BCE, RetentionPolicy.CLASS, CE, RUNTIME);",
" Ordering.explicit(SOURCE, IsoEra.BCE, CLASS, IsoEra.CE, RetentionPolicy.RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.CLASS",
" Ordering.explicit(RetentionPolicy.SOURCE, BCE, CE, RUNTIME);",
" // BUG: Diagnostic contains: RetentionPolicy.CLASS",
" Ordering.explicit(IsoEra.BCE, SOURCE, IsoEra.CE, RetentionPolicy.RUNTIME);",
" // BUG: Diagnostic contains: IsoEra.CE, RetentionPolicy.RUNTIME",
" Ordering.explicit(IsoEra.BCE, SOURCE, RetentionPolicy.CLASS);",
" // BUG: Diagnostic contains: RetentionPolicy.SOURCE, IsoEra.BCE",
" Ordering.explicit(CLASS, RUNTIME, CE);",
" }",
"}")
.doTest();
}
}

View File

@@ -0,0 +1,122 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugCheckerRefactoringTestHelper.newInstance;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class FluxFlatMapUsageCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(FluxFlatMapUsageCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
newInstance(FluxFlatMapUsageCheck.class, getClass());
@Test
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.function.BiFunction;",
"import java.util.function.Function;",
"import reactor.core.publisher.Mono;",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" // BUG: Diagnostic contains:",
" Flux.just(1).flatMap(Flux::just);",
" // BUG: Diagnostic contains:",
" Flux.just(1).<String>flatMap(i -> Flux.just(String.valueOf(i)));",
" // BUG: Diagnostic contains:",
" Flux.just(1).flatMapSequential(Flux::just);",
" // BUG: Diagnostic contains:",
" Flux.just(1).<String>flatMapSequential(i -> Flux.just(String.valueOf(i)));",
"",
" Mono.just(1).flatMap(Mono::just);",
" Flux.just(1).concatMap(Flux::just);",
"",
" Flux.just(1).flatMap(Flux::just, 1);",
" Flux.just(1).flatMap(Flux::just, 1, 1);",
" Flux.just(1).flatMap(Flux::just, throwable -> Flux.empty(), Flux::empty);",
"",
" Flux.just(1).flatMapSequential(Flux::just, 1);",
" Flux.just(1).flatMapSequential(Flux::just, 1, 1);",
"",
" // BUG: Diagnostic contains:",
" this.<String, Flux<String>>sink(Flux::flatMap);",
" // BUG: Diagnostic contains:",
" this.<Integer, Flux<Integer>>sink(Flux::<Integer>flatMap);",
"",
" // BUG: Diagnostic contains:",
" this.<String, Flux<String>>sink(Flux::flatMapSequential);",
" // BUG: Diagnostic contains:",
" this.<Integer, Flux<Integer>>sink(Flux::<Integer>flatMapSequential);",
"",
" this.<String, Mono<String>>sink(Mono::flatMap);",
" }",
"",
" private <T, P> void sink(BiFunction<P, Function<T, P>, P> fun) {}",
"}")
.doTest();
}
@Test
void replacementFirstSuggestedFix() {
refactoringTestHelper
.setFixChooser(FixChoosers.FIRST)
.addInputLines(
"in/A.java",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" Flux.just(1).flatMap(Flux::just);",
" Flux.just(1).flatMapSequential(Flux::just);",
" }",
"}")
.addOutputLines(
"out/A.java",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" void m() {",
" Flux.just(1).concatMap(Flux::just);",
" Flux.just(1).concatMap(Flux::just);",
" }",
"}")
.doTest();
}
@Test
void replacementSecondSuggestedFix() {
refactoringTestHelper
.setFixChooser(FixChoosers.SECOND)
.addInputLines(
"in/A.java",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" private static final int MAX_CONCURRENCY = 8;",
"",
" void m() {",
" Flux.just(1).flatMap(Flux::just);",
" Flux.just(1).flatMapSequential(Flux::just);",
" }",
"}")
.addOutputLines(
"out/A.java",
"import reactor.core.publisher.Flux;",
"",
"class A {",
" private static final int MAX_CONCURRENCY = 8;",
"",
" void m() {",
" Flux.just(1).flatMap(Flux::just, MAX_CONCURRENCY);",
" Flux.just(1).flatMapSequential(Flux::just, MAX_CONCURRENCY);",
" }",
"}")
.doTest();
}
}

View File

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

View File

@@ -9,10 +9,10 @@ public final class JUnitMethodDeclarationCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(JUnitMethodDeclarationCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(new JUnitMethodDeclarationCheck(), getClass());
BugCheckerRefactoringTestHelper.newInstance(JUnitMethodDeclarationCheck.class, getClass());
@Test
public void testIdentification() {
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -91,7 +91,7 @@ public final class JUnitMethodDeclarationCheckTest {
"import org.junit.jupiter.api.Test;",
"import org.junit.jupiter.params.ParameterizedTest;",
"",
"final class B extends A {",
"class B extends A {",
" @Override @BeforeAll void setUp1() {}",
" @Override @BeforeAll public void setUp2() {}",
" @Override @BeforeAll protected void setUp3() {}",
@@ -127,7 +127,7 @@ public final class JUnitMethodDeclarationCheckTest {
}
@Test
public void testReplacement() {
void replacement() {
refactoringTestHelper
.addInputLines(
"in/A.java",

View File

@@ -19,10 +19,10 @@ public final class LexicographicalAnnotationAttributeListingCheckTest {
"-XepOpt:LexicographicalAnnotationAttributeListing:Excludes=pkg.A.Bar#value"));
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(
new LexicographicalAnnotationAttributeListingCheck(), getClass());
LexicographicalAnnotationAttributeListingCheck.class, getClass());
@Test
public void testIdentification() {
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -105,7 +105,7 @@ public final class LexicographicalAnnotationAttributeListingCheckTest {
// introduced. Avoiding that might make the code too complex. Instead, users can have the
// `CanonicalAnnotationSyntaxCheck` correct the situation in a subsequent run.
@Test
public void testReplacement() {
void replacement() {
refactoringTestHelper
.addInputLines(
"in/A.java",
@@ -161,7 +161,7 @@ public final class LexicographicalAnnotationAttributeListingCheckTest {
}
@Test
public void testFiltering() {
void filtering() {
/* Some violations are not flagged because they are not in- or excluded. */
restrictedCompilationTestHelper
.addSourceLines(

View File

@@ -0,0 +1,148 @@
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);
}
}

View File

@@ -47,7 +47,7 @@ public final class MethodMatcherFactoryTest {
CompilationTestHelper.newInstance(MatchedMethodsFlagger.class, getClass());
@Test
public void testCreateWithMalformedSignatures() {
void createWithMalformedSignatures() {
MethodMatcherFactory factory = new MethodMatcherFactory();
assertThatThrownBy(() -> factory.create(ImmutableList.of("foo.bar")))
.isInstanceOf(IllegalArgumentException.class);
@@ -60,7 +60,7 @@ public final class MethodMatcherFactoryTest {
}
@Test
public void testMatcher() {
void matcher() {
compilationTestHelper
.addSourceLines(
"com/example/A.java",

View File

@@ -9,10 +9,10 @@ public final class MethodReferenceUsageCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(MethodReferenceUsageCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(new MethodReferenceUsageCheck(), getClass());
BugCheckerRefactoringTestHelper.newInstance(MethodReferenceUsageCheck.class, getClass());
@Test
public void testIdentification() {
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -201,7 +201,7 @@ public final class MethodReferenceUsageCheckTest {
}
@Test
public void testReplacement() {
void replacement() {
refactoringTestHelper
.addInputLines(
"in/A.java",

View File

@@ -1,6 +1,7 @@
package tech.picnic.errorprone.bugpatterns;
import com.google.common.base.Predicates;
import static com.google.common.base.Predicates.containsPattern;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
@@ -9,11 +10,11 @@ public final class MissingRefasterAnnotationCheckTest {
CompilationTestHelper.newInstance(MissingRefasterAnnotationCheck.class, getClass())
.expectErrorMessage(
"X",
Predicates.containsPattern(
containsPattern(
"The Refaster template contains a method without any Refaster annotations"));
@Test
public void testIdentification() {
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -72,6 +73,20 @@ public final class MissingRefasterAnnotationCheckTest {
" map.computeIfAbsent(key, k -> function(k));",
" }",
" }",
"",
" static final class ValidRefasterTemplate {",
" @BeforeTemplate",
" void unusedPureFunctionCall(Object o) {",
" o.toString();",
" }",
" }",
"",
" static final class NotARefasterTemplate {",
" @Override",
" public String toString() {",
" return \"This is not a Refaster template\";",
" }",
" }",
"}")
.doTest();
}

View File

@@ -0,0 +1,101 @@
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 MockitoStubbingCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(MockitoStubbingCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(MockitoStubbingCheck.class, getClass());
@Test
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",
"import static org.mockito.ArgumentMatchers.eq;",
"import static org.mockito.ArgumentMatchers.notNull;",
"import static org.mockito.Mockito.doAnswer;",
"import static org.mockito.Mockito.mock;",
"",
"import java.util.function.BiConsumer;",
"import java.util.function.Consumer;",
"import org.mockito.ArgumentMatchers;",
"",
"class A {",
" void m() {",
" Runnable runnable = mock(Runnable.class);",
" doAnswer(inv -> null).when(runnable).run();",
"",
" Consumer<String> consumer = mock(Consumer.class);",
" doAnswer(inv -> null).when(consumer).accept(\"foo\");",
" doAnswer(inv -> null).when(consumer).accept(notNull());",
" // BUG: Diagnostic contains:",
" doAnswer(inv -> null).when(consumer).accept(ArgumentMatchers.eq(\"foo\"));",
" // BUG: Diagnostic contains:",
" doAnswer(inv -> null).when(consumer).accept(eq(toString()));",
"",
" BiConsumer<Integer, String> biConsumer = mock(BiConsumer.class);",
" doAnswer(inv -> null).when(biConsumer).accept(0, \"foo\");",
" doAnswer(inv -> null).when(biConsumer).accept(eq(0), notNull());",
" doAnswer(inv -> null).when(biConsumer).accept(notNull(), eq(\"foo\"));",
" // BUG: Diagnostic contains:",
" doAnswer(inv -> null).when(biConsumer).accept(ArgumentMatchers.eq(0), ArgumentMatchers.eq(\"foo\"));",
" // BUG: Diagnostic contains:",
" doAnswer(inv -> null).when(biConsumer).accept(eq(hashCode()), eq(toString()));",
" }",
"}")
.doTest();
}
@Test
void replacement() {
refactoringTestHelper
.addInputLines(
"in/A.java",
"import static org.mockito.ArgumentMatchers.eq;",
"import static org.mockito.Mockito.doAnswer;",
"import static org.mockito.Mockito.mock;",
"",
"import java.util.function.BiConsumer;",
"import java.util.function.Consumer;",
"import org.mockito.ArgumentMatchers;",
"",
"class A {",
" void m() {",
" Consumer<String> consumer = mock(Consumer.class);",
" doAnswer(inv -> null).when(consumer).accept(ArgumentMatchers.eq(\"foo\"));",
" doAnswer(inv -> null).when(consumer).accept(eq(toString()));",
"",
" BiConsumer<Integer, String> biConsumer = mock(BiConsumer.class);",
" doAnswer(inv -> null).when(biConsumer).accept(ArgumentMatchers.eq(0), ArgumentMatchers.eq(\"foo\"));",
" doAnswer(inv -> null).when(biConsumer).accept(eq(hashCode()), eq(toString()));",
" }",
"}")
.addOutputLines(
"out/A.java",
"import static org.mockito.ArgumentMatchers.eq;",
"import static org.mockito.Mockito.doAnswer;",
"import static org.mockito.Mockito.mock;",
"",
"import java.util.function.BiConsumer;",
"import java.util.function.Consumer;",
"import org.mockito.ArgumentMatchers;",
"",
"class A {",
" void m() {",
" Consumer<String> consumer = mock(Consumer.class);",
" doAnswer(inv -> null).when(consumer).accept(\"foo\");",
" doAnswer(inv -> null).when(consumer).accept(toString());",
"",
" BiConsumer<Integer, String> biConsumer = mock(BiConsumer.class);",
" doAnswer(inv -> null).when(biConsumer).accept(0, \"foo\");",
" doAnswer(inv -> null).when(biConsumer).accept(hashCode(), toString());",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -9,7 +9,7 @@ public final class PrimitiveComparisonCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(PrimitiveComparisonCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(new PrimitiveComparisonCheck(), getClass());
BugCheckerRefactoringTestHelper.newInstance(PrimitiveComparisonCheck.class, getClass());
// XXX: There are no tests for multiple replacements within the same expression:
// - Error Prone doesn't currently support this, it seems.
@@ -19,7 +19,7 @@ public final class PrimitiveComparisonCheckTest {
// The logic for `char` and `short` is exactly analogous to the `byte` case.
@Test
public void testByteComparison() {
void byteComparison() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -108,7 +108,7 @@ public final class PrimitiveComparisonCheckTest {
}
@Test
public void testIntComparison() {
void intComparison() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -201,7 +201,7 @@ public final class PrimitiveComparisonCheckTest {
}
@Test
public void testLongComparison() {
void longComparison() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -278,7 +278,7 @@ public final class PrimitiveComparisonCheckTest {
}
@Test
public void testFloatComparison() {
void floatComparison() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -335,7 +335,7 @@ public final class PrimitiveComparisonCheckTest {
}
@Test
public void testDoubleComparison() {
void doubleComparison() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -396,7 +396,7 @@ public final class PrimitiveComparisonCheckTest {
}
@Test
public void testStringComparison() {
void stringComparison() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -429,7 +429,7 @@ public final class PrimitiveComparisonCheckTest {
// XXX: If the explicit `<A, BoxedPrimitive>` generic type information was necessary, then this
// replacement drops too much information.
@Test
public void testReplacementWithPrimitiveVariants() {
void replacementWithPrimitiveVariants() {
refactoringTestHelper
.addInputLines(
"in/A.java",
@@ -483,7 +483,7 @@ public final class PrimitiveComparisonCheckTest {
// XXX: If the explicit `<A>` generic type information was necessary, then this replacement drops
// too much information.
@Test
public void testReplacementWithBoxedVariants() {
void replacementWithBoxedVariants() {
refactoringTestHelper
.addInputLines(
"in/A.java",
@@ -535,7 +535,7 @@ public final class PrimitiveComparisonCheckTest {
}
@Test
public void testReplacementWithPrimitiveVariantsUsingStaticImports() {
void replacementWithPrimitiveVariantsUsingStaticImports() {
refactoringTestHelper
.addInputLines(
"in/A.java",
@@ -574,7 +574,7 @@ public final class PrimitiveComparisonCheckTest {
}
@Test
public void testReplacementWithBoxedVariantsUsingStaticImports() {
void replacementWithBoxedVariantsUsingStaticImports() {
refactoringTestHelper
.addInputLines(
"in/A.java",
@@ -615,7 +615,7 @@ public final class PrimitiveComparisonCheckTest {
}
@Test
public void testReplacementWithPrimitiveVariantsInComplexSyntacticalContext() {
void replacementWithPrimitiveVariantsInComplexSyntacticalContext() {
refactoringTestHelper
.addInputLines(
"in/A.java",
@@ -647,7 +647,7 @@ public final class PrimitiveComparisonCheckTest {
}
@Test
public void testReplacementWithBoxedVariantsInComplexSyntacticalContext() {
void replacementWithBoxedVariantsInComplexSyntacticalContext() {
refactoringTestHelper
.addInputLines(
"in/A.java",

View File

@@ -18,10 +18,10 @@ public final class RedundantStringConversionCheckTest {
ImmutableList.of(
"-XepOpt:RedundantStringConversion:ExtraConversionMethods=java.lang.Enum#name(),A#name(),A.B#toString(int)"));
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(new RedundantStringConversionCheck(), getClass());
BugCheckerRefactoringTestHelper.newInstance(RedundantStringConversionCheck.class, getClass());
@Test
public void testIdentificationOfIdentityTransformation() {
void identificationOfIdentityTransformation() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -44,7 +44,7 @@ public final class RedundantStringConversionCheckTest {
}
@Test
public void testIdentificationWithinMutatingAssignment() {
void identificationWithinMutatingAssignment() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -100,7 +100,7 @@ public final class RedundantStringConversionCheckTest {
}
@Test
public void testIdentificationWithinBinaryOperation() {
void identificationWithinBinaryOperation() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -186,7 +186,7 @@ public final class RedundantStringConversionCheckTest {
}
@Test
public void testIdentificationWithinStringBuilderMethod() {
void identificationWithinStringBuilderMethod() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -235,7 +235,7 @@ public final class RedundantStringConversionCheckTest {
// XXX: Also test the other formatter methods.
@Test
public void testIdentificationWithinFormatterMethod() {
void identificationWithinFormatterMethod() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -280,7 +280,7 @@ public final class RedundantStringConversionCheckTest {
}
@Test
public void testIdentificationWithinGuavaGuardMethod() {
void identificationWithinGuavaGuardMethod() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -340,7 +340,7 @@ public final class RedundantStringConversionCheckTest {
}
@Test
public void testIdentificationWithinSlf4jLoggerMethod() {
void identificationWithinSlf4jLoggerMethod() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -395,7 +395,7 @@ public final class RedundantStringConversionCheckTest {
}
@Test
public void testIdentificationOfCustomConversionMethod() {
void identificationOfCustomConversionMethod() {
customizedCompilationTestHelper
.addSourceLines(
"A.java",
@@ -486,7 +486,7 @@ public final class RedundantStringConversionCheckTest {
}
@Test
public void testReplacement() {
void replacement() {
refactoringTestHelper
.addInputLines(
"in/A.java",

View File

@@ -0,0 +1,68 @@
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 RefasterAnyOfUsageCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(RefasterAnyOfUsageCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(RefasterAnyOfUsageCheck.class, getClass());
@Test
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",
"import com.google.errorprone.refaster.Refaster;",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"",
"class A {",
" @BeforeTemplate",
" String before(String str) {",
" // BUG: Diagnostic contains:",
" Refaster.anyOf();",
" // BUG: Diagnostic contains:",
" return Refaster.anyOf(str);",
" }",
"",
" @BeforeTemplate",
" Object before2(String str, Object obj) {",
" return Refaster.anyOf(str, obj);",
" }",
"}")
.doTest();
}
@Test
void replacement() {
refactoringTestHelper
.addInputLines(
"in/A.java",
"import com.google.errorprone.refaster.Refaster;",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"",
"class A {",
" @BeforeTemplate",
" String before(String str) {",
" Refaster.anyOf();",
" return Refaster.anyOf(str);",
" }",
"}")
.addOutputLines(
"out/A.java",
"import com.google.errorprone.refaster.Refaster;",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
"",
"class A {",
" @BeforeTemplate",
" String before(String str) {",
" Refaster.anyOf();",
" return str;",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -5,15 +5,12 @@ 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.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CodeTransformer;
import com.google.errorprone.ErrorProneFlags;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
@@ -70,16 +67,14 @@ public final class RefasterCheckTest {
"Stream",
"String",
"TestNGToAssertJ",
"Time");
"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\\$.*");
/** All Refaster templates on the classpath, indexed by their name. */
private static final ImmutableListMultimap<String, CodeTransformer> ALL_CODE_TRANSFORMERS =
RefasterCheck.loadAllCodeTransformers();
/**
* A mapping from template group names to associated template names.
*
@@ -87,7 +82,7 @@ public final class RefasterCheckTest {
* templates, while the keys correspond to the associated top-level "aggregator" classes.
*/
private static final ImmutableSetMultimap<String, String> TEMPLATES_BY_GROUP =
indexTemplateNamesByGroup(ALL_CODE_TRANSFORMERS.keySet());
indexTemplateNamesByGroup(RefasterCheck.ALL_CODE_TRANSFORMERS.get().keySet());
/** Returns every known template group name as a parameterized test argument. */
@SuppressWarnings("UnusedMethod" /* Used as a `@MethodSource`. */)
@@ -104,7 +99,7 @@ public final class RefasterCheckTest {
// 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.of(e.getKey(), e.getValue()));
.map(e -> arguments(e.getKey(), e.getValue()));
}
/**
@@ -112,10 +107,10 @@ public final class RefasterCheckTest {
* 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 #testReplacement}'s coverage is exhaustive.
* that in turn {@link #replacement}'s coverage is exhaustive.
*/
@Test
public void testLoadAllCodeTransformers() {
void loadAllCodeTransformers() {
assertThat(TEMPLATES_BY_GROUP.keySet()).hasSameElementsAs(TEMPLATE_GROUPS);
}
@@ -123,9 +118,9 @@ public final class RefasterCheckTest {
* Verifies for each of the {@link #TEMPLATE_GROUPS} that the associated code transformers have
* the desired effect.
*/
@ParameterizedTest
@MethodSource("templateGroupsUnderTest")
public void testReplacement(String group) {
@ParameterizedTest
void replacement(String group) {
verifyRefactoring(group, namePattern(group));
}
@@ -136,9 +131,9 @@ public final class RefasterCheckTest {
* com.google.errorprone.refaster.Refaster#anyOf} branches are tested. Idem for {@link
* com.google.errorprone.refaster.annotation.BeforeTemplate} methods in case there are multiple .
*/
@ParameterizedTest
@MethodSource("templatesUnderTest")
public void testCoverage(String group, String template) {
@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)
@@ -171,10 +166,7 @@ public final class RefasterCheckTest {
private BugCheckerRefactoringTestHelper createRestrictedRefactoringTestHelper(
String namePattern) {
return BugCheckerRefactoringTestHelper.newInstance(
new RefasterCheck(
ErrorProneFlags.fromMap(ImmutableMap.of("Refaster:NamePattern", namePattern)),
ALL_CODE_TRANSFORMERS),
getClass());
return BugCheckerRefactoringTestHelper.newInstance(RefasterCheck.class, getClass())
.setArgs("-XepOpt:Refaster:NamePattern=" + namePattern);
}
}

View File

@@ -8,7 +8,7 @@ public final class RequestMappingAnnotationCheckTest {
CompilationTestHelper.newInstance(RequestMappingAnnotationCheck.class, getClass());
@Test
public void testIdentification() {
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",

View File

@@ -0,0 +1,99 @@
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;
import org.junit.jupiter.api.condition.DisabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
public final class ScheduledTransactionTraceCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(ScheduledTransactionTraceCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(ScheduledTransactionTraceCheck.class, getClass());
@Test
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",
"import com.newrelic.api.agent.Trace;",
"import org.springframework.scheduling.annotation.Scheduled;",
"",
"class A {",
" void notScheduled() {}",
"",
" @Scheduled(fixedDelay = 1)",
" // BUG: Diagnostic contains:",
" void scheduledButNotTraced() {}",
"",
" @Scheduled(fixedDelay = 1)",
" // BUG: Diagnostic contains:",
" @Trace",
" void scheduledButImproperlyTraced1() {}",
"",
" @Scheduled(fixedDelay = 1)",
" // BUG: Diagnostic contains:",
" @Trace(dispatcher = false)",
" void scheduledButImproperlyTraced2() {}",
"",
" @Scheduled(fixedDelay = 1)",
" @Trace(dispatcher = true)",
" void scheduledAndProperlyTraced() {}",
"}")
.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(
"in/A.java",
"import com.newrelic.api.agent.Trace;",
"import org.springframework.scheduling.annotation.Scheduled;",
"",
"class A {",
" @Scheduled(fixedDelay = 1)",
" void scheduledButNotTraced() {}",
"",
" @Scheduled(fixedDelay = 1)",
" @Trace",
" void scheduledButImproperlyTraced1() {}",
"",
" @Scheduled(fixedDelay = 1)",
" @Trace(dispatcher = false)",
" void scheduledButImproperlyTraced2() {}",
"",
" @Scheduled(fixedDelay = 1)",
" @Trace(leaf = true)",
" void scheduledButImproperlyTraced3() {}",
"}")
.addOutputLines(
"out/A.java",
"import com.newrelic.api.agent.Trace;",
"import org.springframework.scheduling.annotation.Scheduled;",
"",
"class A {",
" @Trace(dispatcher = true)",
" @Scheduled(fixedDelay = 1)",
" void scheduledButNotTraced() {}",
"",
" @Scheduled(fixedDelay = 1)",
" @Trace(dispatcher = true)",
" void scheduledButImproperlyTraced1() {}",
"",
" @Scheduled(fixedDelay = 1)",
" @Trace(dispatcher = true)",
" void scheduledButImproperlyTraced2() {}",
"",
" @Scheduled(fixedDelay = 1)",
" @Trace(dispatcher = true, leaf = true)",
" void scheduledButImproperlyTraced3() {}",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -9,10 +9,10 @@ public final class Slf4jLogStatementCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(Slf4jLogStatementCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(new Slf4jLogStatementCheck(), getClass());
BugCheckerRefactoringTestHelper.newInstance(Slf4jLogStatementCheck.class, getClass());
@Test
public void testIdentification() {
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -92,7 +92,7 @@ public final class Slf4jLogStatementCheckTest {
// XXX: Drop what's unused.
@Test
public void testReplacement() {
void replacement() {
refactoringTestHelper
.addInputLines(
"in/A.java",

View File

@@ -9,10 +9,10 @@ public final class SpringMvcAnnotationCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(SpringMvcAnnotationCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(new SpringMvcAnnotationCheck(), getClass());
BugCheckerRefactoringTestHelper.newInstance(SpringMvcAnnotationCheck.class, getClass());
@Test
public void testIdentification() {
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -59,7 +59,7 @@ public final class SpringMvcAnnotationCheckTest {
}
@Test
public void testReplacement() {
void replacement() {
refactoringTestHelper
.addInputLines(
"in/A.java",

View File

@@ -3,6 +3,7 @@ package tech.picnic.errorprone.bugpatterns;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
@@ -10,16 +11,28 @@ public final class StaticImportCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(StaticImportCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(new StaticImportCheck(), getClass());
BugCheckerRefactoringTestHelper.newInstance(StaticImportCheck.class, getClass());
@Test
public void testCandidateMethodsAreNotRedundant() {
assertThat(StaticImportCheck.STATIC_IMPORT_CANDIDATE_METHODS.keySet())
.doesNotContainAnyElementsOf(StaticImportCheck.STATIC_IMPORT_CANDIDATE_CLASSES);
void candidateMethodsAreNotRedundant() {
assertThat(StaticImportCheck.STATIC_IMPORT_CANDIDATE_MEMBERS.keySet())
.doesNotContainAnyElementsOf(StaticImportCheck.STATIC_IMPORT_CANDIDATE_TYPES);
}
@Test
public void testIdentification() {
void exemptedMembersAreNotVacuous() {
assertThat(StaticImportCheck.STATIC_IMPORT_EXEMPTED_MEMBERS.keySet())
.isSubsetOf(StaticImportCheck.STATIC_IMPORT_CANDIDATE_TYPES);
}
@Test
void exemptedMembersAreNotRedundant() {
assertThat(StaticImportCheck.STATIC_IMPORT_EXEMPTED_MEMBERS.values())
.doesNotContainAnyElementsOf(StaticImportCheck.STATIC_IMPORT_EXEMPTED_IDENTIFIERS);
}
@Test
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",
@@ -27,6 +40,7 @@ public final class StaticImportCheckTest {
"import static com.google.common.collect.ImmutableSet.toImmutableSet;",
"import static java.nio.charset.StandardCharsets.UTF_8;",
"import static java.util.function.Predicate.not;",
"import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;",
"",
"import com.google.common.base.Predicates;",
"import com.google.common.collect.ImmutableMap;",
@@ -35,6 +49,9 @@ public final class StaticImportCheckTest {
"import java.nio.charset.StandardCharsets;",
"import java.util.Optional;",
"import java.util.function.Predicate;",
"import org.springframework.boot.test.context.SpringBootTest;",
"import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;",
"import org.springframework.http.MediaType;",
"",
"class A {",
" void m() {",
@@ -68,6 +85,15 @@ public final class StaticImportCheckTest {
" Object o1 = StandardCharsets.UTF_8;",
" Object o2 = UTF_8;",
"",
" // BUG: Diagnostic contains:",
" Object e1 = WebEnvironment.RANDOM_PORT;",
" Object e2 = RANDOM_PORT;",
"",
" // Not flagged because `MediaType.ALL` is exempted.",
" MediaType t1 = MediaType.ALL;",
" // BUG: Diagnostic contains:",
" MediaType t2 = MediaType.APPLICATION_JSON;",
"",
" Optional.empty();",
" }",
"",
@@ -77,7 +103,7 @@ public final class StaticImportCheckTest {
}
@Test
public void testReplacement() {
void replacement() {
refactoringTestHelper
.addInputLines(
"in/A.java",
@@ -87,8 +113,13 @@ public final class StaticImportCheckTest {
"import com.google.common.collect.ImmutableMap;",
"import com.google.common.collect.ImmutableSet;",
"import java.nio.charset.StandardCharsets;",
"import java.util.Objects;",
"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.http.MediaType;",
"",
"class A {",
" void m1() {",
@@ -101,7 +132,17 @@ public final class StaticImportCheckTest {
" Predicates.not(null);",
" not(null);",
"",
" Arguments.arguments(\"foo\");",
"",
" Objects.requireNonNull(\"bar\");",
"",
" Object o = StandardCharsets.UTF_8;",
"",
" ImmutableSet.of(",
" MediaType.ALL,",
" MediaType.APPLICATION_XHTML_XML,",
" MediaType.TEXT_HTML,",
" MediaType.valueOf(\"image/webp\"));",
" }",
"",
" void m2(",
@@ -113,23 +154,36 @@ public final class StaticImportCheckTest {
" @DateTimeFormat(iso = ISO.DATE) String date,",
" @DateTimeFormat(iso = ISO.DATE_TIME) String dateTime,",
" @DateTimeFormat(iso = ISO.TIME) String time) {}",
"",
" @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)",
" final class Test {}",
"}")
.addOutputLines(
"out/A.java",
"import static com.google.common.collect.ImmutableMap.toImmutableMap;",
"import static com.google.common.collect.ImmutableSet.toImmutableSet;",
"import static java.nio.charset.StandardCharsets.UTF_8;",
"import static java.util.Objects.requireNonNull;",
"import static java.util.function.Predicate.not;",
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
"import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;",
"import static org.springframework.format.annotation.DateTimeFormat.ISO.DATE;",
"import static org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIME;",
"import static org.springframework.format.annotation.DateTimeFormat.ISO.TIME;",
"import static org.springframework.http.MediaType.APPLICATION_XHTML_XML;",
"import static org.springframework.http.MediaType.TEXT_HTML;",
"",
"import com.google.common.base.Predicates;",
"import com.google.common.collect.ImmutableMap;",
"import com.google.common.collect.ImmutableSet;",
"import java.nio.charset.StandardCharsets;",
"import java.util.Objects;",
"import org.junit.jupiter.params.provider.Arguments;",
"import org.springframework.boot.test.context.SpringBootTest;",
"import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;",
"import org.springframework.format.annotation.DateTimeFormat;",
"import org.springframework.format.annotation.DateTimeFormat.ISO;",
"import org.springframework.http.MediaType;",
"",
"class A {",
" void m1() {",
@@ -142,7 +196,17 @@ public final class StaticImportCheckTest {
" Predicates.not(null);",
" not(null);",
"",
" arguments(\"foo\");",
"",
" requireNonNull(\"bar\");",
"",
" Object o = UTF_8;",
"",
" ImmutableSet.of(",
" MediaType.ALL,",
" APPLICATION_XHTML_XML,",
" TEXT_HTML,",
" MediaType.valueOf(\"image/webp\"));",
" }",
"",
" void m2(",
@@ -154,7 +218,10 @@ public final class StaticImportCheckTest {
" @DateTimeFormat(iso = DATE) String date,",
" @DateTimeFormat(iso = DATE_TIME) String dateTime,",
" @DateTimeFormat(iso = TIME) String time) {}",
"",
" @SpringBootTest(webEnvironment = RANDOM_PORT)",
" final class Test {}",
"}")
.doTest(BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH);
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -0,0 +1,103 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.common.base.Predicates.containsPattern;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
public final class TimeZoneUsageCheckTest {
private final CompilationTestHelper compilationHelper =
CompilationTestHelper.newInstance(TimeZoneUsageCheck.class, getClass())
.expectErrorMessage(
"X",
containsPattern(
"Derive the current time from an existing `Clock` Spring bean, and don't rely on a `Clock`'s time zone"));
@Test
void identification() {
compilationHelper
.addSourceLines(
"A.java",
"import static java.time.ZoneOffset.UTC;",
"",
"import java.time.Clock;",
"import java.time.Duration;",
"import java.time.Instant;",
"import java.time.LocalDate;",
"import java.time.LocalDateTime;",
"import java.time.LocalTime;",
"import java.time.ZoneId;",
"",
"class A {",
" void m() {",
" Clock clock = Clock.fixed(Instant.EPOCH, UTC);",
" clock.instant();",
" clock.millis();",
" Clock.offset(clock, Duration.ZERO);",
" Clock.tick(clock, Duration.ZERO);",
"",
" // BUG: Diagnostic matches: X",
" Clock.systemUTC();",
" // BUG: Diagnostic matches: X",
" Clock.systemDefaultZone();",
" // BUG: Diagnostic matches: X",
" Clock.system(UTC);",
" // BUG: Diagnostic matches: X",
" Clock.tickMillis(UTC);",
" // BUG: Diagnostic matches: X",
" Clock.tickMinutes(UTC);",
" // BUG: Diagnostic matches: X",
" Clock.tickSeconds(UTC);",
" // BUG: Diagnostic matches: X",
" clock.getZone();",
" // BUG: Diagnostic matches: X",
" clock.withZone(UTC);",
"",
" // BUG: Diagnostic matches: X",
" Instant.now();",
" // This is equivalent to `clock.instant()`, which is fine.",
" Instant.now(clock);",
"",
" // BUG: Diagnostic matches: X",
" LocalDate.now();",
" // BUG: Diagnostic matches: X",
" LocalDate.now(clock);",
" // BUG: Diagnostic matches: X",
" LocalDate.now(UTC);",
"",
" // BUG: Diagnostic matches: X",
" LocalDateTime.now();",
" // BUG: Diagnostic matches: X",
" LocalDateTime.now(clock);",
" // BUG: Diagnostic matches: X",
" LocalDateTime.now(UTC);",
"",
" // BUG: Diagnostic matches: X",
" LocalTime.now();",
" // BUG: Diagnostic matches: X",
" LocalTime.now(clock);",
" // BUG: Diagnostic matches: X",
" LocalTime.now(UTC);",
" }",
"",
" abstract class ForwardingClock extends Clock {",
" private final Clock clock;",
"",
" ForwardingClock(Clock clock) {",
" this.clock = clock;",
" }",
"",
" @Override",
" public ZoneId getZone() {",
" return clock.getZone();",
" }",
"",
" @Override",
" public Clock withZone(ZoneId zone) {",
" return clock.withZone(zone);",
" }",
" }",
"}")
.doTest();
}
}

View File

@@ -44,6 +44,12 @@ final class AssertJOptionalTemplatesTest implements RefasterTemplateTestCase {
assertThat(Optional.of(4)).isPresent().hasValue(4));
}
ImmutableSet<AbstractAssert<?, ?>> testAbstractOptionalAssertContainsSame() {
return ImmutableSet.of(
assertThat(Optional.of(1)).get().isSameAs(1),
assertThat(Optional.of(2)).isPresent().isSameAs(2));
}
AbstractAssert<?, ?> testAssertThatOptionalHasValueMatching() {
return assertThat(Optional.of("foo").filter(String::isEmpty)).isPresent();
}

View File

@@ -40,6 +40,11 @@ final class AssertJOptionalTemplatesTest implements RefasterTemplateTestCase {
assertThat(Optional.of(4)).hasValue(4));
}
ImmutableSet<AbstractAssert<?, ?>> testAbstractOptionalAssertContainsSame() {
return ImmutableSet.of(
assertThat(Optional.of(1)).containsSame(1), assertThat(Optional.of(2)).containsSame(2));
}
AbstractAssert<?, ?> testAssertThatOptionalHasValueMatching() {
return assertThat(Optional.of("foo")).get().matches(String::isEmpty);
}

View File

@@ -83,18 +83,12 @@ final class DoubleStreamTemplatesTest implements RefasterTemplateTestCase {
DoubleStream.of(2).filter(n -> n > 2).findAny().isPresent());
}
ImmutableSet<Boolean> testDoubleStreamAllMatch() {
boolean testDoubleStreamAllMatch() {
DoublePredicate pred = i -> i > 0;
return ImmutableSet.of(
DoubleStream.of(1).noneMatch(pred.negate()),
!DoubleStream.of(2).anyMatch(pred.negate()),
DoubleStream.of(3).filter(pred.negate()).findAny().isEmpty());
return DoubleStream.of(1).noneMatch(pred.negate());
}
ImmutableSet<Boolean> testDoubleStreamAllMatch2() {
return ImmutableSet.of(
DoubleStream.of(1).noneMatch(n -> !(n > 1)),
!DoubleStream.of(2).anyMatch(n -> !(n > 2)),
DoubleStream.of(3).filter(n -> !(n > 3)).findAny().isEmpty());
boolean testDoubleStreamAllMatch2() {
return DoubleStream.of(1).noneMatch(n -> !(n > 1));
}
}

View File

@@ -82,18 +82,12 @@ final class DoubleStreamTemplatesTest implements RefasterTemplateTestCase {
DoubleStream.of(1).anyMatch(n -> n > 1), DoubleStream.of(2).anyMatch(n -> n > 2));
}
ImmutableSet<Boolean> testDoubleStreamAllMatch() {
boolean testDoubleStreamAllMatch() {
DoublePredicate pred = i -> i > 0;
return ImmutableSet.of(
DoubleStream.of(1).allMatch(pred),
DoubleStream.of(2).allMatch(pred),
DoubleStream.of(3).allMatch(pred));
return DoubleStream.of(1).allMatch(pred);
}
ImmutableSet<Boolean> testDoubleStreamAllMatch2() {
return ImmutableSet.of(
DoubleStream.of(1).allMatch(n -> n > 1),
DoubleStream.of(2).allMatch(n -> n > 2),
DoubleStream.of(3).allMatch(n -> n > 3));
boolean testDoubleStreamAllMatch2() {
return DoubleStream.of(1).allMatch(n -> n > 1);
}
}

View File

@@ -7,38 +7,15 @@ import java.util.Objects;
import java.util.stream.Stream;
final class EqualityTemplatesTest implements RefasterTemplateTestCase {
@Override
public ImmutableSet<?> elidedTypesAndStaticImports() {
return ImmutableSet.of(Objects.class);
}
ImmutableSet<Boolean> testPrimitiveOrReferenceEquality() {
return ImmutableSet.of(
Objects.equals(true, false),
Objects.equals((byte) 0, (byte) 1),
Objects.equals((short) 0, (short) 1),
Objects.equals(0, 1),
Objects.equals(0L, 1L),
Objects.equals(0F, 1F),
Objects.equals(0.0, 1.0),
Objects.equals(Boolean.TRUE, Boolean.FALSE),
Objects.equals(Byte.valueOf((byte) 0), Byte.valueOf((byte) 1)),
Objects.equals(Short.valueOf((short) 0), Short.valueOf((short) 1)),
Objects.equals(Integer.valueOf(0), Integer.valueOf(1)),
Objects.equals(Long.valueOf(0L), Long.valueOf(1L)),
Objects.equals(Float.valueOf(0F), Float.valueOf(1F)),
Objects.equals(Double.valueOf(0.0), Double.valueOf(1.0)),
RoundingMode.UP.equals(RoundingMode.DOWN),
Objects.equals(RoundingMode.UP, RoundingMode.DOWN),
!Objects.equals(true, false),
!Objects.equals((byte) 0, (byte) 1),
!Objects.equals((short) 0, (short) 1),
!Objects.equals(0, 1),
!Objects.equals(0L, 1L),
!Objects.equals(0F, 1F),
!Objects.equals(0.0, 1.0),
!Objects.equals(Boolean.TRUE, Boolean.FALSE),
!Objects.equals(Byte.valueOf((byte) 0), Byte.valueOf((byte) 1)),
!Objects.equals(Short.valueOf((short) 0), Short.valueOf((short) 1)),
!Objects.equals(Integer.valueOf(0), Integer.valueOf(1)),
!Objects.equals(Long.valueOf(0L), Long.valueOf(1L)),
!Objects.equals(Float.valueOf(0F), Float.valueOf(1F)),
!Objects.equals(Double.valueOf(0.0), Double.valueOf(1.0)),
!RoundingMode.UP.equals(RoundingMode.DOWN),
!Objects.equals(RoundingMode.UP, RoundingMode.DOWN));
}

View File

@@ -7,38 +7,15 @@ import java.util.Objects;
import java.util.stream.Stream;
final class EqualityTemplatesTest implements RefasterTemplateTestCase {
@Override
public ImmutableSet<?> elidedTypesAndStaticImports() {
return ImmutableSet.of(Objects.class);
}
ImmutableSet<Boolean> testPrimitiveOrReferenceEquality() {
return ImmutableSet.of(
true == false,
(byte) 0 == (byte) 1,
(short) 0 == (short) 1,
0 == 1,
0L == 1L,
0F == 1F,
0.0 == 1.0,
Objects.equals(Boolean.TRUE, Boolean.FALSE),
Objects.equals(Byte.valueOf((byte) 0), Byte.valueOf((byte) 1)),
Objects.equals(Short.valueOf((short) 0), Short.valueOf((short) 1)),
Objects.equals(Integer.valueOf(0), Integer.valueOf(1)),
Objects.equals(Long.valueOf(0L), Long.valueOf(1L)),
Objects.equals(Float.valueOf(0F), Float.valueOf(1F)),
Objects.equals(Double.valueOf(0.0), Double.valueOf(1.0)),
RoundingMode.UP == RoundingMode.DOWN,
RoundingMode.UP == RoundingMode.DOWN,
true != false,
(byte) 0 != (byte) 1,
(short) 0 != (short) 1,
0 != 1,
0L != 1L,
0F != 1F,
0.0 != 1.0,
!Objects.equals(Boolean.TRUE, Boolean.FALSE),
!Objects.equals(Byte.valueOf((byte) 0), Byte.valueOf((byte) 1)),
!Objects.equals(Short.valueOf((short) 0), Short.valueOf((short) 1)),
!Objects.equals(Integer.valueOf(0), Integer.valueOf(1)),
!Objects.equals(Long.valueOf(0L), Long.valueOf(1L)),
!Objects.equals(Float.valueOf(0F), Float.valueOf(1F)),
!Objects.equals(Double.valueOf(0.0), Double.valueOf(1.0)),
RoundingMode.UP != RoundingMode.DOWN,
RoundingMode.UP != RoundingMode.DOWN);
}

View File

@@ -33,25 +33,20 @@ final class ImmutableListMultimapTemplatesTest implements RefasterTemplateTestCa
ImmutableSet<ImmutableMultimap<String, Integer>> testEmptyImmutableListMultimap() {
return ImmutableSet.of(
ImmutableListMultimap.<String, Integer>builder().build(),
ImmutableMultimap.<String, Integer>builder().build(),
ImmutableMultimap.of());
ImmutableListMultimap.<String, Integer>builder().build(), ImmutableMultimap.of());
}
ImmutableSet<ImmutableMultimap<String, Integer>> testPairToImmutableListMultimap() {
return ImmutableSet.of(
ImmutableListMultimap.<String, Integer>builder().put("foo", 1).build(),
ImmutableMultimap.<String, Integer>builder().put("bar", 2).build(),
ImmutableMultimap.of("baz", 3));
ImmutableMultimap.of("bar", 2));
}
ImmutableList<ImmutableMultimap<String, Integer>> testEntryToImmutableListMultimap() {
return ImmutableList.of(
ImmutableListMultimap.<String, Integer>builder().put(Map.entry("foo", 1)).build(),
Stream.of(Map.entry("foo", 1))
.collect(toImmutableListMultimap(Map.Entry::getKey, Map.Entry::getValue)),
ImmutableMultimap.<String, Integer>builder().put(Map.entry("foo", 1)).build(),
ImmutableMultimap.of(Map.entry("foo", 1).getKey(), Map.entry("foo", 1).getValue()));
.collect(toImmutableListMultimap(Map.Entry::getKey, Map.Entry::getValue)));
}
ImmutableList<ImmutableMultimap<String, Integer>> testIterableToImmutableListMultimap() {
@@ -69,12 +64,6 @@ final class ImmutableListMultimapTemplatesTest implements RefasterTemplateTestCa
.collect(toImmutableListMultimap(Map.Entry::getKey, Map.Entry::getValue)),
ImmutableMultimap.copyOf(ImmutableListMultimap.of("foo", 1)),
ImmutableMultimap.copyOf(ImmutableListMultimap.of("foo", 1).entries()),
ImmutableMultimap.<String, Integer>builder()
.putAll(ImmutableListMultimap.of("foo", 1))
.build(),
ImmutableMultimap.<String, Integer>builder()
.putAll(ImmutableListMultimap.of("foo", 1).entries())
.build(),
ImmutableMultimap.copyOf(Iterables.cycle(Map.entry("foo", 1))));
}
@@ -105,7 +94,8 @@ final class ImmutableListMultimapTemplatesTest implements RefasterTemplateTestCa
.collect(
flatteningToImmutableListMultimap(
Map.Entry::getKey, e -> e.getValue().stream().map(Math::toIntExact))),
Multimaps.asMap((Multimap<String, Long>) ImmutableSetMultimap.of("bar", 2L)).entrySet()
Multimaps.asMap((Multimap<String, Long>) ImmutableSetMultimap.of("bar", 2L))
.entrySet()
.stream()
.collect(
flatteningToImmutableListMultimap(

View File

@@ -32,21 +32,15 @@ final class ImmutableListMultimapTemplatesTest implements RefasterTemplateTestCa
}
ImmutableSet<ImmutableMultimap<String, Integer>> testEmptyImmutableListMultimap() {
return ImmutableSet.of(
ImmutableListMultimap.of(), ImmutableListMultimap.of(), ImmutableListMultimap.of());
return ImmutableSet.of(ImmutableListMultimap.of(), ImmutableListMultimap.of());
}
ImmutableSet<ImmutableMultimap<String, Integer>> testPairToImmutableListMultimap() {
return ImmutableSet.of(
ImmutableListMultimap.of("foo", 1),
ImmutableListMultimap.of("bar", 2),
ImmutableListMultimap.of("baz", 3));
return ImmutableSet.of(ImmutableListMultimap.of("foo", 1), ImmutableListMultimap.of("bar", 2));
}
ImmutableList<ImmutableMultimap<String, Integer>> testEntryToImmutableListMultimap() {
return ImmutableList.of(
ImmutableListMultimap.of(Map.entry("foo", 1).getKey(), Map.entry("foo", 1).getValue()),
ImmutableListMultimap.of(Map.entry("foo", 1).getKey(), Map.entry("foo", 1).getValue()),
ImmutableListMultimap.of(Map.entry("foo", 1).getKey(), Map.entry("foo", 1).getValue()),
ImmutableListMultimap.of(Map.entry("foo", 1).getKey(), Map.entry("foo", 1).getValue()));
}
@@ -60,8 +54,6 @@ final class ImmutableListMultimapTemplatesTest implements RefasterTemplateTestCa
ImmutableListMultimap.copyOf(Iterables.cycle(Map.entry("foo", 1))),
ImmutableListMultimap.copyOf(ImmutableListMultimap.of("foo", 1)),
ImmutableListMultimap.copyOf(ImmutableListMultimap.of("foo", 1)),
ImmutableListMultimap.copyOf(ImmutableListMultimap.of("foo", 1)),
ImmutableListMultimap.copyOf(ImmutableListMultimap.of("foo", 1).entries()),
ImmutableListMultimap.copyOf(Iterables.cycle(Map.entry("foo", 1))));
}

View File

@@ -56,12 +56,7 @@ final class ImmutableListTemplatesTest implements RefasterTemplateTestCase {
ImmutableSet<ImmutableList<Integer>> testStreamToImmutableList() {
return ImmutableSet.of(
ImmutableList.copyOf(Stream.of(1).iterator()),
ImmutableList.copyOf(Stream.of(2).iterator()),
Stream.of(3).collect(collectingAndThen(toList(), ImmutableList::copyOf)));
}
ImmutableList<Integer> testImmutableListAsList() {
return ImmutableList.of(1, 2, 3).asList();
Stream.of(2).collect(collectingAndThen(toList(), ImmutableList::copyOf)));
}
ImmutableSet<ImmutableList<Integer>> testImmutableListSortedCopyOf() {

View File

@@ -54,13 +54,7 @@ final class ImmutableListTemplatesTest implements RefasterTemplateTestCase {
ImmutableSet<ImmutableList<Integer>> testStreamToImmutableList() {
return ImmutableSet.of(
Stream.of(1).collect(toImmutableList()),
Stream.of(2).collect(toImmutableList()),
Stream.of(3).collect(toImmutableList()));
}
ImmutableList<Integer> testImmutableListAsList() {
return ImmutableList.of(1, 2, 3);
Stream.of(1).collect(toImmutableList()), Stream.of(2).collect(toImmutableList()));
}
ImmutableSet<ImmutableList<Integer>> testImmutableListSortedCopyOf() {

View File

@@ -42,8 +42,7 @@ final class ImmutableMultisetTemplatesTest implements RefasterTemplateTestCase {
ImmutableSet<ImmutableMultiset<Integer>> testStreamToImmutableMultiset() {
return ImmutableSet.of(
ImmutableMultiset.copyOf(Stream.of(1).iterator()),
ImmutableMultiset.copyOf(Stream.of(2)::iterator),
Stream.of(3).collect(collectingAndThen(toList(), ImmutableMultiset::copyOf)));
Stream.of(2).collect(collectingAndThen(toList(), ImmutableMultiset::copyOf)));
}
ImmutableMultiset<Integer> testImmutableMultisetCopyOfImmutableMultiset() {

View File

@@ -39,9 +39,7 @@ final class ImmutableMultisetTemplatesTest implements RefasterTemplateTestCase {
ImmutableSet<ImmutableMultiset<Integer>> testStreamToImmutableMultiset() {
return ImmutableSet.of(
Stream.of(1).collect(toImmutableMultiset()),
Stream.of(2).collect(toImmutableMultiset()),
Stream.of(3).collect(toImmutableMultiset()));
Stream.of(1).collect(toImmutableMultiset()), Stream.of(2).collect(toImmutableMultiset()));
}
ImmutableMultiset<Integer> testImmutableMultisetCopyOfImmutableMultiset() {

View File

@@ -72,7 +72,8 @@ final class ImmutableSetMultimapTemplatesTest implements RefasterTemplateTestCas
.collect(
flatteningToImmutableSetMultimap(
Map.Entry::getKey, e -> e.getValue().stream().map(Math::toIntExact))),
Multimaps.asMap((Multimap<String, Long>) ImmutableSetMultimap.of("bar", 2L)).entrySet()
Multimaps.asMap((Multimap<String, Long>) ImmutableSetMultimap.of("bar", 2L))
.entrySet()
.stream()
.collect(
flatteningToImmutableSetMultimap(

View File

@@ -53,7 +53,7 @@ final class ImmutableSetMultimapTemplatesTest implements RefasterTemplateTestCas
ImmutableSetMultimap<String, Integer> testTransformMultimapValuesToImmutableSetMultimap() {
return ImmutableSetMultimap.copyOf(
Multimaps.transformValues(ImmutableSetMultimap.of("foo", 1L), v -> Math.toIntExact(v)));
Multimaps.transformValues(ImmutableSetMultimap.of("foo", 1L), e -> Math.toIntExact(e)));
}
ImmutableSet<ImmutableSetMultimap<String, Integer>>

View File

@@ -54,10 +54,9 @@ final class ImmutableSetTemplatesTest implements RefasterTemplateTestCase {
ImmutableSet<ImmutableSet<Integer>> testStreamToImmutableSet() {
return ImmutableSet.of(
ImmutableSet.copyOf(Stream.of(1).iterator()),
ImmutableSet.copyOf(Stream.of(2)::iterator),
Stream.of(3).distinct().collect(toImmutableSet()),
Stream.of(4).collect(collectingAndThen(toList(), ImmutableSet::copyOf)),
Stream.of(5).collect(collectingAndThen(toSet(), ImmutableSet::copyOf)));
Stream.of(2).distinct().collect(toImmutableSet()),
Stream.of(3).collect(collectingAndThen(toList(), ImmutableSet::copyOf)),
Stream.of(4).collect(collectingAndThen(toSet(), ImmutableSet::copyOf)));
}
ImmutableSet<Integer> testImmutableSetCopyOfImmutableSet() {

View File

@@ -55,8 +55,7 @@ final class ImmutableSetTemplatesTest implements RefasterTemplateTestCase {
Stream.of(1).collect(toImmutableSet()),
Stream.of(2).collect(toImmutableSet()),
Stream.of(3).collect(toImmutableSet()),
Stream.of(4).collect(toImmutableSet()),
Stream.of(5).collect(toImmutableSet()));
Stream.of(4).collect(toImmutableSet()));
}
ImmutableSet<Integer> testImmutableSetCopyOfImmutableSet() {

View File

@@ -61,7 +61,6 @@ final class ImmutableSortedMultisetTemplatesTest implements RefasterTemplateTest
ImmutableSet<ImmutableSortedMultiset<Integer>> testStreamToImmutableSortedMultiset() {
return ImmutableSet.of(
ImmutableSortedMultiset.copyOf(Stream.of(1).iterator()),
ImmutableSortedMultiset.copyOf(Stream.of(2)::iterator),
Stream.of(3).collect(collectingAndThen(toList(), ImmutableSortedMultiset::copyOf)));
Stream.of(2).collect(collectingAndThen(toList(), ImmutableSortedMultiset::copyOf)));
}
}

View File

@@ -53,7 +53,6 @@ final class ImmutableSortedMultisetTemplatesTest implements RefasterTemplateTest
ImmutableSet<ImmutableSortedMultiset<Integer>> testStreamToImmutableSortedMultiset() {
return ImmutableSet.of(
Stream.of(1).collect(toImmutableSortedMultiset(naturalOrder())),
Stream.of(2).collect(toImmutableSortedMultiset(naturalOrder())),
Stream.of(3).collect(toImmutableSortedMultiset(naturalOrder())));
Stream.of(2).collect(toImmutableSortedMultiset(naturalOrder())));
}
}

View File

@@ -56,7 +56,6 @@ final class ImmutableSortedSetTemplatesTest implements RefasterTemplateTestCase
ImmutableSet<ImmutableSortedSet<Integer>> testStreamToImmutableSortedSet() {
return ImmutableSet.of(
ImmutableSortedSet.copyOf(Stream.of(1).iterator()),
ImmutableSortedSet.copyOf(Stream.of(2)::iterator),
Stream.of(3).collect(collectingAndThen(toList(), ImmutableSortedSet::copyOf)));
Stream.of(2).collect(collectingAndThen(toList(), ImmutableSortedSet::copyOf)));
}
}

View File

@@ -53,7 +53,6 @@ final class ImmutableSortedSetTemplatesTest implements RefasterTemplateTestCase
ImmutableSet<ImmutableSortedSet<Integer>> testStreamToImmutableSortedSet() {
return ImmutableSet.of(
Stream.of(1).collect(toImmutableSortedSet(naturalOrder())),
Stream.of(2).collect(toImmutableSortedSet(naturalOrder())),
Stream.of(3).collect(toImmutableSortedSet(naturalOrder())));
Stream.of(2).collect(toImmutableSortedSet(naturalOrder())));
}
}

View File

@@ -87,18 +87,12 @@ final class IntStreamTemplatesTest implements RefasterTemplateTestCase {
IntStream.of(2).filter(n -> n > 2).findAny().isPresent());
}
ImmutableSet<Boolean> testIntStreamAllMatch() {
boolean testIntStreamAllMatch() {
IntPredicate pred = i -> i > 0;
return ImmutableSet.of(
IntStream.of(1).noneMatch(pred.negate()),
!IntStream.of(2).anyMatch(pred.negate()),
IntStream.of(3).filter(pred.negate()).findAny().isEmpty());
return IntStream.of(1).noneMatch(pred.negate());
}
ImmutableSet<Boolean> testIntStreamAllMatch2() {
return ImmutableSet.of(
IntStream.of(1).noneMatch(n -> !(n > 1)),
!IntStream.of(2).anyMatch(n -> !(n > 2)),
IntStream.of(3).filter(n -> !(n > 3)).findAny().isEmpty());
boolean testIntStreamAllMatch2() {
return IntStream.of(1).noneMatch(n -> !(n > 1));
}
}

View File

@@ -86,18 +86,12 @@ final class IntStreamTemplatesTest implements RefasterTemplateTestCase {
IntStream.of(1).anyMatch(n -> n > 1), IntStream.of(2).anyMatch(n -> n > 2));
}
ImmutableSet<Boolean> testIntStreamAllMatch() {
boolean testIntStreamAllMatch() {
IntPredicate pred = i -> i > 0;
return ImmutableSet.of(
IntStream.of(1).allMatch(pred),
IntStream.of(2).allMatch(pred),
IntStream.of(3).allMatch(pred));
return IntStream.of(1).allMatch(pred);
}
ImmutableSet<Boolean> testIntStreamAllMatch2() {
return ImmutableSet.of(
IntStream.of(1).allMatch(n -> n > 1),
IntStream.of(2).allMatch(n -> n > 2),
IntStream.of(3).allMatch(n -> n > 3));
boolean testIntStreamAllMatch2() {
return IntStream.of(1).allMatch(n -> n > 1);
}
}

View File

@@ -87,18 +87,12 @@ final class LongStreamTemplatesTest implements RefasterTemplateTestCase {
LongStream.of(2).filter(n -> n > 2).findAny().isPresent());
}
ImmutableSet<Boolean> testLongStreamAllMatch() {
boolean testLongStreamAllMatch() {
LongPredicate pred = i -> i > 0;
return ImmutableSet.of(
LongStream.of(1).noneMatch(pred.negate()),
!LongStream.of(2).anyMatch(pred.negate()),
LongStream.of(3).filter(pred.negate()).findAny().isEmpty());
return LongStream.of(1).noneMatch(pred.negate());
}
ImmutableSet<Boolean> testLongStreamAllMatch2() {
return ImmutableSet.of(
LongStream.of(1).noneMatch(n -> !(n > 1)),
!LongStream.of(2).anyMatch(n -> !(n > 2)),
LongStream.of(3).filter(n -> !(n > 3)).findAny().isEmpty());
boolean testLongStreamAllMatch2() {
return LongStream.of(1).noneMatch(n -> !(n > 1));
}
}

View File

@@ -86,18 +86,12 @@ final class LongStreamTemplatesTest implements RefasterTemplateTestCase {
LongStream.of(1).anyMatch(n -> n > 1), LongStream.of(2).anyMatch(n -> n > 2));
}
ImmutableSet<Boolean> testLongStreamAllMatch() {
boolean testLongStreamAllMatch() {
LongPredicate pred = i -> i > 0;
return ImmutableSet.of(
LongStream.of(1).allMatch(pred),
LongStream.of(2).allMatch(pred),
LongStream.of(3).allMatch(pred));
return LongStream.of(1).allMatch(pred);
}
ImmutableSet<Boolean> testLongStreamAllMatch2() {
return ImmutableSet.of(
LongStream.of(1).allMatch(n -> n > 1),
LongStream.of(2).allMatch(n -> n > 2),
LongStream.of(3).allMatch(n -> n > 3));
boolean testLongStreamAllMatch2() {
return LongStream.of(1).allMatch(n -> n > 1);
}
}

View File

@@ -5,6 +5,8 @@ import com.google.common.collect.Streams;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
final class OptionalTemplatesTest implements RefasterTemplateTestCase {
@Override
@@ -12,52 +14,65 @@ final class OptionalTemplatesTest implements RefasterTemplateTestCase {
return ImmutableSet.of(Streams.class);
}
ImmutableSet<Optional<String>> testOptionalOfNullable() {
// ImmutableSet<Optional<String>> testOptionalOfNullable() {
// return ImmutableSet.of(
// toString() == null ? Optional.empty() : Optional.of(toString()),
// toString() != null ? Optional.of(toString()) : Optional.empty());
// }
//
// ImmutableSet<Boolean> testOptionalIsEmpty() {
// return ImmutableSet.of(!Optional.empty().isPresent(), !Optional.of("foo").isPresent());
// }
//
// ImmutableSet<Boolean> testOptionalIsPresent() {
// return ImmutableSet.of(!Optional.empty().isEmpty(), !Optional.of("foo").isEmpty());
// }
//
// String testOptionalOrElseThrow() {
// return Optional.of("foo").get();
// }
//
// Function<Optional<Integer>, Integer> testOptionalOrElseThrowMethodReference() {
// return Optional::get;
// }
//
// ImmutableSet<Optional<String>> testOptionalFirstIteratorElement() {
// return ImmutableSet.of(
// ImmutableSet.of("foo").iterator().hasNext()
// ? Optional.of(ImmutableSet.of("foo").iterator().next())
// : Optional.empty(),
// !ImmutableSet.of("foo").iterator().hasNext()
// ? Optional.empty()
// : Optional.of(ImmutableSet.of("foo").iterator().next()));
// }
//
// ImmutableSet<Optional<String>> testTernaryOperatorOptionalPositiveFiltering() {
// return ImmutableSet.of(
// "foo".length() > 5 ? Optional.of("foo") : Optional.empty(),
// !"bar".contains("baz") ? Optional.of("bar") : Optional.empty());
// }
@Nullable String nullableObj;
@NonNull Object nonnullObj;
ImmutableSet<Optional<String>> testOptionalOfNullableFilterPositive(
@Nullable String nullableString) {
return ImmutableSet.of(
toString() == null ? Optional.empty() : Optional.of(toString()),
toString() != null ? Optional.of(toString()) : Optional.empty());
nullableObj.length() > 5 ? Optional.of(nullableObj) : Optional.empty(),
!nullableString.contains("baz") ? Optional.of(nullableString) : Optional.empty());
}
ImmutableSet<Boolean> testOptionalIsEmpty() {
return ImmutableSet.of(!Optional.empty().isPresent(), !Optional.of("foo").isPresent());
}
ImmutableSet<Boolean> testOptionalIsPresent() {
return ImmutableSet.of(!Optional.empty().isEmpty(), !Optional.of("foo").isEmpty());
}
String testOptionalOrElseThrow() {
return Optional.of("foo").get();
}
Function<Optional<Integer>, Integer> testOptionalOrElseThrowMethodReference() {
return Optional::get;
}
Stream<Object> testOptionalToStream() {
return Stream.concat(Streams.stream(Optional.empty()), Streams.stream(Optional.of("foo")));
}
ImmutableSet<Optional<String>> testOptionalFirstIteratorElement() {
ImmutableSet<Optional<String>> testTernaryOperatorOptionalNegativeFiltering(String testy) {
return ImmutableSet.of(
ImmutableSet.of("foo").iterator().hasNext()
? Optional.of(ImmutableSet.of("foo").iterator().next())
: Optional.empty(),
!ImmutableSet.of("foo").iterator().hasNext()
? Optional.empty()
: Optional.of(ImmutableSet.of("foo").iterator().next()));
testy.length() > 5 ? Optional.empty() : Optional.of(testy),
!testy.contains("baz") ? Optional.empty() : Optional.of(testy));
}
ImmutableSet<Optional<String>> testTernaryOperatorOptionalPositiveFiltering() {
ImmutableSet<Optional<String>> testOptionalOfNullableFilterNegative(
@Nullable String nullableString) {
return ImmutableSet.of(
"foo".length() > 5 ? Optional.of("foo") : Optional.empty(),
!"bar".contains("baz") ? Optional.of("bar") : Optional.empty());
}
ImmutableSet<Optional<String>> testTernaryOperatorOptionalNegativeFiltering() {
return ImmutableSet.of(
"foo".length() > 5 ? Optional.empty() : Optional.of("foo"),
!"bar".contains("baz") ? Optional.empty() : Optional.of("bar"));
nullableString.length() > 5 ? Optional.empty() : Optional.of(nullableString),
!nullableString.contains("baz") ? Optional.empty() : Optional.of(nullableString));
}
ImmutableSet<Boolean> testMapOptionalToBoolean() {

View File

@@ -34,10 +34,6 @@ final class OptionalTemplatesTest implements RefasterTemplateTestCase {
return Optional::orElseThrow;
}
Stream<Object> testOptionalToStream() {
return Stream.concat(Optional.empty().stream(), Optional.of("foo").stream());
}
ImmutableSet<Optional<String>> testOptionalFirstIteratorElement() {
return ImmutableSet.of(
stream(ImmutableSet.of("foo").iterator()).findFirst(),

View File

@@ -1,5 +1,6 @@
package tech.picnic.errorprone.bugpatterns;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.time.Duration;
import java.util.Optional;
@@ -24,16 +25,12 @@ final class ReactorTemplatesTest implements RefasterTemplateTestCase {
return Flux.defer(() -> Flux.error(new IllegalStateException()));
}
ImmutableSet<Mono<Void>> testMonoErrorSupplier() {
return ImmutableSet.of(
Mono.error(((Supplier<RuntimeException>) null)::get),
Mono.error(() -> ((Supplier<RuntimeException>) null).get()));
Mono<Void> testMonoErrorSupplier() {
return Mono.error(() -> ((Supplier<RuntimeException>) null).get());
}
ImmutableSet<Flux<Void>> testFluxErrorSupplier() {
return ImmutableSet.of(
Flux.error(((Supplier<RuntimeException>) null)::get),
Flux.error(() -> ((Supplier<RuntimeException>) null).get()));
Flux<Void> testFluxErrorSupplier() {
return Flux.error(() -> ((Supplier<RuntimeException>) null).get());
}
Mono<String> testMonoThenReturn() {
@@ -49,10 +46,27 @@ final class ReactorTemplatesTest implements RefasterTemplateTestCase {
Flux.just(1).switchIfEmpty(Mono.empty()), Flux.just(2).switchIfEmpty(Flux.empty()));
}
ImmutableSet<Flux<Integer>> testFluxConcatMap() {
return ImmutableSet.of(
Flux.just(1).flatMap(Mono::just, 1), Flux.just(2).flatMapSequential(Mono::just, 1));
}
Flux<Integer> testFluxConcatMapIterable() {
return Flux.just(1, 2).flatMapIterable(ImmutableList::of);
}
Flux<String> testMonoFlatMapToFlux() {
return Mono.just("foo").flatMapMany(s -> Mono.just(s + s));
}
Flux<String> testMonoFlux() {
return Flux.concat(Mono.just("foo"));
}
Flux<String> testFluxIdentity() {
return Flux.concat(Flux.just("foo"));
}
ImmutableSet<Mono<Optional<String>>> testMonoCollectToOptional() {
return ImmutableSet.of(
Mono.just("foo").map(Optional::of).defaultIfEmpty(Optional.empty()),
@@ -63,6 +77,14 @@ final class ReactorTemplatesTest implements RefasterTemplateTestCase {
return ImmutableSet.of(PublisherProbe.of(Mono.empty()), PublisherProbe.of(Flux.empty()));
}
StepVerifier.FirstStep<Integer> testStepVerifierFromMono() {
return StepVerifier.create(Mono.just(1));
}
StepVerifier.FirstStep<Integer> testStepVerifierFromFlux() {
return StepVerifier.create(Flux.just(1));
}
StepVerifier.Step<Integer> testStepVerifierStepExpectNextEmpty() {
return StepVerifier.create(Mono.just(0)).expectNext();
}

View File

@@ -2,6 +2,7 @@ package tech.picnic.errorprone.bugpatterns;
import static com.google.common.collect.MoreCollectors.toOptional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.time.Duration;
import java.util.Optional;
@@ -26,16 +27,12 @@ final class ReactorTemplatesTest implements RefasterTemplateTestCase {
return Flux.error(() -> new IllegalStateException());
}
ImmutableSet<Mono<Void>> testMonoErrorSupplier() {
return ImmutableSet.of(
Mono.error(((Supplier<RuntimeException>) null)),
Mono.error(((Supplier<RuntimeException>) null)));
Mono<Void> testMonoErrorSupplier() {
return Mono.error(((Supplier<RuntimeException>) null));
}
ImmutableSet<Flux<Void>> testFluxErrorSupplier() {
return ImmutableSet.of(
Flux.error(((Supplier<RuntimeException>) null)),
Flux.error(((Supplier<RuntimeException>) null)));
Flux<Void> testFluxErrorSupplier() {
return Flux.error(((Supplier<RuntimeException>) null));
}
Mono<String> testMonoThenReturn() {
@@ -50,10 +47,26 @@ final class ReactorTemplatesTest implements RefasterTemplateTestCase {
return ImmutableSet.of(Flux.just(1), Flux.just(2));
}
ImmutableSet<Flux<Integer>> testFluxConcatMap() {
return ImmutableSet.of(Flux.just(1).concatMap(Mono::just), Flux.just(2).concatMap(Mono::just));
}
Flux<Integer> testFluxConcatMapIterable() {
return Flux.just(1, 2).concatMapIterable(ImmutableList::of);
}
Flux<String> testMonoFlatMapToFlux() {
return Mono.just("foo").flatMap(s -> Mono.just(s + s)).flux();
}
Flux<String> testMonoFlux() {
return Mono.just("foo").flux();
}
Flux<String> testFluxIdentity() {
return Flux.just("foo");
}
ImmutableSet<Mono<Optional<String>>> testMonoCollectToOptional() {
return ImmutableSet.of(
Mono.just("foo").flux().collect(toOptional()),
@@ -64,6 +77,14 @@ final class ReactorTemplatesTest implements RefasterTemplateTestCase {
return ImmutableSet.of(PublisherProbe.empty(), PublisherProbe.empty());
}
StepVerifier.FirstStep<Integer> testStepVerifierFromMono() {
return Mono.just(1).as(StepVerifier::create);
}
StepVerifier.FirstStep<Integer> testStepVerifierFromFlux() {
return Flux.just(1).as(StepVerifier::create);
}
StepVerifier.Step<Integer> testStepVerifierStepExpectNextEmpty() {
return StepVerifier.create(Mono.just(0));
}

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