Compare commits

...

1 Commits

Author SHA1 Message Date
Stephan Schroevers
728814aa6a WIP (Use new SuggestedFix.mergeFixes()) 2024-12-01 15:53:55 +01:00
10 changed files with 112 additions and 127 deletions

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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());
}
}

View File

@@ -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();

View File

@@ -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(

View File

@@ -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()

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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>