|
22 | 22 | import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
23 | 23 | import static com.google.errorprone.matchers.Matchers.staticMethod;
|
24 | 24 |
|
| 25 | +import com.google.common.collect.ImmutableList; |
25 | 26 | import com.google.errorprone.BugPattern;
|
26 | 27 | import com.google.errorprone.VisitorState;
|
27 | 28 | import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
|
| 29 | +import com.google.errorprone.fixes.SuggestedFix; |
| 30 | +import com.google.errorprone.fixes.SuggestedFixes; |
28 | 31 | import com.google.errorprone.matchers.Description;
|
29 | 32 | import com.google.errorprone.matchers.Matcher;
|
30 | 33 | import com.google.errorprone.util.ASTHelpers;
|
| 34 | +import com.sun.source.tree.ExpressionStatementTree; |
31 | 35 | import com.sun.source.tree.ExpressionTree;
|
32 | 36 | import com.sun.source.tree.MethodInvocationTree;
|
| 37 | +import com.sun.source.tree.Tree; |
| 38 | +import com.sun.tools.javac.util.Name; |
33 | 39 |
|
34 | 40 | /**
|
35 | 41 | * Checks if {@code Optional#of} is chained with a redundant method.
|
|
58 | 64 | severity = ERROR)
|
59 | 65 | public class OptionalOfRedundantMethod extends BugChecker implements MethodInvocationTreeMatcher {
|
60 | 66 |
|
| 67 | + private static final Matcher<ExpressionTree> GUAVA_OPTIONAL_OF_MATCHER = |
| 68 | + staticMethod().onClass("com.google.common.base.Optional").named("of"); |
| 69 | + |
61 | 70 | private static final Matcher<ExpressionTree> OPTIONAL_OF_MATCHER =
|
62 |
| - anyOf( |
63 |
| - staticMethod().onClass("java.util.Optional").named("of"), |
64 |
| - staticMethod().onClass("com.google.common.base.Optional").named("of")); |
| 71 | + anyOf(staticMethod().onClass("java.util.Optional").named("of"), GUAVA_OPTIONAL_OF_MATCHER); |
65 | 72 |
|
66 | 73 | private static final Matcher<ExpressionTree> REDUNDANT_METHOD_MATCHER =
|
67 | 74 | anyOf(
|
@@ -90,6 +97,47 @@ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState
|
90 | 97 | "Optional.of() always returns a non-empty Optional. Using '%s' method on it is"
|
91 | 98 | + " unnecessary and most probably a bug.",
|
92 | 99 | methodName))
|
| 100 | + .addAllFixes(getSuggestedFixes(tree, state)) |
93 | 101 | .build();
|
94 | 102 | }
|
| 103 | + |
| 104 | + private ImmutableList<SuggestedFix> getSuggestedFixes( |
| 105 | + MethodInvocationTree tree, VisitorState state) { |
| 106 | + MethodInvocationTree optionalOfInvocationTree = |
| 107 | + (MethodInvocationTree) ASTHelpers.getReceiver(tree); |
| 108 | + String nullableMethodName = |
| 109 | + GUAVA_OPTIONAL_OF_MATCHER.matches(optionalOfInvocationTree, state) |
| 110 | + ? "fromNullable" |
| 111 | + : "ofNullable"; |
| 112 | + |
| 113 | + ImmutableList.Builder<SuggestedFix> fixesBuilder = ImmutableList.builder(); |
| 114 | + fixesBuilder.add( |
| 115 | + SuggestedFixes.renameMethodInvocation(optionalOfInvocationTree, nullableMethodName, state)); |
| 116 | + |
| 117 | + if (state.getPath().getParentPath().getLeaf() instanceof ExpressionStatementTree) { |
| 118 | + return fixesBuilder.build(); |
| 119 | + } |
| 120 | + |
| 121 | + Name methodSimpleName = ASTHelpers.getSymbol(tree).getSimpleName(); |
| 122 | + if (methodSimpleName.contentEquals("orElse") |
| 123 | + || methodSimpleName.contentEquals("orElseGet") |
| 124 | + || methodSimpleName.contentEquals("orElseThrow") |
| 125 | + || methodSimpleName.contentEquals("or") |
| 126 | + || methodSimpleName.contentEquals("orNull")) { |
| 127 | + Tree argument = optionalOfInvocationTree.getArguments().get(0); |
| 128 | + SuggestedFix.Builder fixBuilder = |
| 129 | + SuggestedFix.builder().replace(tree, state.getSourceForNode(argument)); |
| 130 | + fixBuilder.setShortDescription("Simplify expression."); |
| 131 | + if (methodSimpleName.contentEquals("orElse")) { |
| 132 | + fixBuilder.setShortDescription( |
| 133 | + "Simplify expression. Note that this may change semantics if arguments have side" |
| 134 | + + " effects"); |
| 135 | + } |
| 136 | + fixesBuilder.add(fixBuilder.build()); |
| 137 | + } else if (methodSimpleName.contentEquals("isPresent")) { |
| 138 | + fixesBuilder.add(SuggestedFix.builder().replace(tree, "true").build()); |
| 139 | + } |
| 140 | + // TODO(b/192550897): Add suggested fix for ifpresent |
| 141 | + return fixesBuilder.build(); |
| 142 | + } |
95 | 143 | }
|
0 commit comments