mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 08:11:25 +00:00
Compare commits
19 Commits
nkooij/web
...
rossendrij
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e952fc94c7 | ||
|
|
c8c896c9e4 | ||
|
|
3ba8a83cd1 | ||
|
|
0824a5254b | ||
|
|
72a8bf7e12 | ||
|
|
427435b3f1 | ||
|
|
432ce9bce0 | ||
|
|
19da6ac149 | ||
|
|
793a70c29b | ||
|
|
63b4fae185 | ||
|
|
73f8f056b4 | ||
|
|
c1638066cd | ||
|
|
08e99fb54e | ||
|
|
00012c6aa8 | ||
|
|
193589d193 | ||
|
|
1a1588d413 | ||
|
|
6656bd8186 | ||
|
|
26a9b46de7 | ||
|
|
17e74f778b |
@@ -56,6 +56,11 @@
|
||||
<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>
|
||||
@@ -76,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>
|
||||
@@ -186,6 +196,11 @@
|
||||
<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>
|
||||
<artifactId>testng</artifactId>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
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.fixes.SuggestedFix;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.google.errorprone.util.ASTHelpers.TargetType;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import java.util.List;
|
||||
|
||||
/** A {@link BugChecker} that flags redundant identity conversions. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "IdentityConversion",
|
||||
summary = "Avoid or clarify identity conversions",
|
||||
linkType = LinkType.NONE,
|
||||
severity = SeverityLevel.WARNING,
|
||||
tags = StandardTags.SIMPLIFICATION)
|
||||
public final class IdentityConversionCheck extends BugChecker
|
||||
implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> IS_CONVERSION_METHOD =
|
||||
anyOf(
|
||||
staticMethod()
|
||||
.onClassAny(
|
||||
"com.google.common.collect.ImmutableBiMap",
|
||||
"com.google.common.collect.ImmutableList",
|
||||
"com.google.common.collect.ImmutableListMultimap",
|
||||
"com.google.common.collect.ImmutableMap",
|
||||
"com.google.common.collect.ImmutableMultimap",
|
||||
"com.google.common.collect.ImmutableMultiset",
|
||||
"com.google.common.collect.ImmutableRangeMap",
|
||||
"com.google.common.collect.ImmutableRangeSet",
|
||||
"com.google.common.collect.ImmutableSet",
|
||||
"com.google.common.collect.ImmutableSetMultimap",
|
||||
"com.google.common.collect.ImmutableTable")
|
||||
.named("copyOf"),
|
||||
staticMethod()
|
||||
.onClassAny(
|
||||
Byte.class.getName(),
|
||||
Character.class.getName(),
|
||||
Double.class.getName(),
|
||||
Float.class.getName(),
|
||||
Integer.class.getName(),
|
||||
String.class.getName())
|
||||
.named("valueOf"),
|
||||
staticMethod().onClass("reactor.adapter.rxjava.RxJava2Adapter"),
|
||||
staticMethod()
|
||||
.onClass("reactor.core.publisher.Flux")
|
||||
.namedAnyOf("concat", "firstWithSignal", "from", "merge"),
|
||||
staticMethod().onClass("reactor.core.publisher.Mono").namedAnyOf("from", "fromDirect"));
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
List<? extends ExpressionTree> arguments = tree.getArguments();
|
||||
if (arguments.size() != 1 || !IS_CONVERSION_METHOD.matches(tree, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
ExpressionTree sourceTree = arguments.get(0);
|
||||
Type sourceType = ASTHelpers.getType(sourceTree);
|
||||
TargetType targetType = ASTHelpers.targetType(state);
|
||||
if (sourceType == null
|
||||
|| targetType == null
|
||||
|| !state.getTypes().isSubtype(sourceType, targetType.type())) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
return buildDescription(tree)
|
||||
.setMessage(
|
||||
"This method invocation appears redundant; remove it or suppress this warning and "
|
||||
+ "add an comment explaining its purpose")
|
||||
.addFix(SuggestedFix.replace(tree, state.getSourceForNode(sourceTree)))
|
||||
.addFix(SuggestedFixes.addSuppressWarnings(state, canonicalName()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -20,10 +20,16 @@ import com.google.errorprone.matchers.MultiMatcher;
|
||||
import com.google.errorprone.predicates.TypePredicate;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.CompilationUnitTree;
|
||||
import com.sun.source.tree.ImportTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.Name;
|
||||
|
||||
/** A {@link BugChecker} which flags non-canonical JUnit method declarations. */
|
||||
// XXX: Consider introducing a class-level check which enforces that test classes:
|
||||
@@ -78,24 +84,83 @@ public final class JUnitMethodDeclarationCheck extends BugChecker implements Met
|
||||
.ifPresent(builder::merge);
|
||||
|
||||
if (isTestMethod) {
|
||||
// XXX: In theory this rename could clash with an existing method or static import. In that
|
||||
// case we should emit a warning without a suggested replacement.
|
||||
tryCanonicalizeMethodName(tree, state).ifPresent(builder::merge);
|
||||
}
|
||||
Optional<String> newMethodName = tryCanonicalizeMethodName(tree);
|
||||
|
||||
if (newMethodName.isEmpty()) {
|
||||
return describeMatchesIfPresent(tree, builder);
|
||||
}
|
||||
|
||||
boolean reportedNameClash =
|
||||
reportDescriptionForPossibleNameClash(tree, newMethodName.orElseThrow(), state);
|
||||
if (!reportedNameClash) {
|
||||
builder.merge(SuggestedFixes.renameMethod(tree, newMethodName.orElseThrow(), state));
|
||||
}
|
||||
}
|
||||
return describeMatchesIfPresent(tree, builder);
|
||||
}
|
||||
|
||||
private Description describeMatchesIfPresent(MethodTree tree, SuggestedFix.Builder builder) {
|
||||
return builder.isEmpty() ? Description.NO_MATCH : describeMatch(tree, builder.build());
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix> tryCanonicalizeMethodName(
|
||||
MethodTree tree, VisitorState state) {
|
||||
private boolean reportDescriptionForPossibleNameClash(
|
||||
MethodTree tree, String methodName, VisitorState state) {
|
||||
if (isMethodNameInClass(methodName, state)) {
|
||||
state.reportMatch(
|
||||
buildDescription(tree)
|
||||
.setMessage(
|
||||
String.format("A method with name %s already exists in the class.", methodName))
|
||||
.build());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isMethodNameStaticallyImported(methodName, state)) {
|
||||
state.reportMatch(
|
||||
buildDescription(tree)
|
||||
.setMessage(
|
||||
String.format(
|
||||
"A method with name %s is already statically imported.", methodName))
|
||||
.build());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isMethodNameInClass(String methodName, VisitorState state) {
|
||||
return state.findEnclosing(ClassTree.class).getMembers().stream()
|
||||
.filter(MethodTree.class::isInstance)
|
||||
.map(MethodTree.class::cast)
|
||||
.filter(member -> !ASTHelpers.isGeneratedConstructor(member))
|
||||
.map(MethodTree::getName)
|
||||
.map(Name::toString)
|
||||
.anyMatch(methodName::equals);
|
||||
}
|
||||
|
||||
private static boolean isMethodNameStaticallyImported(String methodName, VisitorState state) {
|
||||
CompilationUnitTree compilationUnit = state.getPath().getCompilationUnit();
|
||||
|
||||
return compilationUnit.getImports().stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(ImportTree::isStatic)
|
||||
.map(ImportTree::getQualifiedIdentifier)
|
||||
.map(tree -> getStaticImportIdentifier(tree, state))
|
||||
.anyMatch(methodName::contentEquals);
|
||||
}
|
||||
|
||||
private static CharSequence getStaticImportIdentifier(Tree tree, VisitorState state) {
|
||||
String source = Util.treeToString(tree, state);
|
||||
return source.subSequence(source.lastIndexOf('.') + 1, source.length());
|
||||
}
|
||||
|
||||
private static Optional<String> tryCanonicalizeMethodName(MethodTree tree) {
|
||||
return Optional.ofNullable(ASTHelpers.getSymbol(tree))
|
||||
.map(sym -> sym.getQualifiedName().toString())
|
||||
.filter(name -> name.startsWith(TEST_PREFIX))
|
||||
.map(name -> name.substring(TEST_PREFIX.length()))
|
||||
.filter(not(String::isEmpty))
|
||||
.map(name -> Character.toLowerCase(name.charAt(0)) + name.substring(1))
|
||||
.filter(name -> !Character.isDigit(name.charAt(0)))
|
||||
.map(name -> SuggestedFixes.renameMethod(tree, name, state));
|
||||
.filter(name -> !Character.isDigit(name.charAt(0)));
|
||||
}
|
||||
|
||||
// XXX: Move to a `MoreMatchers` utility class.
|
||||
|
||||
@@ -37,7 +37,6 @@ import java.util.function.Function;
|
||||
*/
|
||||
// XXX: Add more documentation. Explain how this is useful in the face of refactoring to more
|
||||
// specific types.
|
||||
// XXX: Change this checker's name?
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
name = "PrimitiveComparison",
|
||||
@@ -77,23 +76,44 @@ public final class PrimitiveComparisonCheck extends BugChecker
|
||||
}
|
||||
|
||||
return getPotentiallyBoxedReturnType(tree.getArguments().get(0))
|
||||
.flatMap(cmpType -> tryFix(tree, state, cmpType, isStatic))
|
||||
.flatMap(cmpType -> tryMakeMethodCallMorePrecise(tree, cmpType, isStatic, state))
|
||||
.map(fix -> describeMatch(tree, fix))
|
||||
.orElse(Description.NO_MATCH);
|
||||
}
|
||||
|
||||
private static Optional<Fix> tryFix(
|
||||
MethodInvocationTree tree, VisitorState state, Type cmpType, boolean isStatic) {
|
||||
private static Optional<Fix> tryMakeMethodCallMorePrecise(
|
||||
MethodInvocationTree tree, Type cmpType, boolean isStatic, VisitorState state) {
|
||||
return Optional.ofNullable(ASTHelpers.getSymbol(tree))
|
||||
.map(methodSymbol -> methodSymbol.getSimpleName().toString())
|
||||
.flatMap(
|
||||
actualMethodName ->
|
||||
Optional.of(getPreferredMethod(state, cmpType, isStatic))
|
||||
Optional.of(getPreferredMethod(cmpType, isStatic, state))
|
||||
.filter(not(actualMethodName::equals)))
|
||||
.map(
|
||||
preferredMethodName ->
|
||||
prefixWithTypeArgumentsIfNeeded(preferredMethodName, tree, cmpType, state))
|
||||
.map(preferredMethodName -> suggestFix(tree, preferredMethodName, state));
|
||||
}
|
||||
|
||||
private static String getPreferredMethod(VisitorState state, Type cmpType, boolean isStatic) {
|
||||
private static String prefixWithTypeArgumentsIfNeeded(
|
||||
String preferredMethodName, MethodInvocationTree tree, Type cmpType, VisitorState state) {
|
||||
int typeArguments = tree.getTypeArguments().size();
|
||||
boolean methodNameIsComparing = preferredMethodName.equals("comparing");
|
||||
|
||||
if (typeArguments == 0 || (typeArguments == 1 && !methodNameIsComparing)) {
|
||||
return preferredMethodName;
|
||||
}
|
||||
|
||||
String optionalSecondTypeArgument =
|
||||
methodNameIsComparing ? ", " + cmpType.tsym.getSimpleName() : "";
|
||||
return String.format(
|
||||
"<%s%s>%s",
|
||||
Util.treeToString(tree.getTypeArguments().get(0), state),
|
||||
optionalSecondTypeArgument,
|
||||
preferredMethodName);
|
||||
}
|
||||
|
||||
private static String getPreferredMethod(Type cmpType, boolean isStatic, VisitorState state) {
|
||||
Types types = state.getTypes();
|
||||
Symtab symtab = state.getSymtab();
|
||||
|
||||
@@ -128,9 +148,6 @@ public final class PrimitiveComparisonCheck extends BugChecker
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: We drop explicitly specified generic type information. In case the number of type
|
||||
// arguments before and after doesn't match, that's for the better. But if we e.g. replace
|
||||
// `comparingLong` with `comparingInt`, then we should retain it.
|
||||
private static Fix suggestFix(
|
||||
MethodInvocationTree tree, String preferredMethodName, VisitorState state) {
|
||||
ExpressionTree expr = tree.getMethodSelect();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -19,14 +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 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`
|
||||
@@ -42,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",
|
||||
@@ -80,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",
|
||||
@@ -108,6 +119,7 @@ public final class StaticImportCheck extends BugChecker implements MemberSelectT
|
||||
.put("com.google.common.collect.ImmutableTable", "toImmutableTable")
|
||||
.put("com.google.common.collect.Sets", "toImmutableEnumSet")
|
||||
.put("com.google.common.base.Functions", "identity")
|
||||
.put("java.time.ZoneOffset", "UTC")
|
||||
.put("java.util.function.Function", "identity")
|
||||
.put("java.util.function.Predicate", "not")
|
||||
.put("org.junit.jupiter.params.provider.Arguments", "arguments")
|
||||
@@ -122,9 +134,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;
|
||||
}
|
||||
|
||||
@@ -139,7 +183,7 @@ 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 =
|
||||
requireNonNull(state.getPath().getParentPath(), "MemberSelectTree lacks enclosing node")
|
||||
.getLeaf();
|
||||
@@ -154,6 +198,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 +216,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(
|
||||
|
||||
@@ -8,7 +8,22 @@ import com.google.errorprone.refaster.annotation.AfterTemplate;
|
||||
import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
import java.math.BigDecimal;
|
||||
import org.assertj.core.api.AbstractBigDecimalAssert;
|
||||
import org.assertj.core.api.BigDecimalAssert;
|
||||
|
||||
/**
|
||||
* Refaster templates which improve {@link BigDecimal} asserts written in AssertJ.
|
||||
*
|
||||
* <p>A few {@link BigDecimal} assertions are not rewritten. This is because the {@link BigDecimal}
|
||||
* also uses scale to determine whether two values are equal. As a result, we cannot rewrite the
|
||||
* following assertions:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link BigDecimalAssert#isEqualTo(Object)} (for values 0L, 1L, BigDecimal.ZERO, and
|
||||
* BigDecimal.ONE)
|
||||
* <li>{@link BigDecimalAssert#isNotEqualTo(Object)} (for values 0L, 1L, BigDecimal.ZERO, and
|
||||
* BigDecimal.ONE)
|
||||
* </ul>
|
||||
*/
|
||||
// XXX: If we add a rule which drops unnecessary `L` suffixes from literal longs, then the `0L`/`1L`
|
||||
// cases below can go.
|
||||
final class AssertJBigDecimalTemplates {
|
||||
@@ -45,45 +60,36 @@ final class AssertJBigDecimalTemplates {
|
||||
static final class AbstractBigDecimalAssertIsZero {
|
||||
@BeforeTemplate
|
||||
AbstractBigDecimalAssert<?> before(AbstractBigDecimalAssert<?> bigDecimalAssert) {
|
||||
return Refaster.anyOf(
|
||||
bigDecimalAssert.isZero(),
|
||||
bigDecimalAssert.isEqualTo(0L),
|
||||
bigDecimalAssert.isEqualTo(BigDecimal.ZERO));
|
||||
return bigDecimalAssert.isEqualTo(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
AbstractBigDecimalAssert<?> after(AbstractBigDecimalAssert<?> bigDecimalAssert) {
|
||||
return bigDecimalAssert.isEqualTo(0);
|
||||
return bigDecimalAssert.isZero();
|
||||
}
|
||||
}
|
||||
|
||||
static final class AbstractBigDecimalAssertIsNotZero {
|
||||
@BeforeTemplate
|
||||
AbstractBigDecimalAssert<?> before(AbstractBigDecimalAssert<?> bigDecimalAssert) {
|
||||
return Refaster.anyOf(
|
||||
bigDecimalAssert.isNotZero(),
|
||||
bigDecimalAssert.isNotEqualTo(0L),
|
||||
bigDecimalAssert.isNotEqualTo(BigDecimal.ZERO));
|
||||
return bigDecimalAssert.isNotEqualTo(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
AbstractBigDecimalAssert<?> after(AbstractBigDecimalAssert<?> bigDecimalAssert) {
|
||||
return bigDecimalAssert.isNotEqualTo(0);
|
||||
return bigDecimalAssert.isNotZero();
|
||||
}
|
||||
}
|
||||
|
||||
static final class AbstractBigDecimalAssertIsOne {
|
||||
@BeforeTemplate
|
||||
AbstractBigDecimalAssert<?> before(AbstractBigDecimalAssert<?> bigDecimalAssert) {
|
||||
return Refaster.anyOf(
|
||||
bigDecimalAssert.isOne(),
|
||||
bigDecimalAssert.isEqualTo(1L),
|
||||
bigDecimalAssert.isEqualTo(BigDecimal.ONE));
|
||||
return bigDecimalAssert.isEqualTo(BigDecimal.ONE);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
AbstractBigDecimalAssert<?> after(AbstractBigDecimalAssert<?> bigDecimalAssert) {
|
||||
return bigDecimalAssert.isEqualTo(1);
|
||||
return bigDecimalAssert.isOne();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,6 +365,7 @@ final class AssertJTemplates {
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
|
||||
ObjectEnumerableAssert<?, S> after(Set<S> set, T element) {
|
||||
return assertThat(set).containsExactly(element);
|
||||
}
|
||||
|
||||
@@ -177,9 +177,7 @@ final class AssortedTemplates {
|
||||
@BeforeTemplate
|
||||
boolean before(Collection<T> collection1, Collection<T> collection2) {
|
||||
return Refaster.anyOf(
|
||||
Collections.disjoint(ImmutableSet.copyOf(collection1), collection2),
|
||||
Collections.disjoint(new HashSet<>(collection1), collection2),
|
||||
Collections.disjoint(collection1, ImmutableSet.copyOf(collection2)),
|
||||
Collections.disjoint(collection1, new HashSet<>(collection2)));
|
||||
}
|
||||
|
||||
|
||||
@@ -272,17 +272,4 @@ final class ImmutableListMultimapTemplates {
|
||||
return ImmutableListMultimap.copyOf(Multimaps.transformValues(multimap, transformation));
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily copy an {@link ImmutableListMultimap}. */
|
||||
static final class ImmutableListMultimapCopyOfImmutableListMultimap<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableListMultimap<K, V> before(ImmutableListMultimap<K, V> multimap) {
|
||||
return ImmutableListMultimap.copyOf(multimap);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableListMultimap<K, V> after(ImmutableListMultimap<K, V> multimap) {
|
||||
return multimap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ final class ImmutableListTemplates {
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableList.Builder<T> after() {
|
||||
return ImmutableList.builder();
|
||||
return ImmutableList.<T>builder();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,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.Collection;
|
||||
@@ -20,6 +22,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import tech.picnic.errorprone.refaster.util.IsIdentity;
|
||||
|
||||
/** Refaster templates related to expressions dealing with {@link ImmutableMap}s. */
|
||||
final class ImmutableMapTemplates {
|
||||
@@ -36,7 +39,7 @@ final class ImmutableMapTemplates {
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableMap.Builder<K, V> after() {
|
||||
return ImmutableMap.builder();
|
||||
return ImmutableMap.<K, V>builder();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,32 +97,35 @@ final class ImmutableMapTemplates {
|
||||
* Prefer {@link Maps#toMap(Iterable, com.google.common.base.Function)} over more contrived
|
||||
* alternatives.
|
||||
*/
|
||||
// XXX: Drop the `Refaster.anyOf` if/when we decide to rewrite one to the other.
|
||||
static final class IterableToImmutableMap<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(
|
||||
Iterator<K> iterable, Function<? super K, ? extends V> valueFunction) {
|
||||
Iterator<K> iterable,
|
||||
@NotMatches(IsIdentity.class) Function<? super K, ? extends V> valueFunction) {
|
||||
return Streams.stream(iterable)
|
||||
.collect(toImmutableMap(Refaster.anyOf(identity(), k -> k), valueFunction));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(
|
||||
Iterable<K> iterable, Function<? super K, ? extends V> valueFunction) {
|
||||
Iterable<K> iterable,
|
||||
@NotMatches(IsIdentity.class) Function<? super K, ? extends V> valueFunction) {
|
||||
return Streams.stream(iterable)
|
||||
.collect(toImmutableMap(Refaster.anyOf(identity(), k -> k), valueFunction));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(
|
||||
Collection<K> iterable, Function<? super K, ? extends V> valueFunction) {
|
||||
Collection<K> iterable,
|
||||
@NotMatches(IsIdentity.class) Function<? super K, ? extends V> valueFunction) {
|
||||
return iterable.stream()
|
||||
.collect(toImmutableMap(Refaster.anyOf(identity(), k -> k), valueFunction));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(
|
||||
Set<K> iterable, com.google.common.base.Function<? super K, V> valueFunction) {
|
||||
Set<K> iterable,
|
||||
@NotMatches(IsIdentity.class) com.google.common.base.Function<? super K, V> valueFunction) {
|
||||
return ImmutableMap.copyOf(Maps.asMap(iterable, valueFunction));
|
||||
}
|
||||
|
||||
@@ -130,6 +136,49 @@ final class ImmutableMapTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Maps#toMap(Iterable, com.google.common.base.Function)} over more contrived
|
||||
* alternatives and rewrite {@link Function#identity()} to {@code k -> k}.
|
||||
*/
|
||||
static final class IterableToImmutableMapIdentity<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(
|
||||
Iterator<K> iterable,
|
||||
@Matches(IsIdentity.class) Function<? super K, ? extends V> valueFunction) {
|
||||
return Streams.stream(iterable)
|
||||
.collect(toImmutableMap(Refaster.anyOf(identity(), k -> k), valueFunction));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(
|
||||
Iterable<K> iterable,
|
||||
@Matches(IsIdentity.class) Function<? super K, ? extends V> valueFunction) {
|
||||
return Streams.stream(iterable)
|
||||
.collect(toImmutableMap(Refaster.anyOf(identity(), k -> k), valueFunction));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(
|
||||
Collection<K> iterable,
|
||||
@Matches(IsIdentity.class) Function<? super K, ? extends V> valueFunction) {
|
||||
return iterable.stream()
|
||||
.collect(toImmutableMap(Refaster.anyOf(identity(), k -> k), valueFunction));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(
|
||||
Set<K> iterable,
|
||||
@Matches(IsIdentity.class) com.google.common.base.Function<? super K, V> valueFunction) {
|
||||
return ImmutableMap.copyOf(Maps.asMap(iterable, valueFunction));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableMap<K, K> after(
|
||||
Iterable<K> iterable, com.google.common.base.Function<? super K, V> valueFunction) {
|
||||
return Maps.toMap(iterable, k -> k);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link ImmutableMap#copyOf(Iterable)} over more contrived alternatives. */
|
||||
static final class EntryIterableToImmutableMap<K, V> {
|
||||
@BeforeTemplate
|
||||
@@ -188,23 +237,27 @@ final class ImmutableMapTemplates {
|
||||
* Prefer {@link Maps#uniqueIndex(Iterable, com.google.common.base.Function)} over the
|
||||
* stream-based alternative.
|
||||
*/
|
||||
// XXX: Drop the `Refaster.anyOf` if/when we decide to rewrite one to the other.
|
||||
static final class IndexIterableToImmutableMap<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(Iterator<V> iterable, Function<? super V, ? extends K> keyFunction) {
|
||||
return Streams.stream(iterable)
|
||||
.collect(toImmutableMap(keyFunction, Refaster.anyOf(identity(), v -> v)));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(Iterable<V> iterable, Function<? super V, ? extends K> keyFunction) {
|
||||
ImmutableMap<K, V> before(
|
||||
Iterator<V> iterable,
|
||||
@NotMatches(IsIdentity.class) Function<? super V, ? extends K> keyFunction) {
|
||||
return Streams.stream(iterable)
|
||||
.collect(toImmutableMap(keyFunction, Refaster.anyOf(identity(), v -> v)));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(
|
||||
Collection<V> iterable, Function<? super V, ? extends K> keyFunction) {
|
||||
Iterable<V> iterable,
|
||||
@NotMatches(IsIdentity.class) Function<? super V, ? extends K> keyFunction) {
|
||||
return Streams.stream(iterable)
|
||||
.collect(toImmutableMap(keyFunction, Refaster.anyOf(identity(), v -> v)));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(
|
||||
Collection<V> iterable,
|
||||
@NotMatches(IsIdentity.class) Function<? super V, ? extends K> keyFunction) {
|
||||
return iterable.stream()
|
||||
.collect(toImmutableMap(keyFunction, Refaster.anyOf(identity(), v -> v)));
|
||||
}
|
||||
@@ -216,6 +269,42 @@ final class ImmutableMapTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link Maps#uniqueIndex(Iterable, com.google.common.base.Function)} over the
|
||||
* stream-based alternatives and rewrite {@link Function#identity()} to {@code k -> k}.
|
||||
*/
|
||||
static final class IndexIterableToImmutableMapIdentity<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(
|
||||
Iterator<V> iterable,
|
||||
@Matches(IsIdentity.class) Function<? super V, ? extends K> keyFunction) {
|
||||
return Streams.stream(iterable)
|
||||
.collect(toImmutableMap(keyFunction, Refaster.anyOf(identity(), v -> v)));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(
|
||||
Iterable<V> iterable,
|
||||
@Matches(IsIdentity.class) Function<? super V, ? extends K> keyFunction) {
|
||||
return Streams.stream(iterable)
|
||||
.collect(toImmutableMap(keyFunction, Refaster.anyOf(identity(), v -> v)));
|
||||
}
|
||||
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(
|
||||
Collection<V> iterable,
|
||||
@Matches(IsIdentity.class) Function<? super V, ? extends K> keyFunction) {
|
||||
return iterable.stream()
|
||||
.collect(toImmutableMap(keyFunction, Refaster.anyOf(identity(), v -> v)));
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableMap<V, V> after(
|
||||
Iterable<V> iterable, com.google.common.base.Function<? super V, K> keyFunction) {
|
||||
return Maps.uniqueIndex(iterable, v -> v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer creating an immutable copy of the result of {@link Maps#transformValues(Map,
|
||||
* com.google.common.base.Function)} over more contrived alternatives.
|
||||
@@ -242,19 +331,6 @@ final class ImmutableMapTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily copy an {@link ImmutableMap}. */
|
||||
static final class ImmutableMapCopyOfImmutableMap<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableMap<K, V> before(ImmutableMap<K, V> map) {
|
||||
return ImmutableMap.copyOf(map);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableMap<K, V> after(ImmutableMap<K, V> map) {
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Add a template for this:
|
||||
// Maps.transformValues(streamOfEntries.collect(groupBy(fun)), ImmutableMap::copyOf)
|
||||
// ->
|
||||
|
||||
@@ -101,17 +101,4 @@ final class ImmutableMultisetTemplates {
|
||||
return stream.collect(toImmutableMultiset());
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily copy an {@link ImmutableMultiset}. */
|
||||
static final class ImmutableMultisetCopyOfImmutableMultiset<T> {
|
||||
@BeforeTemplate
|
||||
ImmutableMultiset<T> before(ImmutableMultiset<T> multiset) {
|
||||
return ImmutableMultiset.copyOf(multiset);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableMultiset<T> after(ImmutableMultiset<T> multiset) {
|
||||
return multiset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ final class ImmutableSetMultimapTemplates {
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableSetMultimap.Builder<K, V> after() {
|
||||
return ImmutableSetMultimap.builder();
|
||||
return ImmutableSetMultimap.<K, V>builder();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,17 +215,4 @@ final class ImmutableSetMultimapTemplates {
|
||||
return ImmutableSetMultimap.copyOf(Multimaps.transformValues(multimap, transformation));
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily copy an {@link ImmutableSetMultimap}. */
|
||||
static final class ImmutableSetMultimapCopyOfImmutableSetMultimap<K, V> {
|
||||
@BeforeTemplate
|
||||
ImmutableSetMultimap<K, V> before(ImmutableSetMultimap<K, V> multimap) {
|
||||
return ImmutableSetMultimap.copyOf(multimap);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableSetMultimap<K, V> after(ImmutableSetMultimap<K, V> multimap) {
|
||||
return multimap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ final class ImmutableSetTemplates {
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableSet.Builder<T> after() {
|
||||
return ImmutableSet.builder();
|
||||
return ImmutableSet.<T>builder();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,19 +126,6 @@ final class ImmutableSetTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
/** Don't unnecessarily copy an {@link ImmutableSet}. */
|
||||
static final class ImmutableSetCopyOfImmutableSet<T> {
|
||||
@BeforeTemplate
|
||||
ImmutableSet<T> before(ImmutableSet<T> set) {
|
||||
return ImmutableSet.copyOf(set);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
ImmutableSet<T> after(ImmutableSet<T> set) {
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link SetView#immutableCopy()} over the more verbose alternative. */
|
||||
static final class ImmutableSetCopyOfSetView<T> {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Refaster templates related to expressions dealing with {@link Optional}s. */
|
||||
final class OptionalTemplates {
|
||||
@@ -24,7 +25,7 @@ final class OptionalTemplates {
|
||||
// parentheses around the null check, but that's currently not the case. Try to fix that.
|
||||
@BeforeTemplate
|
||||
@SuppressWarnings("TernaryOperatorOptionalNegativeFiltering" /* Special case. */)
|
||||
Optional<T> before(T object) {
|
||||
Optional<T> before(@Nullable T object) {
|
||||
return object == null ? Optional.empty() : Optional.of(object);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,19 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer a collection using {@link MoreCollectors#toOptional()} over more contrived alternatives.
|
||||
*/
|
||||
@@ -194,6 +236,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
|
||||
|
||||
@@ -41,11 +41,9 @@ final class RxJava2AdapterTemplates {
|
||||
@BeforeTemplate
|
||||
Publisher<T> before(Flowable<T> flowable) {
|
||||
return Refaster.anyOf(
|
||||
Flux.from(flowable),
|
||||
flowable.compose(Flux::from),
|
||||
flowable.to(Flux::from),
|
||||
flowable.as(Flux::from),
|
||||
RxJava2Adapter.flowableToFlux(flowable),
|
||||
flowable.compose(RxJava2Adapter::flowableToFlux),
|
||||
flowable.to(RxJava2Adapter::flowableToFlux));
|
||||
}
|
||||
@@ -67,7 +65,6 @@ final class RxJava2AdapterTemplates {
|
||||
Flowable.fromPublisher(flux),
|
||||
flux.transform(Flowable::fromPublisher),
|
||||
flux.as(Flowable::fromPublisher),
|
||||
RxJava2Adapter.fluxToFlowable(flux),
|
||||
flux.transform(RxJava2Adapter::fluxToFlowable));
|
||||
}
|
||||
|
||||
@@ -140,7 +137,6 @@ final class RxJava2AdapterTemplates {
|
||||
Flowable.fromPublisher(mono),
|
||||
mono.transform(Flowable::fromPublisher),
|
||||
mono.as(Flowable::fromPublisher),
|
||||
RxJava2Adapter.monoToFlowable(mono),
|
||||
mono.transform(RxJava2Adapter::monoToFlowable));
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.google.errorprone.refaster.annotation.BeforeTemplate;
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,19 @@ final class TimeTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Instant#atOffset(ZoneOffset)} over the more verbose alternative. */
|
||||
static final class InstantAtOffset {
|
||||
@BeforeTemplate
|
||||
OffsetDateTime before(Instant instant, ZoneOffset zoneOffset) {
|
||||
return OffsetDateTime.ofInstant(instant, zoneOffset);
|
||||
}
|
||||
|
||||
@AfterTemplate
|
||||
OffsetDateTime after(Instant instant, ZoneOffset zoneOffset) {
|
||||
return instant.atOffset(zoneOffset);
|
||||
}
|
||||
}
|
||||
|
||||
/** Use {@link Clock#systemUTC()} when possible. */
|
||||
static final class UtcClock {
|
||||
@BeforeTemplate
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class IdentityConversionCheckTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(IdentityConversionCheck.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(IdentityConversionCheck.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"Foo.java",
|
||||
"import com.google.common.collect.ImmutableBiMap;",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableListMultimap;",
|
||||
"import com.google.common.collect.ImmutableMap;",
|
||||
"import com.google.common.collect.ImmutableMultimap;",
|
||||
"import com.google.common.collect.ImmutableMultiset;",
|
||||
"import com.google.common.collect.ImmutableRangeMap;",
|
||||
"import com.google.common.collect.ImmutableRangeSet;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import com.google.common.collect.ImmutableSetMultimap;",
|
||||
"import com.google.common.collect.ImmutableTable;",
|
||||
"import reactor.adapter.rxjava.RxJava2Adapter;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
"",
|
||||
"public final class Foo {",
|
||||
" public void foo() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Byte b1 = Byte.valueOf((Byte) Byte.MIN_VALUE);",
|
||||
" Byte b2 = Byte.valueOf(Byte.MIN_VALUE);",
|
||||
" byte b3 = Byte.valueOf((Byte) Byte.MIN_VALUE);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" byte b4 = Byte.valueOf(Byte.MIN_VALUE);",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Character c1 = Character.valueOf((Character) 'a');",
|
||||
" Character c2 = Character.valueOf('a');",
|
||||
" char c3 = Character.valueOf((Character)'a');",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" char c4 = Character.valueOf('a');",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Integer int1 = Integer.valueOf((Integer) 1);",
|
||||
" Integer int2 = Integer.valueOf(1);",
|
||||
" int int3 = Integer.valueOf((Integer) 1);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" int int4 = Integer.valueOf(1);",
|
||||
"",
|
||||
" String s1 = String.valueOf(0);",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" String s2 = String.valueOf(\"1\");",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableBiMap<Object, Object> i2 = ImmutableBiMap.copyOf(ImmutableBiMap.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableList<Object> i3 = ImmutableList.copyOf(ImmutableList.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableListMultimap<Object, Object> i4 = ImmutableListMultimap.copyOf(ImmutableListMultimap.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableMap<Object, Object> i5 = ImmutableMap.copyOf(ImmutableMap.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableMultimap<Object, Object> i6 = ImmutableMultimap.copyOf(ImmutableMultimap.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableMultiset<Object> i7 = ImmutableMultiset.copyOf(ImmutableMultiset.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableRangeMap<String, Object> i8 = ImmutableRangeMap.copyOf(ImmutableRangeMap.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableRangeSet<String> i9 = ImmutableRangeSet.copyOf(ImmutableRangeSet.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableSet<Object> i10 = ImmutableSet.copyOf(ImmutableSet.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableSetMultimap<Object, Object> i11 = ImmutableSetMultimap.copyOf(ImmutableSetMultimap.of());",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" ImmutableTable<Object, Object, Object> i15 = ImmutableTable.copyOf(ImmutableTable.of());",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux<Integer> f1 = Flux.just(1).flatMap(e -> RxJava2Adapter.fluxToFlowable(Flux.just(2)));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux<Integer> f2 = Flux.concat(Flux.just(1));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux<Integer> f3 = Flux.firstWithSignal(Flux.just(1));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux<Integer> f4 = Flux.from(Flux.just(1));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Flux<Integer> f5 = Flux.merge(Flux.just(1));",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Mono<Integer> m1 = Mono.from(Mono.just(1));",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" Mono<Integer> m2 = Mono.fromDirect(Mono.just(1));",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacementFirstSuggestedFix() {
|
||||
refactoringTestHelper
|
||||
.setFixChooser(FixChoosers.FIRST)
|
||||
.addInputLines(
|
||||
"Foo.java",
|
||||
"import com.google.common.collect.ImmutableCollection;",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import java.util.Collection;",
|
||||
"import java.util.ArrayList;",
|
||||
"import org.reactivestreams.Publisher;",
|
||||
"import reactor.adapter.rxjava.RxJava2Adapter;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
"",
|
||||
"public final class Foo {",
|
||||
" public void foo() {",
|
||||
" ImmutableSet<Object> set1 = ImmutableSet.copyOf(ImmutableSet.of());",
|
||||
" ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());",
|
||||
"",
|
||||
" ImmutableCollection<Integer> list1 = ImmutableList.copyOf(ImmutableList.of(1));",
|
||||
" ImmutableCollection<Integer> list2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));",
|
||||
"",
|
||||
" Collection<Integer> c1 = ImmutableSet.copyOf(ImmutableSet.of(1));",
|
||||
" Collection<Integer> c2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));",
|
||||
"",
|
||||
" Flux<Integer> f1 = Flux.just(1).flatMap(e -> RxJava2Adapter.fluxToFlowable(Flux.just(2)));",
|
||||
" Flux<Integer> f2 = Flux.concat(Flux.just(3));",
|
||||
" Publisher<Integer> f3 = Flux.firstWithSignal(Flux.just(4));",
|
||||
" Publisher<Integer> f4 = Flux.from(Flux.just(5));",
|
||||
" Publisher<Integer> f5 = Flux.merge(Flux.just(6));",
|
||||
"",
|
||||
" Mono<Integer> m1 = Mono.from(Mono.just(7));",
|
||||
" Publisher<Integer> m2 = Mono.fromDirect(Mono.just(8));",
|
||||
"",
|
||||
" bar(Flux.concat(Flux.just(9)));",
|
||||
" bar(Mono.from(Mono.just(10)));",
|
||||
" }",
|
||||
"",
|
||||
" void bar(Publisher<Integer> publisher) {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"Foo.java",
|
||||
"import com.google.common.collect.ImmutableCollection;",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import java.util.Collection;",
|
||||
"import java.util.ArrayList;",
|
||||
"import org.reactivestreams.Publisher;",
|
||||
"import reactor.adapter.rxjava.RxJava2Adapter;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
"",
|
||||
"public final class Foo {",
|
||||
" public void foo() {",
|
||||
" ImmutableSet<Object> set1 = ImmutableSet.of();",
|
||||
" ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());",
|
||||
"",
|
||||
" ImmutableCollection<Integer> list1 = ImmutableList.of(1);",
|
||||
" ImmutableCollection<Integer> list2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));",
|
||||
"",
|
||||
" Collection<Integer> c1 = ImmutableSet.of(1);",
|
||||
" Collection<Integer> c2 = new ArrayList<>(ImmutableList.of(1));",
|
||||
"",
|
||||
" Flux<Integer> f1 = Flux.just(1).flatMap(e -> Flux.just(2));",
|
||||
" Flux<Integer> f2 = Flux.just(3);",
|
||||
" Publisher<Integer> f3 = Flux.just(4);",
|
||||
" Publisher<Integer> f4 = Flux.just(5);",
|
||||
" Publisher<Integer> f5 = Flux.just(6);",
|
||||
"",
|
||||
" Mono<Integer> m1 = Mono.just(7);",
|
||||
" Publisher<Integer> m2 = Mono.just(8);",
|
||||
"",
|
||||
" bar(Flux.just(9));",
|
||||
" bar(Mono.just(10));",
|
||||
" }",
|
||||
"",
|
||||
" void bar(Publisher<Integer> publisher) {}",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacementSecondSuggestedFix() {
|
||||
refactoringTestHelper
|
||||
.setFixChooser(FixChoosers.SECOND)
|
||||
.addInputLines(
|
||||
"Foo.java",
|
||||
"import com.google.common.collect.ImmutableCollection;",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import java.util.ArrayList;",
|
||||
"import reactor.adapter.rxjava.RxJava2Adapter;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
"",
|
||||
"public final class Foo {",
|
||||
" public void foo() {",
|
||||
" ImmutableSet<Object> set1 = ImmutableSet.copyOf(ImmutableSet.of());",
|
||||
" ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());",
|
||||
"",
|
||||
" ImmutableCollection<Integer> list1 = ImmutableList.copyOf(ImmutableList.of(1));",
|
||||
" ImmutableCollection<Integer> list2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"Foo.java",
|
||||
"import com.google.common.collect.ImmutableCollection;",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import java.util.ArrayList;",
|
||||
"import reactor.adapter.rxjava.RxJava2Adapter;",
|
||||
"import reactor.core.publisher.Flux;",
|
||||
"import reactor.core.publisher.Mono;",
|
||||
"",
|
||||
"public final class Foo {",
|
||||
" public void foo() {",
|
||||
" @SuppressWarnings(\"IdentityConversion\")",
|
||||
" ImmutableSet<Object> set1 = ImmutableSet.copyOf(ImmutableSet.of());",
|
||||
" ImmutableSet<Object> set2 = ImmutableSet.copyOf(ImmutableList.of());",
|
||||
"",
|
||||
" @SuppressWarnings(\"IdentityConversion\")",
|
||||
" ImmutableCollection<Integer> list1 = ImmutableList.copyOf(ImmutableList.of(1));",
|
||||
" ImmutableCollection<Integer> list2 = ImmutableList.copyOf(new ArrayList<>(ImmutableList.of(1)));",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -177,4 +177,94 @@ public final class JUnitMethodDeclarationCheckTest {
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodAlreadyExistsInClass() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.junit.jupiter.api.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" @Test void testFoo() {}",
|
||||
" void foo() {}",
|
||||
"",
|
||||
" @Test void testBar() {}",
|
||||
" private void bar() {}",
|
||||
"",
|
||||
" @Test void testFooDifferent() {}",
|
||||
" @Test void testBarDifferent() {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import org.junit.jupiter.api.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" @Test void testFoo() {}",
|
||||
" void foo() {}",
|
||||
"",
|
||||
" @Test void testBar() {}",
|
||||
" private void bar() {}",
|
||||
"",
|
||||
" @Test void fooDifferent() {}",
|
||||
" @Test void barDifferent() {}",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodAlreadyInStaticImports() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import static com.google.common.collect.ImmutableSet.toImmutableSet;",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import org.junit.jupiter.api.Test;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"",
|
||||
"class A {",
|
||||
" @Test",
|
||||
" void testArguments() {",
|
||||
" arguments(1, 2, 3);",
|
||||
" }",
|
||||
"",
|
||||
" @Test",
|
||||
" void testToImmutableSet() {",
|
||||
" ImmutableSet.of(1).stream().filter(i -> i > 1).collect(toImmutableSet());",
|
||||
" }",
|
||||
"",
|
||||
" @Test",
|
||||
" void testArgumentsDifferentName() {}",
|
||||
"",
|
||||
" @Test",
|
||||
" void testToImmutableSetDifferentName() {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import static com.google.common.collect.ImmutableSet.toImmutableSet;",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import org.junit.jupiter.api.Test;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"",
|
||||
"class A {",
|
||||
" @Test",
|
||||
" void testArguments() {",
|
||||
" arguments(1, 2, 3);",
|
||||
" }",
|
||||
"",
|
||||
" @Test",
|
||||
" void testToImmutableSet() {",
|
||||
" ImmutableSet.of(1).stream().filter(i -> i > 1).collect(toImmutableSet());",
|
||||
" }",
|
||||
"",
|
||||
" @Test",
|
||||
" void argumentsDifferentName() {}",
|
||||
"",
|
||||
" @Test",
|
||||
" void toImmutableSetDifferentName() {}",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,8 +426,6 @@ public final class PrimitiveComparisonCheckTest {
|
||||
.doTest();
|
||||
}
|
||||
|
||||
// XXX: If the explicit `<A, BoxedPrimitive>` generic type information was necessary, then this
|
||||
// replacement drops too much information.
|
||||
@Test
|
||||
void replacementWithPrimitiveVariants() {
|
||||
refactoringTestHelper
|
||||
@@ -459,13 +457,13 @@ public final class PrimitiveComparisonCheckTest {
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
" Comparator<A> bCmp = Comparator.comparingInt(o -> (byte) 0);",
|
||||
" Comparator<A> cCmp = Comparator.comparingInt(o -> (char) 0);",
|
||||
" Comparator<A> sCmp = Comparator.comparingInt(o -> (short) 0);",
|
||||
" Comparator<A> iCmp = Comparator.comparingInt(o -> 0);",
|
||||
" Comparator<A> lCmp = Comparator.comparingLong(o -> 0L);",
|
||||
" Comparator<A> fCmp = Comparator.comparingDouble(o -> 0.0f);",
|
||||
" Comparator<A> dCmp = Comparator.comparingDouble(o -> 0.0);",
|
||||
" Comparator<A> bCmp = Comparator.<A>comparingInt(o -> (byte) 0);",
|
||||
" Comparator<A> cCmp = Comparator.<A>comparingInt(o -> (char) 0);",
|
||||
" Comparator<A> sCmp = Comparator.<A>comparingInt(o -> (short) 0);",
|
||||
" Comparator<A> iCmp = Comparator.<A>comparingInt(o -> 0);",
|
||||
" Comparator<A> lCmp = Comparator.<A>comparingLong(o -> 0L);",
|
||||
" Comparator<A> fCmp = Comparator.<A>comparingDouble(o -> 0.0f);",
|
||||
" Comparator<A> dCmp = Comparator.<A>comparingDouble(o -> 0.0);",
|
||||
"",
|
||||
" default void m() {",
|
||||
" bCmp.thenComparingInt(o -> (byte) 0);",
|
||||
@@ -480,8 +478,6 @@ public final class PrimitiveComparisonCheckTest {
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
// XXX: If the explicit `<A>` generic type information was necessary, then this replacement drops
|
||||
// too much information.
|
||||
@Test
|
||||
void replacementWithBoxedVariants() {
|
||||
refactoringTestHelper
|
||||
@@ -513,13 +509,13 @@ public final class PrimitiveComparisonCheckTest {
|
||||
"import java.util.Comparator;",
|
||||
"",
|
||||
"interface A extends Comparable<A> {",
|
||||
" Comparator<A> bCmp = Comparator.comparing(o -> Byte.valueOf((byte) 0));",
|
||||
" Comparator<A> cCmp = Comparator.comparing(o -> Character.valueOf((char) 0));",
|
||||
" Comparator<A> sCmp = Comparator.comparing(o -> Short.valueOf((short) 0));",
|
||||
" Comparator<A> iCmp = Comparator.comparing(o -> Integer.valueOf(0));",
|
||||
" Comparator<A> lCmp = Comparator.comparing(o -> Long.valueOf(0));",
|
||||
" Comparator<A> fCmp = Comparator.comparing(o -> Float.valueOf(0));",
|
||||
" Comparator<A> dCmp = Comparator.comparing(o -> Double.valueOf(0));",
|
||||
" Comparator<A> bCmp = Comparator.<A, Byte>comparing(o -> Byte.valueOf((byte) 0));",
|
||||
" Comparator<A> cCmp = Comparator.<A, Character>comparing(o -> Character.valueOf((char) 0));",
|
||||
" Comparator<A> sCmp = Comparator.<A, Short>comparing(o -> Short.valueOf((short) 0));",
|
||||
" Comparator<A> iCmp = Comparator.<A, Integer>comparing(o -> Integer.valueOf(0));",
|
||||
" Comparator<A> lCmp = Comparator.<A, Long>comparing(o -> Long.valueOf(0));",
|
||||
" Comparator<A> fCmp = Comparator.<A, Float>comparing(o -> Float.valueOf(0));",
|
||||
" Comparator<A> dCmp = Comparator.<A, Double>comparing(o -> Double.valueOf(0));",
|
||||
"",
|
||||
" default void m() {",
|
||||
" bCmp.thenComparing(o -> Byte.valueOf((byte) 0));",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -14,8 +15,20 @@ public final class StaticImportCheckTest {
|
||||
|
||||
@Test
|
||||
void candidateMethodsAreNotRedundant() {
|
||||
assertThat(StaticImportCheck.STATIC_IMPORT_CANDIDATE_METHODS.keySet())
|
||||
.doesNotContainAnyElementsOf(StaticImportCheck.STATIC_IMPORT_CANDIDATE_CLASSES);
|
||||
assertThat(StaticImportCheck.STATIC_IMPORT_CANDIDATE_MEMBERS.keySet())
|
||||
.doesNotContainAnyElementsOf(StaticImportCheck.STATIC_IMPORT_CANDIDATE_TYPES);
|
||||
}
|
||||
|
||||
@Test
|
||||
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
|
||||
@@ -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();",
|
||||
" }",
|
||||
"",
|
||||
@@ -87,8 +113,14 @@ public final class StaticImportCheckTest {
|
||||
"import com.google.common.collect.ImmutableMap;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import java.nio.charset.StandardCharsets;",
|
||||
"import java.time.ZoneOffset;",
|
||||
"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 +133,20 @@ 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\"));",
|
||||
"",
|
||||
" ZoneOffset z1 = ZoneOffset.UTC;",
|
||||
" ZoneOffset z2 = ZoneOffset.MIN;",
|
||||
" }",
|
||||
"",
|
||||
" void m2(",
|
||||
@@ -113,23 +158,38 @@ 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.time.ZoneOffset.UTC;",
|
||||
"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.time.ZoneOffset;",
|
||||
"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 +202,20 @@ 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\"));",
|
||||
"",
|
||||
" ZoneOffset z1 = UTC;",
|
||||
" ZoneOffset z2 = ZoneOffset.MIN;",
|
||||
" }",
|
||||
"",
|
||||
" void m2(",
|
||||
@@ -154,7 +227,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,24 +26,15 @@ final class AssertJBigDecimalTemplatesTest implements RefasterTemplateTestCase {
|
||||
assertThat(BigDecimal.ZERO).isNotCloseTo(BigDecimal.ONE, withPercentage(0)));
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractBigDecimalAssert<?>> testAbstractBigDecimalAssertIsZero() {
|
||||
return ImmutableSet.of(
|
||||
assertThat(BigDecimal.ZERO).isZero(),
|
||||
assertThat(BigDecimal.ZERO).isEqualTo(0L),
|
||||
assertThat(BigDecimal.ZERO).isEqualTo(BigDecimal.ZERO));
|
||||
AbstractBigDecimalAssert<?> testAbstractBigDecimalAssertIsZero() {
|
||||
return assertThat(BigDecimal.ZERO).isEqualTo(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractBigDecimalAssert<?>> testAbstractBigDecimalAssertIsNotZero() {
|
||||
return ImmutableSet.of(
|
||||
assertThat(BigDecimal.ZERO).isNotZero(),
|
||||
assertThat(BigDecimal.ZERO).isNotEqualTo(0L),
|
||||
assertThat(BigDecimal.ZERO).isNotEqualTo(BigDecimal.ZERO));
|
||||
AbstractBigDecimalAssert<?> testAbstractBigDecimalAssertIsNotZero() {
|
||||
return assertThat(BigDecimal.ZERO).isNotEqualTo(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractBigDecimalAssert<?>> testAbstractBigDecimalAssertIsOne() {
|
||||
return ImmutableSet.of(
|
||||
assertThat(BigDecimal.ZERO).isOne(),
|
||||
assertThat(BigDecimal.ZERO).isEqualTo(1L),
|
||||
assertThat(BigDecimal.ZERO).isEqualTo(BigDecimal.ONE));
|
||||
AbstractBigDecimalAssert<?> testAbstractBigDecimalAssertIsOne() {
|
||||
return assertThat(BigDecimal.ZERO).isEqualTo(BigDecimal.ONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,24 +26,15 @@ final class AssertJBigDecimalTemplatesTest implements RefasterTemplateTestCase {
|
||||
assertThat(BigDecimal.ZERO).isNotEqualTo(BigDecimal.ONE));
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractBigDecimalAssert<?>> testAbstractBigDecimalAssertIsZero() {
|
||||
return ImmutableSet.of(
|
||||
assertThat(BigDecimal.ZERO).isEqualTo(0),
|
||||
assertThat(BigDecimal.ZERO).isEqualTo(0),
|
||||
assertThat(BigDecimal.ZERO).isEqualTo(0));
|
||||
AbstractBigDecimalAssert<?> testAbstractBigDecimalAssertIsZero() {
|
||||
return assertThat(BigDecimal.ZERO).isZero();
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractBigDecimalAssert<?>> testAbstractBigDecimalAssertIsNotZero() {
|
||||
return ImmutableSet.of(
|
||||
assertThat(BigDecimal.ZERO).isNotEqualTo(0),
|
||||
assertThat(BigDecimal.ZERO).isNotEqualTo(0),
|
||||
assertThat(BigDecimal.ZERO).isNotEqualTo(0));
|
||||
AbstractBigDecimalAssert<?> testAbstractBigDecimalAssertIsNotZero() {
|
||||
return assertThat(BigDecimal.ZERO).isNotZero();
|
||||
}
|
||||
|
||||
ImmutableSet<AbstractBigDecimalAssert<?>> testAbstractBigDecimalAssertIsOne() {
|
||||
return ImmutableSet.of(
|
||||
assertThat(BigDecimal.ZERO).isEqualTo(1),
|
||||
assertThat(BigDecimal.ZERO).isEqualTo(1),
|
||||
assertThat(BigDecimal.ZERO).isEqualTo(1));
|
||||
AbstractBigDecimalAssert<?> testAbstractBigDecimalAssertIsOne() {
|
||||
return assertThat(BigDecimal.ZERO).isOne();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,10 +78,8 @@ final class AssortedTemplatesTest implements RefasterTemplateTestCase {
|
||||
|
||||
ImmutableSet<Boolean> testDisjointCollections() {
|
||||
return ImmutableSet.of(
|
||||
Collections.disjoint(ImmutableSet.copyOf(ImmutableList.of(1)), ImmutableList.of(2)),
|
||||
Collections.disjoint(new HashSet<>(ImmutableList.of(3)), ImmutableList.of(4)),
|
||||
Collections.disjoint(ImmutableList.of(5), ImmutableSet.copyOf(ImmutableList.of(6))),
|
||||
Collections.disjoint(ImmutableList.of(7), new HashSet<>(ImmutableList.of(8))));
|
||||
Collections.disjoint(new HashSet<>(ImmutableList.of(1)), ImmutableList.of(2)),
|
||||
Collections.disjoint(ImmutableList.of(3), new HashSet<>(ImmutableList.of(4))));
|
||||
}
|
||||
|
||||
boolean testIterableIsEmpty() {
|
||||
|
||||
@@ -81,9 +81,7 @@ final class AssortedTemplatesTest implements RefasterTemplateTestCase {
|
||||
ImmutableSet<Boolean> testDisjointCollections() {
|
||||
return ImmutableSet.of(
|
||||
Collections.disjoint(ImmutableList.of(1), ImmutableList.of(2)),
|
||||
Collections.disjoint(ImmutableList.of(3), ImmutableList.of(4)),
|
||||
Collections.disjoint(ImmutableList.of(5), ImmutableList.of(6)),
|
||||
Collections.disjoint(ImmutableList.of(7), ImmutableList.of(8)));
|
||||
Collections.disjoint(ImmutableList.of(3), ImmutableList.of(4)));
|
||||
}
|
||||
|
||||
boolean testIterableIsEmpty() {
|
||||
|
||||
@@ -113,8 +113,4 @@ final class ImmutableListMultimapTemplatesTest implements RefasterTemplateTestCa
|
||||
flatteningToImmutableListMultimap(
|
||||
Map.Entry::getKey, e -> e.getValue().stream().map(Math::toIntExact))));
|
||||
}
|
||||
|
||||
ImmutableListMultimap<String, Integer> testImmutableListMultimapCopyOfImmutableListMultimap() {
|
||||
return ImmutableListMultimap.copyOf(ImmutableListMultimap.of("foo", 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,8 +89,4 @@ final class ImmutableListMultimapTemplatesTest implements RefasterTemplateTestCa
|
||||
ImmutableListMultimap.copyOf(
|
||||
Multimaps.transformValues(TreeMultimap.<String, Long>create(), Math::toIntExact)));
|
||||
}
|
||||
|
||||
ImmutableListMultimap<String, Integer> testImmutableListMultimapCopyOfImmutableListMultimap() {
|
||||
return ImmutableListMultimap.of("foo", 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ final class ImmutableMapTemplatesTest implements RefasterTemplateTestCase {
|
||||
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"IterableToImmutableMapIdentity", "IndexIterableToImmutableMapIdentity"})
|
||||
ImmutableSet<ImmutableMap<Integer, Integer>> testIterableToImmutableMap() {
|
||||
return ImmutableSet.of(
|
||||
ImmutableList.of(1).stream().collect(toImmutableMap(identity(), n -> n * 2)),
|
||||
@@ -47,7 +48,14 @@ final class ImmutableMapTemplatesTest implements RefasterTemplateTestCase {
|
||||
.collect(toImmutableMap(n -> n, Integer::valueOf)),
|
||||
Streams.stream(ImmutableList.of(3).iterator())
|
||||
.collect(toImmutableMap(identity(), n -> n.intValue())),
|
||||
ImmutableMap.copyOf(Maps.asMap(ImmutableSet.of(4), Integer::valueOf)));
|
||||
ImmutableMap.copyOf(Maps.asMap(ImmutableSet.of(4), Integer::valueOf)),
|
||||
ImmutableList.of(1).stream().collect(toImmutableMap(identity(), identity())));
|
||||
}
|
||||
|
||||
ImmutableSet<ImmutableMap<Integer, Integer>> testIterableToImmutableMapIdentity() {
|
||||
return ImmutableSet.of(
|
||||
ImmutableList.of(1).stream().collect(toImmutableMap(identity(), identity())),
|
||||
Streams.stream(ImmutableList.of(2)::iterator).collect(toImmutableMap(n -> n, identity())));
|
||||
}
|
||||
|
||||
ImmutableSet<ImmutableMap<String, Integer>> testEntryIterableToImmutableMap() {
|
||||
@@ -78,6 +86,16 @@ final class ImmutableMapTemplatesTest implements RefasterTemplateTestCase {
|
||||
.collect(toImmutableMap(n -> n.intValue(), identity())));
|
||||
}
|
||||
|
||||
@SuppressWarnings("IterableToImmutableMapIdentity")
|
||||
ImmutableSet<ImmutableMap<Integer, Integer>> testIndexIterableToImmutableMapIdentity() {
|
||||
return ImmutableSet.of(
|
||||
ImmutableList.of(1).stream().collect(toImmutableMap(identity(), identity())),
|
||||
Streams.stream(ImmutableList.of(2)::iterator)
|
||||
.collect(toImmutableMap(identity(), identity())),
|
||||
Streams.stream(ImmutableList.of(3).iterator())
|
||||
.collect(toImmutableMap(identity(), identity())));
|
||||
}
|
||||
|
||||
ImmutableSet<ImmutableMap<String, Integer>> testTransformMapValuesToImmutableMap() {
|
||||
return ImmutableSet.of(
|
||||
ImmutableMap.of("foo", 1L).entrySet().stream()
|
||||
@@ -86,8 +104,4 @@ final class ImmutableMapTemplatesTest implements RefasterTemplateTestCase {
|
||||
ImmutableMap.of("bar", 2L).keySet(),
|
||||
k -> Math.toIntExact(ImmutableMap.of("bar", 2L).get(k))));
|
||||
}
|
||||
|
||||
ImmutableMap<String, Integer> testImmutableMapCopyOfImmutableMap() {
|
||||
return ImmutableMap.copyOf(ImmutableMap.of("foo", 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,12 +37,19 @@ final class ImmutableMapTemplatesTest implements RefasterTemplateTestCase {
|
||||
ImmutableMap.of(Map.entry("foo", 1).getKey(), Map.entry("foo", 1).getValue()));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"IterableToImmutableMapIdentity", "IndexIterableToImmutableMapIdentity"})
|
||||
ImmutableSet<ImmutableMap<Integer, Integer>> testIterableToImmutableMap() {
|
||||
return ImmutableSet.of(
|
||||
Maps.toMap(ImmutableList.of(1), n -> n * 2),
|
||||
Maps.toMap(ImmutableList.of(2)::iterator, Integer::valueOf),
|
||||
Maps.toMap(ImmutableList.of(3).iterator(), n -> n.intValue()),
|
||||
Maps.toMap(ImmutableSet.of(4), Integer::valueOf));
|
||||
Maps.toMap(ImmutableSet.of(4), Integer::valueOf),
|
||||
ImmutableList.of(1).stream().collect(toImmutableMap(identity(), identity())));
|
||||
}
|
||||
|
||||
ImmutableSet<ImmutableMap<Integer, Integer>> testIterableToImmutableMapIdentity() {
|
||||
return ImmutableSet.of(
|
||||
Maps.toMap(ImmutableList.of(1), k -> k), Maps.toMap(ImmutableList.of(2)::iterator, n -> n));
|
||||
}
|
||||
|
||||
ImmutableSet<ImmutableMap<String, Integer>> testEntryIterableToImmutableMap() {
|
||||
@@ -65,6 +72,14 @@ final class ImmutableMapTemplatesTest implements RefasterTemplateTestCase {
|
||||
Maps.uniqueIndex(ImmutableList.of(3).iterator(), n -> n.intValue()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("IterableToImmutableMapIdentity")
|
||||
ImmutableSet<ImmutableMap<Integer, Integer>> testIndexIterableToImmutableMapIdentity() {
|
||||
return ImmutableSet.of(
|
||||
Maps.uniqueIndex(ImmutableList.of(1), v -> v),
|
||||
Maps.uniqueIndex(ImmutableList.of(2)::iterator, v -> v),
|
||||
Maps.uniqueIndex(ImmutableList.of(3).iterator(), v -> v));
|
||||
}
|
||||
|
||||
ImmutableSet<ImmutableMap<String, Integer>> testTransformMapValuesToImmutableMap() {
|
||||
return ImmutableSet.of(
|
||||
ImmutableMap.copyOf(
|
||||
@@ -72,8 +87,4 @@ final class ImmutableMapTemplatesTest implements RefasterTemplateTestCase {
|
||||
ImmutableMap.copyOf(
|
||||
Maps.transformValues(ImmutableMap.of("bar", 2L), v -> Math.toIntExact(v))));
|
||||
}
|
||||
|
||||
ImmutableMap<String, Integer> testImmutableMapCopyOfImmutableMap() {
|
||||
return ImmutableMap.of("foo", 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,4 @@ final class ImmutableMultisetTemplatesTest implements RefasterTemplateTestCase {
|
||||
ImmutableMultiset.copyOf(Stream.of(1).iterator()),
|
||||
Stream.of(2).collect(collectingAndThen(toList(), ImmutableMultiset::copyOf)));
|
||||
}
|
||||
|
||||
ImmutableMultiset<Integer> testImmutableMultisetCopyOfImmutableMultiset() {
|
||||
return ImmutableMultiset.copyOf(ImmutableMultiset.of(1, 2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,4 @@ final class ImmutableMultisetTemplatesTest implements RefasterTemplateTestCase {
|
||||
return ImmutableSet.of(
|
||||
Stream.of(1).collect(toImmutableMultiset()), Stream.of(2).collect(toImmutableMultiset()));
|
||||
}
|
||||
|
||||
ImmutableMultiset<Integer> testImmutableMultisetCopyOfImmutableMultiset() {
|
||||
return ImmutableMultiset.of(1, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +91,4 @@ final class ImmutableSetMultimapTemplatesTest implements RefasterTemplateTestCas
|
||||
flatteningToImmutableSetMultimap(
|
||||
Map.Entry::getKey, e -> e.getValue().stream().map(Math::toIntExact))));
|
||||
}
|
||||
|
||||
ImmutableSetMultimap<String, Integer> testImmutableSetMultimapCopyOfImmutableSetMultimap() {
|
||||
return ImmutableSetMultimap.copyOf(ImmutableSetMultimap.of("foo", 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>>
|
||||
@@ -72,8 +72,4 @@ final class ImmutableSetMultimapTemplatesTest implements RefasterTemplateTestCas
|
||||
ImmutableSetMultimap.copyOf(
|
||||
Multimaps.transformValues(TreeMultimap.<String, Long>create(), Math::toIntExact)));
|
||||
}
|
||||
|
||||
ImmutableSetMultimap<String, Integer> testImmutableSetMultimapCopyOfImmutableSetMultimap() {
|
||||
return ImmutableSetMultimap.of("foo", 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,10 +59,6 @@ final class ImmutableSetTemplatesTest implements RefasterTemplateTestCase {
|
||||
Stream.of(4).collect(collectingAndThen(toSet(), ImmutableSet::copyOf)));
|
||||
}
|
||||
|
||||
ImmutableSet<Integer> testImmutableSetCopyOfImmutableSet() {
|
||||
return ImmutableSet.copyOf(ImmutableSet.of(1, 2));
|
||||
}
|
||||
|
||||
ImmutableSet<Integer> testImmutableSetCopyOfSetView() {
|
||||
return ImmutableSet.copyOf(Sets.difference(ImmutableSet.of(1), ImmutableSet.of(2)));
|
||||
}
|
||||
|
||||
@@ -58,10 +58,6 @@ final class ImmutableSetTemplatesTest implements RefasterTemplateTestCase {
|
||||
Stream.of(4).collect(toImmutableSet()));
|
||||
}
|
||||
|
||||
ImmutableSet<Integer> testImmutableSetCopyOfImmutableSet() {
|
||||
return ImmutableSet.of(1, 2);
|
||||
}
|
||||
|
||||
ImmutableSet<Integer> testImmutableSetCopyOfSetView() {
|
||||
return Sets.difference(ImmutableSet.of(1), ImmutableSet.of(2)).immutableCopy();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -45,10 +46,23 @@ 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"));
|
||||
}
|
||||
|
||||
ImmutableSet<Mono<Optional<String>>> testMonoCollectToOptional() {
|
||||
return ImmutableSet.of(
|
||||
Mono.just("foo").map(Optional::of).defaultIfEmpty(Optional.empty()),
|
||||
@@ -59,6 +73,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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -46,10 +47,22 @@ 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();
|
||||
}
|
||||
|
||||
ImmutableSet<Mono<Optional<String>>> testMonoCollectToOptional() {
|
||||
return ImmutableSet.of(
|
||||
Mono.just("foo").flux().collect(toOptional()),
|
||||
@@ -60,6 +73,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));
|
||||
}
|
||||
|
||||
@@ -25,13 +25,11 @@ final class RxJava2AdapterTemplatesTest implements RefasterTemplateTestCase {
|
||||
// seventh parameter onwards.
|
||||
return ImmutableSet.copyOf(
|
||||
Arrays.asList(
|
||||
Flux.from(Flowable.just(1)),
|
||||
Flowable.just(2).compose(Flux::from),
|
||||
Flowable.just(3).to(Flux::from),
|
||||
Flowable.just(4).as(Flux::from),
|
||||
RxJava2Adapter.flowableToFlux(Flowable.just(5)),
|
||||
Flowable.just(6).compose(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(7).<Publisher<Integer>>to(RxJava2Adapter::flowableToFlux)));
|
||||
Flowable.just(1).compose(Flux::from),
|
||||
Flowable.just(2).to(Flux::from),
|
||||
Flowable.just(3).as(Flux::from),
|
||||
Flowable.just(4).compose(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(5).<Publisher<Integer>>to(RxJava2Adapter::flowableToFlux)));
|
||||
}
|
||||
|
||||
ImmutableSet<Publisher<String>> testFluxToFlowable() {
|
||||
@@ -39,8 +37,7 @@ final class RxJava2AdapterTemplatesTest implements RefasterTemplateTestCase {
|
||||
Flowable.fromPublisher(Flux.just("foo")),
|
||||
Flux.just("bar").transform(Flowable::fromPublisher),
|
||||
Flux.just("baz").as(Flowable::fromPublisher),
|
||||
RxJava2Adapter.fluxToFlowable(Flux.just("qux")),
|
||||
Flux.just("quux").transform(RxJava2Adapter::fluxToFlowable));
|
||||
Flux.just("qux").transform(RxJava2Adapter::fluxToFlowable));
|
||||
}
|
||||
|
||||
ImmutableSet<Observable<Integer>> testFluxToObservable() {
|
||||
@@ -68,8 +65,7 @@ final class RxJava2AdapterTemplatesTest implements RefasterTemplateTestCase {
|
||||
Flowable.fromPublisher(Mono.just(1)),
|
||||
Mono.just(2).transform(Flowable::fromPublisher),
|
||||
Mono.just(3).as(Flowable::fromPublisher),
|
||||
RxJava2Adapter.monoToFlowable(Mono.just(4)),
|
||||
Mono.just(5).transform(RxJava2Adapter::monoToFlowable));
|
||||
Mono.just(4).transform(RxJava2Adapter::monoToFlowable));
|
||||
}
|
||||
|
||||
Maybe<String> testMonoToMaybe() {
|
||||
|
||||
@@ -29,9 +29,7 @@ final class RxJava2AdapterTemplatesTest implements RefasterTemplateTestCase {
|
||||
Flowable.just(2).as(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(3).as(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(4).as(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(5).as(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(6).as(RxJava2Adapter::flowableToFlux),
|
||||
Flowable.just(7).as(RxJava2Adapter::flowableToFlux)));
|
||||
Flowable.just(5).as(RxJava2Adapter::flowableToFlux)));
|
||||
}
|
||||
|
||||
ImmutableSet<Publisher<String>> testFluxToFlowable() {
|
||||
@@ -39,8 +37,7 @@ final class RxJava2AdapterTemplatesTest implements RefasterTemplateTestCase {
|
||||
Flux.just("foo").as(RxJava2Adapter::fluxToFlowable),
|
||||
Flux.just("bar").as(RxJava2Adapter::fluxToFlowable),
|
||||
Flux.just("baz").as(RxJava2Adapter::fluxToFlowable),
|
||||
Flux.just("qux").as(RxJava2Adapter::fluxToFlowable),
|
||||
Flux.just("quux").as(RxJava2Adapter::fluxToFlowable));
|
||||
Flux.just("qux").as(RxJava2Adapter::fluxToFlowable));
|
||||
}
|
||||
|
||||
ImmutableSet<Observable<Integer>> testFluxToObservable() {
|
||||
@@ -68,8 +65,7 @@ final class RxJava2AdapterTemplatesTest implements RefasterTemplateTestCase {
|
||||
Mono.just(1).as(RxJava2Adapter::monoToFlowable),
|
||||
Mono.just(2).as(RxJava2Adapter::monoToFlowable),
|
||||
Mono.just(3).as(RxJava2Adapter::monoToFlowable),
|
||||
Mono.just(4).as(RxJava2Adapter::monoToFlowable),
|
||||
Mono.just(5).as(RxJava2Adapter::monoToFlowable));
|
||||
Mono.just(4).as(RxJava2Adapter::monoToFlowable));
|
||||
}
|
||||
|
||||
Maybe<String> testMonoToMaybe() {
|
||||
|
||||
@@ -34,6 +34,10 @@ final class TimeTemplatesTest implements RefasterTemplateTestCase {
|
||||
ZoneId.from(ZoneOffset.UTC));
|
||||
}
|
||||
|
||||
OffsetDateTime testInstantAtOffset() {
|
||||
return OffsetDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
Clock testUtcClock() {
|
||||
return Clock.system(ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,10 @@ final class TimeTemplatesTest implements RefasterTemplateTestCase {
|
||||
ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
OffsetDateTime testInstantAtOffset() {
|
||||
return Instant.EPOCH.atOffset(ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
Clock testUtcClock() {
|
||||
return Clock.systemUTC();
|
||||
}
|
||||
|
||||
45
pom.xml
45
pom.xml
@@ -125,17 +125,17 @@
|
||||
<!-- Dependency and plugin versions that are referenced in more than
|
||||
one place. We use these to keep dependencies in sync. Version numbers
|
||||
that need to be referenced only once should *not* be listed here. -->
|
||||
<version.auto-service>1.0</version.auto-service>
|
||||
<version.auto-service>1.0.1</version.auto-service>
|
||||
<version.auto-value>1.8.2</version.auto-value>
|
||||
<version.error-prone>${version.error-prone-orig}</version.error-prone>
|
||||
<version.error-prone-fork>v${version.error-prone-orig}-picnic-1</version.error-prone-fork>
|
||||
<version.error-prone-orig>2.9.0</version.error-prone-orig>
|
||||
<version.error-prone-orig>2.10.0</version.error-prone-orig>
|
||||
<version.error-prone-slf4j>0.1.4</version.error-prone-slf4j>
|
||||
<version.findbugs-format-string>3.0.0</version.findbugs-format-string>
|
||||
<version.guava-beta-checker>1.0</version.guava-beta-checker>
|
||||
<version.jdk>11</version.jdk>
|
||||
<version.maven>3.6.3</version.maven>
|
||||
<version.mockito>3.12.4</version.mockito>
|
||||
<version.mockito>4.0.0</version.mockito>
|
||||
<version.nopen-checker>1.0.1</version.nopen-checker>
|
||||
<version.nullaway>0.9.2</version.nullaway>
|
||||
<!-- XXX: Two other dependencies are potentially of interest:
|
||||
@@ -146,7 +146,7 @@
|
||||
that we can pick them up. (But in case of `baseline-refaster-rules`
|
||||
perhaps we can simply incorporate all of them.) -->
|
||||
<version.palantir-assertj-automation>0.3.0</version.palantir-assertj-automation>
|
||||
<version.palantir-baseline>4.25.0</version.palantir-baseline>
|
||||
<version.palantir-baseline>4.42.0</version.palantir-baseline>
|
||||
<version.surefire>2.22.2</version.surefire>
|
||||
</properties>
|
||||
|
||||
@@ -190,14 +190,14 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson</groupId>
|
||||
<artifactId>jackson-bom</artifactId>
|
||||
<version>2.12.5</version>
|
||||
<version>2.13.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto</groupId>
|
||||
<artifactId>auto-common</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto.service</groupId>
|
||||
@@ -241,7 +241,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava-bom</artifactId>
|
||||
<version>31.0-jre</version>
|
||||
<version>31.0.1-jre</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -257,6 +257,11 @@
|
||||
<artifactId>nopen-checker</artifactId>
|
||||
<version>${version.nopen-checker}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.newrelic.agent.java</groupId>
|
||||
<artifactId>newrelic-api</artifactId>
|
||||
<version>7.4.3</version>
|
||||
</dependency>
|
||||
<!-- Specified as a workaround for
|
||||
https://github.com/mojohaus/versions-maven-plugin/issues/244. -->
|
||||
<dependency>
|
||||
@@ -281,7 +286,7 @@
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-bom</artifactId>
|
||||
<version>2020.0.11</version>
|
||||
<version>2020.0.13</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -293,12 +298,12 @@
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.6.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>2.1.10</version>
|
||||
<version>2.1.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
@@ -335,7 +340,7 @@
|
||||
<dependency>
|
||||
<groupId>org.checkerframework</groupId>
|
||||
<artifactId>checker-qual</artifactId>
|
||||
<version>3.18.0</version>
|
||||
<version>3.19.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
@@ -367,10 +372,15 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>5.3.10</version>
|
||||
<version>5.3.13</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
<version>2.5.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
@@ -681,7 +691,7 @@
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>9.0</version>
|
||||
<version>9.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
@@ -1102,7 +1112,7 @@
|
||||
<plugin>
|
||||
<groupId>org.pitest</groupId>
|
||||
<artifactId>pitest-maven</artifactId>
|
||||
<version>1.7.0</version>
|
||||
<version>1.7.3</version>
|
||||
<configuration>
|
||||
<!-- Use multiple threads to speed things up. Extend
|
||||
timeouts to prevent false positives as a result of
|
||||
@@ -1130,7 +1140,7 @@
|
||||
<plugin>
|
||||
<groupId>org.sonarsource.scanner.maven</groupId>
|
||||
<artifactId>sonar-maven-plugin</artifactId>
|
||||
<version>3.9.0.2155</version>
|
||||
<version>3.9.1.2184</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
@@ -1446,8 +1456,13 @@
|
||||
-Xep:BetaApi:OFF
|
||||
<!-- We don't target JDK 7. -->
|
||||
-Xep:Java7ApiChecker:OFF
|
||||
<!-- We don't target JDK 8. -->
|
||||
-Xep:Java8ApiChecker:OFF
|
||||
<!-- We don't target Android. -->
|
||||
-Xep:StaticOrDefaultInterfaceMethod:OFF
|
||||
<!-- XXX: This check flags false positives.
|
||||
See https://github.com/google/error-prone/issues/2679. -->
|
||||
-Xep:VoidMissingNullable:OFF
|
||||
-XepOpt:NullAway:AnnotatedPackages=tech.picnic
|
||||
-XepOpt:NullAway:AssertsEnabled=true
|
||||
-XepOpt:NullAway:CheckOptionalEmptiness=true
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package tech.picnic.errorprone.refaster.util;
|
||||
|
||||
import static com.google.errorprone.matchers.Matchers.staticMethod;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
|
||||
/**
|
||||
* A matcher for `identity` operations, for use with Refaster's {@code {Not,}Matches} annotation.
|
||||
*/
|
||||
public final class IsIdentity implements Matcher<ExpressionTree> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ExpressionTree> IDENTITY =
|
||||
staticMethod()
|
||||
.onClass((type, state) -> type.toString().contains("Function"))
|
||||
.named("identity");
|
||||
|
||||
@Override
|
||||
public boolean matches(ExpressionTree tree, VisitorState state) {
|
||||
return IDENTITY.matches(tree, state);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package tech.picnic.errorprone.refaster.util;
|
||||
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class IsIdentityTest {
|
||||
@Test
|
||||
void matches() {
|
||||
CompilationTestHelper.newInstance(TestChecker.class, getClass())
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static com.google.common.collect.ImmutableMap.toImmutableMap;",
|
||||
"import static java.util.function.Function.identity;",
|
||||
"import com.google.common.collect.ImmutableList;",
|
||||
"import com.google.common.collect.ImmutableMap;",
|
||||
"import com.google.common.collect.ImmutableSet;",
|
||||
"import com.google.common.collect.Streams;",
|
||||
"",
|
||||
"class A {",
|
||||
" void positive() {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" java.util.function.Function.identity();",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" com.google.common.base.Functions.identity();",
|
||||
" }",
|
||||
"",
|
||||
" public static void identity() { }",
|
||||
"",
|
||||
" static void negative() {",
|
||||
" identity();",
|
||||
" A.identity();",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
/** A {@link BugChecker} which simply delegates to {@link IsIdentity}. */
|
||||
@BugPattern(name = "TestChecker", summary = "Flags identity method invocations", severity = ERROR)
|
||||
public static final class TestChecker extends BugChecker implements MethodInvocationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
|
||||
return new IsIdentity().matches(tree, state) ? describeMatch(tree) : Description.NO_MATCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user