mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 08:11:25 +00:00
Compare commits
1 Commits
mohamedsam
...
sschroever
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
728814aa6a |
@@ -3,6 +3,7 @@ package tech.picnic.errorprone.bugpatterns;
|
||||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION;
|
||||
import static java.util.function.Predicate.not;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
@@ -19,7 +20,6 @@ import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.AssignmentTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.NewArrayTree;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
@@ -38,12 +38,11 @@ import tech.picnic.errorprone.utils.SourceCode;
|
||||
public final class CanonicalAnnotationSyntax extends BugChecker implements AnnotationTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Pattern TRAILING_ARRAY_COMMA = Pattern.compile(",\\s*}$");
|
||||
private static final ImmutableSet<BiFunction<AnnotationTree, VisitorState, Optional<Fix>>>
|
||||
FIX_FACTORIES =
|
||||
ImmutableSet.of(
|
||||
CanonicalAnnotationSyntax::dropRedundantParentheses,
|
||||
CanonicalAnnotationSyntax::dropRedundantValueAttribute,
|
||||
CanonicalAnnotationSyntax::dropRedundantCurlies);
|
||||
private static final ImmutableSet<BiFunction<AnnotationTree, VisitorState, Fix>> FIX_FACTORIES =
|
||||
ImmutableSet.of(
|
||||
CanonicalAnnotationSyntax::dropRedundantParentheses,
|
||||
CanonicalAnnotationSyntax::dropRedundantValueAttribute,
|
||||
CanonicalAnnotationSyntax::dropRedundantCurlies);
|
||||
|
||||
/** Instantiates a new {@link CanonicalAnnotationSyntax} instance. */
|
||||
public CanonicalAnnotationSyntax() {}
|
||||
@@ -52,39 +51,38 @@ public final class CanonicalAnnotationSyntax extends BugChecker implements Annot
|
||||
public Description matchAnnotation(AnnotationTree tree, VisitorState state) {
|
||||
return FIX_FACTORIES.stream()
|
||||
.map(op -> op.apply(tree, state))
|
||||
.flatMap(Optional::stream)
|
||||
.filter(not(Fix::isEmpty))
|
||||
.findFirst()
|
||||
.map(fix -> describeMatch(tree, fix))
|
||||
.orElse(Description.NO_MATCH);
|
||||
}
|
||||
|
||||
private static Optional<Fix> dropRedundantParentheses(AnnotationTree tree, VisitorState state) {
|
||||
private static Fix dropRedundantParentheses(AnnotationTree tree, VisitorState state) {
|
||||
if (!tree.getArguments().isEmpty()) {
|
||||
/* Parentheses are necessary. */
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
String src = state.getSourceForNode(tree);
|
||||
if (src == null) {
|
||||
/* Without the source code there's not much we can do. */
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
int parenIndex = src.indexOf('(');
|
||||
if (parenIndex < 0) {
|
||||
/* There are no redundant parentheses. */
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
return Optional.of(SuggestedFix.replace(tree, src.substring(0, parenIndex)));
|
||||
return SuggestedFix.replace(tree, src.substring(0, parenIndex));
|
||||
}
|
||||
|
||||
private static Optional<Fix> dropRedundantValueAttribute(
|
||||
AnnotationTree tree, VisitorState state) {
|
||||
private static Fix dropRedundantValueAttribute(AnnotationTree tree, VisitorState state) {
|
||||
List<? extends ExpressionTree> args = tree.getArguments();
|
||||
if (args.size() != 1) {
|
||||
/* The `value` attribute, if specified, cannot be dropped. */
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
ExpressionTree arg = args.get(0);
|
||||
@@ -93,25 +91,23 @@ public final class CanonicalAnnotationSyntax extends BugChecker implements Annot
|
||||
* The annotation argument doesn't have a source representation, e.g. because `value` isn't
|
||||
* assigned explicitly.
|
||||
*/
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
ExpressionTree expr = AnnotationMatcherUtils.getArgument(tree, "value");
|
||||
if (expr == null) {
|
||||
/* This is not an explicit assignment to the `value` attribute. */
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
/* Replace the assignment with (the simplified representation of) just its value. */
|
||||
return Optional.of(
|
||||
SuggestedFix.replace(
|
||||
arg,
|
||||
simplifyAttributeValue(expr, state)
|
||||
.orElseGet(() -> SourceCode.treeToString(expr, state))));
|
||||
return SuggestedFix.replace(
|
||||
arg,
|
||||
simplifyAttributeValue(expr, state).orElseGet(() -> SourceCode.treeToString(expr, state)));
|
||||
}
|
||||
|
||||
private static Optional<Fix> dropRedundantCurlies(AnnotationTree tree, VisitorState state) {
|
||||
List<SuggestedFix.Builder> fixes = new ArrayList<>();
|
||||
private static Fix dropRedundantCurlies(AnnotationTree tree, VisitorState state) {
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder();
|
||||
for (ExpressionTree arg : tree.getArguments()) {
|
||||
/*
|
||||
* We'll try to simplify each assignment's RHS; for non-assignment we'll try to simplify
|
||||
@@ -122,10 +118,10 @@ public final class CanonicalAnnotationSyntax extends BugChecker implements Annot
|
||||
|
||||
/* Store a fix for each expression that was successfully simplified. */
|
||||
simplifyAttributeValue(value, state)
|
||||
.ifPresent(expr -> fixes.add(SuggestedFix.builder().replace(value, expr)));
|
||||
.ifPresent(expr -> fix.merge(SuggestedFix.replace(value, expr)));
|
||||
}
|
||||
|
||||
return fixes.stream().reduce(SuggestedFix.Builder::merge).map(SuggestedFix.Builder::build);
|
||||
return fix.build();
|
||||
}
|
||||
|
||||
private static Optional<String> simplifyAttributeValue(ExpressionTree expr, VisitorState state) {
|
||||
|
||||
@@ -105,22 +105,19 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
|
||||
@Override
|
||||
public Description matchAnnotation(AnnotationTree tree, VisitorState state) {
|
||||
return sortArrayElements(tree, state)
|
||||
.map(fix -> describeMatch(tree, fix))
|
||||
.orElse(Description.NO_MATCH);
|
||||
Fix fix = sortArrayElements(tree, state);
|
||||
return fix.isEmpty() ? Description.NO_MATCH : describeMatch(tree, fix);
|
||||
}
|
||||
|
||||
private Optional<Fix> sortArrayElements(AnnotationTree tree, VisitorState state) {
|
||||
private Fix sortArrayElements(AnnotationTree tree, VisitorState state) {
|
||||
/*
|
||||
* We loop over the array's attributes, trying to sort each array associated with a
|
||||
* non-blacklisted attribute. A single compound fix, if any, is returned.
|
||||
*/
|
||||
return matcher
|
||||
.extractMatchingArguments(tree)
|
||||
.map(expr -> extractArray(expr).flatMap(arr -> suggestSorting(arr, state)))
|
||||
.flatMap(Optional::stream)
|
||||
.reduce(SuggestedFix.Builder::merge)
|
||||
.map(SuggestedFix.Builder::build);
|
||||
.flatMap(expr -> extractArray(expr).map(arr -> suggestSorting(arr, state)).stream())
|
||||
.collect(SuggestedFix.mergeFixes());
|
||||
}
|
||||
|
||||
private static Optional<NewArrayTree> extractArray(ExpressionTree expr) {
|
||||
@@ -129,18 +126,17 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
: Optional.of(expr).filter(NewArrayTree.class::isInstance).map(NewArrayTree.class::cast);
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix.Builder> suggestSorting(
|
||||
NewArrayTree array, VisitorState state) {
|
||||
private static SuggestedFix suggestSorting(NewArrayTree array, VisitorState state) {
|
||||
if (array.getInitializers().size() < 2 || !canSort(array, state)) {
|
||||
/* There's nothing to sort, or we don't want to sort. */
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
List<? extends ExpressionTree> actualOrdering = array.getInitializers();
|
||||
ImmutableList<? extends ExpressionTree> desiredOrdering = doSort(actualOrdering);
|
||||
if (actualOrdering.equals(desiredOrdering)) {
|
||||
/* In the (presumably) common case the elements are already sorted. */
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
/* The elements aren't sorted. Suggest the sorted alternative. */
|
||||
@@ -148,7 +144,7 @@ public final class LexicographicalAnnotationAttributeListing extends BugChecker
|
||||
desiredOrdering.stream()
|
||||
.map(expr -> SourceCode.treeToString(expr, state))
|
||||
.collect(joining(", ", "{", "}"));
|
||||
return Optional.of(SuggestedFix.builder().replace(array, suggestion));
|
||||
return SuggestedFix.replace(array, suggestion);
|
||||
}
|
||||
|
||||
private static boolean canSort(Tree array, VisitorState state) {
|
||||
|
||||
@@ -10,7 +10,6 @@ import static java.util.Comparator.comparing;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.VerifyException;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.errorprone.BugPattern;
|
||||
@@ -105,10 +104,7 @@ public final class LexicographicalAnnotationListing extends BugChecker
|
||||
originalAnnotations.stream(),
|
||||
sortedAnnotations.stream(),
|
||||
(original, replacement) ->
|
||||
SuggestedFix.builder()
|
||||
.replace(original, SourceCode.treeToString(replacement, state)))
|
||||
.reduce(SuggestedFix.Builder::merge)
|
||||
.map(SuggestedFix.Builder::build)
|
||||
.orElseThrow(() -> new VerifyException("No annotations were provided"));
|
||||
SuggestedFix.replace(original, SourceCode.treeToString(replacement, state)))
|
||||
.collect(SuggestedFix.mergeFixes());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import static com.google.errorprone.BugPattern.StandardTags.PERFORMANCE;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
|
||||
import static java.util.function.Predicate.not;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
|
||||
@@ -85,22 +86,22 @@ public final class PrimitiveComparison extends BugChecker implements MethodInvoc
|
||||
}
|
||||
|
||||
return getPotentiallyBoxedReturnType(tree.getArguments().get(0))
|
||||
.flatMap(cmpType -> attemptMethodInvocationReplacement(tree, cmpType, isStatic, state))
|
||||
.map(cmpType -> attemptMethodInvocationReplacement(tree, cmpType, isStatic, state))
|
||||
.filter(not(SuggestedFix::isEmpty))
|
||||
.map(fix -> describeMatch(tree, fix))
|
||||
.orElse(Description.NO_MATCH);
|
||||
}
|
||||
|
||||
private static Optional<Fix> attemptMethodInvocationReplacement(
|
||||
private static SuggestedFix attemptMethodInvocationReplacement(
|
||||
MethodInvocationTree tree, Type cmpType, boolean isStatic, VisitorState state) {
|
||||
String actualMethodName = ASTHelpers.getSymbol(tree).getSimpleName().toString();
|
||||
String preferredMethodName = getPreferredMethod(cmpType, isStatic, state);
|
||||
if (actualMethodName.equals(preferredMethodName)) {
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
return Optional.of(
|
||||
suggestFix(
|
||||
tree, prefixTypeArgumentsIfRelevant(preferredMethodName, tree, cmpType, state), state));
|
||||
return suggestFix(
|
||||
tree, prefixTypeArgumentsIfRelevant(preferredMethodName, tree, cmpType, state), state);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,7 +170,7 @@ public final class PrimitiveComparison extends BugChecker implements MethodInvoc
|
||||
}
|
||||
|
||||
// XXX: Use switch pattern matching once the targeted JDK supports this.
|
||||
private static Fix suggestFix(
|
||||
private static SuggestedFix suggestFix(
|
||||
MethodInvocationTree tree, String preferredMethodName, VisitorState state) {
|
||||
ExpressionTree expr = tree.getMethodSelect();
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ import com.sun.source.tree.Tree.Kind;
|
||||
import java.io.Console;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Formattable;
|
||||
import java.util.Formatter;
|
||||
import java.util.List;
|
||||
@@ -177,18 +176,18 @@ public final class RedundantStringConversion extends BugChecker
|
||||
return createDescription(tree, tryFix(rhs, state, STRING));
|
||||
}
|
||||
|
||||
List<SuggestedFix.Builder> fixes = new ArrayList<>();
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder();
|
||||
|
||||
// XXX: Avoid trying to simplify the RHS twice.
|
||||
ExpressionTree preferredRhs = trySimplify(rhs, state).orElse(rhs);
|
||||
if (STRING.matches(preferredRhs, state)) {
|
||||
tryFix(lhs, state, ANY_EXPR).ifPresent(fixes::add);
|
||||
fix.merge(tryFix(lhs, state, ANY_EXPR));
|
||||
} else {
|
||||
tryFix(lhs, state, STRING).ifPresent(fixes::add);
|
||||
fix.merge(tryFix(lhs, state, STRING));
|
||||
}
|
||||
tryFix(rhs, state, ANY_EXPR).ifPresent(fixes::add);
|
||||
fix.merge(tryFix(rhs, state, ANY_EXPR));
|
||||
|
||||
return createDescription(tree, fixes.stream().reduce(SuggestedFix.Builder::merge));
|
||||
return createDescription(tree, fix.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -229,17 +228,18 @@ public final class RedundantStringConversion extends BugChecker
|
||||
return createDescription(tree, tryFix(tree, state, NON_NULL_STRING));
|
||||
}
|
||||
|
||||
private Optional<SuggestedFix.Builder> tryFixPositionalConverter(
|
||||
private SuggestedFix tryFixPositionalConverter(
|
||||
List<? extends ExpressionTree> arguments, VisitorState state, int index) {
|
||||
return Optional.of(arguments)
|
||||
.filter(args -> args.size() > index)
|
||||
.flatMap(args -> tryFix(args.get(index), state, ANY_EXPR));
|
||||
.map(args -> tryFix(args.get(index), state, ANY_EXPR))
|
||||
.orElseGet(SuggestedFix::emptyFix);
|
||||
}
|
||||
|
||||
// XXX: Write another check that checks that Formatter patterns don't use `{}` and have a
|
||||
// matching number of arguments of the appropriate type. Also flag explicit conversions from
|
||||
// `Formattable` to string.
|
||||
private Optional<SuggestedFix.Builder> tryFixFormatter(
|
||||
private SuggestedFix tryFixFormatter(
|
||||
List<? extends ExpressionTree> arguments, VisitorState state) {
|
||||
/*
|
||||
* Formatter methods have an optional first `Locale` parameter; if present, it must be
|
||||
@@ -258,7 +258,7 @@ public final class RedundantStringConversion extends BugChecker
|
||||
return tryFixFormatterArguments(arguments, state, LOCALE, NOT_FORMATTABLE);
|
||||
}
|
||||
|
||||
private Optional<SuggestedFix.Builder> tryFixGuavaGuard(
|
||||
private SuggestedFix tryFixGuavaGuard(
|
||||
List<? extends ExpressionTree> arguments, VisitorState state) {
|
||||
/*
|
||||
* All Guava guard methods accept a value to be checked, a format string and zero or more
|
||||
@@ -271,7 +271,7 @@ public final class RedundantStringConversion extends BugChecker
|
||||
// number of arguments of the appropriate type. Also flag explicit conversions from `Throwable` to
|
||||
// string as the last logger argument. Suggests either dropping the conversion or going with
|
||||
// `Throwable#getMessage()` instead.
|
||||
private Optional<SuggestedFix.Builder> tryFixSlf4jLogger(
|
||||
private SuggestedFix tryFixSlf4jLogger(
|
||||
List<? extends ExpressionTree> arguments, VisitorState state) {
|
||||
/*
|
||||
* SLF4J treats the final argument to a log statement specially if it is a `Throwable`: it
|
||||
@@ -291,36 +291,34 @@ public final class RedundantStringConversion extends BugChecker
|
||||
omitLast ? arguments.subList(0, arguments.size() - 1) : arguments, state, MARKER, ANY_EXPR);
|
||||
}
|
||||
|
||||
private Optional<SuggestedFix.Builder> tryFixFormatterArguments(
|
||||
private SuggestedFix tryFixFormatterArguments(
|
||||
List<? extends ExpressionTree> arguments,
|
||||
VisitorState state,
|
||||
Matcher<ExpressionTree> firstArgFilter,
|
||||
Matcher<ExpressionTree> remainingArgFilter) {
|
||||
if (arguments.isEmpty()) {
|
||||
/* This format method accepts no arguments. Some odd overload? */
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
int patternIndex = firstArgFilter.matches(arguments.get(0), state) ? 1 : 0;
|
||||
if (arguments.size() <= patternIndex) {
|
||||
/* This format method accepts only an ignored parameter. Some odd overload? */
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
/* Simplify the values to be plugged into the format pattern, if possible. */
|
||||
return arguments.stream()
|
||||
.skip(patternIndex + 1L)
|
||||
.map(arg -> tryFix(arg, state, remainingArgFilter))
|
||||
.flatMap(Optional::stream)
|
||||
.reduce(SuggestedFix.Builder::merge);
|
||||
.collect(SuggestedFix.mergeFixes());
|
||||
}
|
||||
|
||||
private Optional<SuggestedFix.Builder> tryFix(
|
||||
private SuggestedFix tryFix(
|
||||
ExpressionTree tree, VisitorState state, Matcher<ExpressionTree> filter) {
|
||||
return trySimplify(tree, state, filter)
|
||||
.map(
|
||||
replacement ->
|
||||
SuggestedFix.builder().replace(tree, SourceCode.treeToString(replacement, state)));
|
||||
.map(replacement -> SuggestedFix.replace(tree, SourceCode.treeToString(replacement, state)))
|
||||
.orElseGet(SuggestedFix::emptyFix);
|
||||
}
|
||||
|
||||
private Optional<ExpressionTree> trySimplify(
|
||||
@@ -369,11 +367,8 @@ public final class RedundantStringConversion extends BugChecker
|
||||
return Optional.of(Iterables.getOnlyElement(methodInvocation.getArguments()));
|
||||
}
|
||||
|
||||
private Description createDescription(Tree tree, Optional<SuggestedFix.Builder> fixes) {
|
||||
return fixes
|
||||
.map(SuggestedFix.Builder::build)
|
||||
.map(fix -> describeMatch(tree, fix))
|
||||
.orElse(Description.NO_MATCH);
|
||||
private Description createDescription(Tree tree, SuggestedFix fix) {
|
||||
return fix.isEmpty() ? Description.NO_MATCH : describeMatch(tree, fix);
|
||||
}
|
||||
|
||||
private static Matcher<MethodInvocationTree> createConversionMethodMatcher(
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.AnnotationTreeMatcher;
|
||||
import com.google.errorprone.fixes.Fix;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
@@ -65,16 +64,18 @@ public final class SpringMvcAnnotation extends BugChecker implements AnnotationT
|
||||
return ARGUMENT_SELECTOR
|
||||
.extractMatchingArguments(tree)
|
||||
.findFirst()
|
||||
.flatMap(arg -> trySimplification(tree, arg, state))
|
||||
.map(arg -> trySimplification(tree, arg, state))
|
||||
.filter(not(SuggestedFix::isEmpty))
|
||||
.map(fix -> describeMatch(tree, fix))
|
||||
.orElse(Description.NO_MATCH);
|
||||
}
|
||||
|
||||
private static Optional<Fix> trySimplification(
|
||||
private static SuggestedFix trySimplification(
|
||||
AnnotationTree tree, ExpressionTree arg, VisitorState state) {
|
||||
return extractUniqueMethod(arg, state)
|
||||
.map(REPLACEMENTS::get)
|
||||
.map(newAnnotation -> replaceAnnotation(tree, arg, newAnnotation, state));
|
||||
.map(newAnnotation -> replaceAnnotation(tree, arg, newAnnotation, state))
|
||||
.orElseGet(SuggestedFix::emptyFix);
|
||||
}
|
||||
|
||||
private static Optional<String> extractUniqueMethod(ExpressionTree arg, VisitorState state) {
|
||||
@@ -99,7 +100,7 @@ public final class SpringMvcAnnotation extends BugChecker implements AnnotationT
|
||||
};
|
||||
}
|
||||
|
||||
private static Fix replaceAnnotation(
|
||||
private static SuggestedFix replaceAnnotation(
|
||||
AnnotationTree tree, ExpressionTree argToRemove, String newAnnotation, VisitorState state) {
|
||||
String newArguments =
|
||||
tree.getArguments().stream()
|
||||
|
||||
@@ -4,6 +4,7 @@ import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.STYLE;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.function.Predicate.not;
|
||||
import static tech.picnic.errorprone.bugpatterns.NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_IDENTIFIERS;
|
||||
import static tech.picnic.errorprone.bugpatterns.NonStaticImport.NON_STATIC_IMPORT_CANDIDATE_MEMBERS;
|
||||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL;
|
||||
@@ -34,7 +35,6 @@ import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MemberSelectTreeMatcher;
|
||||
import com.google.errorprone.bugpatterns.StaticImports;
|
||||
import com.google.errorprone.bugpatterns.StaticImports.StaticImportInfo;
|
||||
import com.google.errorprone.fixes.Fix;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
@@ -203,7 +203,8 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa
|
||||
}
|
||||
|
||||
return getCandidateSimpleName(importInfo)
|
||||
.flatMap(n -> tryStaticImport(tree, importInfo.canonicalName() + '.' + n, n, state))
|
||||
.map(n -> tryStaticImport(tree, importInfo.canonicalName() + '.' + n, n, state))
|
||||
.filter(not(SuggestedFix::isEmpty))
|
||||
.map(fix -> describeMatch(tree, fix))
|
||||
.orElse(Description.NO_MATCH);
|
||||
}
|
||||
@@ -240,15 +241,15 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa
|
||||
|| STATIC_IMPORT_CANDIDATE_MEMBERS.containsEntry(canonicalName, name));
|
||||
}
|
||||
|
||||
private static Optional<Fix> tryStaticImport(
|
||||
private static SuggestedFix tryStaticImport(
|
||||
MemberSelectTree tree, String fullyQualifiedName, String simpleName, VisitorState state) {
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder().replace(tree, simpleName);
|
||||
|
||||
if (!simpleName.equals(SuggestedFixes.qualifyStaticImport(fullyQualifiedName, fix, state))) {
|
||||
/* Statically importing this symbol would clash with an existing import. */
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
return Optional.of(fix.build());
|
||||
return fix.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,19 +73,18 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
|
||||
* because the latter are not syntactically valid or ambiguous. Rather than encoding all these
|
||||
* edge cases we try to compile the code with the suggested fix, to see whether this works.
|
||||
*/
|
||||
return constructMethodRef(tree, tree.getBody())
|
||||
.map(SuggestedFix.Builder::build)
|
||||
.filter(
|
||||
fix ->
|
||||
SuggestedFixes.compilesWithFix(
|
||||
fix, state, ImmutableList.of(), /* onlyInSameCompilationUnit= */ true))
|
||||
.map(fix -> describeMatch(tree, fix))
|
||||
.orElse(Description.NO_MATCH);
|
||||
SuggestedFix fix = constructMethodRef(tree, tree.getBody());
|
||||
if (fix.isEmpty()
|
||||
|| !SuggestedFixes.compilesWithFix(
|
||||
fix, state, ImmutableList.of(), /* onlyInSameCompilationUnit= */ true)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
return describeMatch(tree, fix);
|
||||
}
|
||||
|
||||
// XXX: Use switch pattern matching once the targeted JDK supports this.
|
||||
private static Optional<SuggestedFix.Builder> constructMethodRef(
|
||||
LambdaExpressionTree lambdaExpr, Tree subTree) {
|
||||
private static SuggestedFix constructMethodRef(LambdaExpressionTree lambdaExpr, Tree subTree) {
|
||||
return switch (subTree.getKind()) {
|
||||
case BLOCK -> constructMethodRef(lambdaExpr, (BlockTree) subTree);
|
||||
case EXPRESSION_STATEMENT ->
|
||||
@@ -94,27 +93,28 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
|
||||
case PARENTHESIZED ->
|
||||
constructMethodRef(lambdaExpr, ((ParenthesizedTree) subTree).getExpression());
|
||||
case RETURN -> constructMethodRef(lambdaExpr, ((ReturnTree) subTree).getExpression());
|
||||
default -> Optional.empty();
|
||||
default -> SuggestedFix.emptyFix();
|
||||
};
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix.Builder> constructMethodRef(
|
||||
private static SuggestedFix constructMethodRef(
|
||||
LambdaExpressionTree lambdaExpr, BlockTree subTree) {
|
||||
return Optional.of(subTree.getStatements())
|
||||
.filter(statements -> statements.size() == 1)
|
||||
.flatMap(statements -> constructMethodRef(lambdaExpr, statements.get(0)));
|
||||
.map(statements -> constructMethodRef(lambdaExpr, statements.get(0)))
|
||||
.orElseGet(SuggestedFix::emptyFix);
|
||||
}
|
||||
|
||||
// XXX: Replace nested `Optional` usage.
|
||||
@SuppressWarnings("NestedOptionals")
|
||||
private static Optional<SuggestedFix.Builder> constructMethodRef(
|
||||
private static SuggestedFix constructMethodRef(
|
||||
LambdaExpressionTree lambdaExpr, MethodInvocationTree subTree) {
|
||||
return matchArguments(lambdaExpr, subTree)
|
||||
.flatMap(expectedInstance -> constructMethodRef(lambdaExpr, subTree, expectedInstance));
|
||||
.map(expectedInstance -> constructMethodRef(lambdaExpr, subTree, expectedInstance))
|
||||
.orElseGet(SuggestedFix::emptyFix);
|
||||
}
|
||||
|
||||
// XXX: Review whether to use switch pattern matching once the targeted JDK supports this.
|
||||
private static Optional<SuggestedFix.Builder> constructMethodRef(
|
||||
private static SuggestedFix constructMethodRef(
|
||||
LambdaExpressionTree lambdaExpr,
|
||||
MethodInvocationTree subTree,
|
||||
Optional<Name> expectedInstance) {
|
||||
@@ -123,7 +123,7 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
|
||||
if (methodSelect instanceof IdentifierTree) {
|
||||
if (expectedInstance.isPresent()) {
|
||||
/* Direct method call; there is no matching "implicit parameter". */
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
Symbol sym = ASTHelpers.getSymbol(methodSelect);
|
||||
@@ -139,7 +139,7 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
|
||||
throw new VerifyException("Unexpected type of expression: " + methodSelect.getKind());
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix.Builder> constructMethodRef(
|
||||
private static SuggestedFix constructMethodRef(
|
||||
LambdaExpressionTree lambdaExpr, MemberSelectTree subTree, Optional<Name> expectedInstance) {
|
||||
if (!(subTree.getExpression() instanceof IdentifierTree identifier)) {
|
||||
// XXX: Could be parenthesized. Handle. Also in other classes.
|
||||
@@ -147,7 +147,7 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
|
||||
* Only suggest a replacement if the method select's expression provably doesn't have
|
||||
* side-effects. Otherwise the replacement may not be behavior preserving.
|
||||
*/
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
Name lhs = identifier.getName();
|
||||
@@ -157,7 +157,7 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
|
||||
|
||||
Type lhsType = ASTHelpers.getType(identifier);
|
||||
if (lhsType == null || !expectedInstance.orElseThrow().equals(lhs)) {
|
||||
return Optional.empty();
|
||||
return SuggestedFix.emptyFix();
|
||||
}
|
||||
|
||||
// XXX: Dropping generic type information is in most cases fine or even more likely to yield a
|
||||
@@ -195,23 +195,23 @@ public final class MethodReferenceUsage extends BugChecker implements LambdaExpr
|
||||
|
||||
// XXX: Resolve this suppression.
|
||||
@SuppressWarnings("UnqualifiedSuggestedFixImport")
|
||||
private static Optional<SuggestedFix.Builder> constructFix(
|
||||
private static SuggestedFix constructFix(
|
||||
LambdaExpressionTree lambdaExpr, Symbol target, Object methodName) {
|
||||
Name sName = target.getSimpleName();
|
||||
Optional<SuggestedFix.Builder> fix = constructFix(lambdaExpr, sName, methodName);
|
||||
SuggestedFix fix = constructFix(lambdaExpr, sName, methodName);
|
||||
|
||||
if (!"java.lang".equals(ASTHelpers.enclosingPackage(target).toString())) {
|
||||
Name fqName = target.getQualifiedName();
|
||||
if (!sName.equals(fqName)) {
|
||||
return fix.map(b -> b.addImport(fqName.toString()));
|
||||
return fix.toBuilder().addImport(fqName.toString()).build();
|
||||
}
|
||||
}
|
||||
|
||||
return fix;
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix.Builder> constructFix(
|
||||
private static SuggestedFix constructFix(
|
||||
LambdaExpressionTree lambdaExpr, Object target, Object methodName) {
|
||||
return Optional.of(SuggestedFix.builder().replace(lambdaExpr, target + "::" + methodName));
|
||||
return SuggestedFix.replace(lambdaExpr, target + "::" + methodName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import javax.lang.model.element.Name;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.utils.SourceCode;
|
||||
@@ -74,12 +73,11 @@ public final class RefasterMethodParameterOrder extends BugChecker implements Cl
|
||||
|
||||
Comparator<VariableTree> canonicalOrder = determineCanonicalParameterOrder(methods);
|
||||
|
||||
return methods.stream()
|
||||
.flatMap(m -> tryReorderParameters(m, canonicalOrder, state))
|
||||
.reduce(SuggestedFix.Builder::merge)
|
||||
.map(SuggestedFix.Builder::build)
|
||||
.map(fix -> describeMatch(tree, fix))
|
||||
.orElse(Description.NO_MATCH);
|
||||
SuggestedFix fix =
|
||||
methods.stream()
|
||||
.map(m -> tryReorderParameters(m, canonicalOrder, state))
|
||||
.collect(SuggestedFix.mergeFixes());
|
||||
return fix.isEmpty() ? Description.NO_MATCH : describeMatch(tree, fix);
|
||||
}
|
||||
|
||||
private static ImmutableList<MethodTree> getMethodsByPriority(
|
||||
@@ -123,17 +121,18 @@ public final class RefasterMethodParameterOrder extends BugChecker implements Cl
|
||||
}.scan(method, null);
|
||||
}
|
||||
|
||||
private static Stream<SuggestedFix.Builder> tryReorderParameters(
|
||||
private static SuggestedFix tryReorderParameters(
|
||||
MethodTree method, Comparator<VariableTree> canonicalOrder, VisitorState state) {
|
||||
List<? extends VariableTree> originalOrder = method.getParameters();
|
||||
ImmutableList<? extends VariableTree> orderedParams =
|
||||
ImmutableList.sortedCopyOf(canonicalOrder, originalOrder);
|
||||
|
||||
return originalOrder.equals(orderedParams)
|
||||
? Stream.empty()
|
||||
? SuggestedFix.emptyFix()
|
||||
: Streams.zip(
|
||||
originalOrder.stream(),
|
||||
orderedParams.stream().map(p -> SourceCode.treeToString(p, state)),
|
||||
SuggestedFix.builder()::replace);
|
||||
originalOrder.stream(),
|
||||
orderedParams.stream().map(p -> SourceCode.treeToString(p, state)),
|
||||
SuggestedFix::replace)
|
||||
.collect(SuggestedFix.mergeFixes());
|
||||
}
|
||||
}
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -211,7 +211,7 @@
|
||||
<version.auto-value>1.11.0</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.35.1</version.error-prone-orig>
|
||||
<version.error-prone-orig>1.0-HEAD-SNAPSHOT</version.error-prone-orig>
|
||||
<version.error-prone-slf4j>0.1.28</version.error-prone-slf4j>
|
||||
<version.guava-beta-checker>1.0</version.guava-beta-checker>
|
||||
<version.jdk>17</version.jdk>
|
||||
|
||||
Reference in New Issue
Block a user