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