Skip to content

Commit

Permalink
Change SuggestedFixes.{addModifiers,removeModifiers} to return an Opt…
Browse files Browse the repository at this point in the history
…ional.

RELNOTES: Changed SuggestedFixes.{addModifiers,removeModifiers} to return
an Optional.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=173177755
  • Loading branch information
eaftan authored and cushon committed Oct 25, 2017
1 parent 9fd7bd3 commit ec64a7c
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 40 deletions.
Expand Up @@ -86,6 +86,7 @@
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import javax.annotation.CheckReturnValue; import javax.annotation.CheckReturnValue;


Expand Down Expand Up @@ -117,6 +118,12 @@ protected Description describeMatch(Tree node) {
return buildDescription(node).build(); return buildDescription(node).build();
} }


/** Helper to create a Description for the common case where there is an {@link Optional} fix. */
@CheckReturnValue
protected Description describeMatch(Tree node, Optional<? extends Fix> fix) {
return buildDescription(node).addFix(fix).build();
}

/** /**
* Returns a Description builder, which allows you to customize the diagnostic with a custom * Returns a Description builder, which allows you to customize the diagnostic with a custom
* message or multiple fixes. * message or multiple fixes.
Expand Down
Expand Up @@ -29,7 +29,6 @@


import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable; import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -85,6 +84,7 @@
import java.util.Deque; import java.util.Deque;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.TreeSet; import java.util.TreeSet;
Expand Down Expand Up @@ -139,12 +139,12 @@ private static Modifier getTokModifierKind(ErrorProneToken tok) {
} }
} }


/** Add modifiers to the given class, method, or field declaration. */ /** Adds modifiers to the given class, method, or field declaration. */
@Nullable public static Optional<SuggestedFix> addModifiers(
public static SuggestedFix addModifiers(Tree tree, VisitorState state, Modifier... modifiers) { Tree tree, VisitorState state, Modifier... modifiers) {
ModifiersTree originalModifiers = getModifiers(tree); ModifiersTree originalModifiers = getModifiers(tree);
if (originalModifiers == null) { if (originalModifiers == null) {
return null; return Optional.empty();
} }
Set<Modifier> toAdd = Set<Modifier> toAdd =
Sets.difference(new TreeSet<>(Arrays.asList(modifiers)), originalModifiers.getFlags()); Sets.difference(new TreeSet<>(Arrays.asList(modifiers)), originalModifiers.getFlags());
Expand All @@ -154,15 +154,16 @@ public static SuggestedFix addModifiers(Tree tree, VisitorState state, Modifier.
? state.getEndPosition(originalModifiers) + 1 ? state.getEndPosition(originalModifiers) + 1
: ((JCTree) tree).getStartPosition(); : ((JCTree) tree).getStartPosition();
int base = ((JCTree) tree).getStartPosition(); int base = ((JCTree) tree).getStartPosition();
java.util.Optional<Integer> insert = Optional<Integer> insert =
state state
.getTokensForNode(tree) .getTokensForNode(tree)
.stream() .stream()
.map(token -> token.pos() + base) .map(token -> token.pos() + base)
.filter(thisPos -> thisPos >= pos) .filter(thisPos -> thisPos >= pos)
.findFirst(); .findFirst();
int insertPos = insert.orElse(pos); // shouldn't ever be able to get to the else int insertPos = insert.orElse(pos); // shouldn't ever be able to get to the else
return SuggestedFix.replace(insertPos, insertPos, Joiner.on(' ').join(toAdd) + " "); return Optional.of(
SuggestedFix.replace(insertPos, insertPos, Joiner.on(' ').join(toAdd) + " "));
} }
// a map from modifiers to modifier position (or -1 if the modifier is being added) // a map from modifiers to modifier position (or -1 if the modifier is being added)
// modifiers are sorted in Google Java Style order // modifiers are sorted in Google Java Style order
Expand Down Expand Up @@ -194,16 +195,16 @@ public static SuggestedFix addModifiers(Tree tree, VisitorState state, Modifier.
if (!modifiersToWrite.isEmpty()) { if (!modifiersToWrite.isEmpty()) {
fix.postfixWith(originalModifiers, " " + Joiner.on(' ').join(modifiersToWrite)); fix.postfixWith(originalModifiers, " " + Joiner.on(' ').join(modifiersToWrite));
} }
return fix.build(); return Optional.of(fix.build());
} }


/** Remove modifiers from the given class, method, or field declaration. */ /** Remove modifiers from the given class, method, or field declaration. */
@Nullable public static Optional<SuggestedFix> removeModifiers(
public static SuggestedFix removeModifiers(Tree tree, VisitorState state, Modifier... modifiers) { Tree tree, VisitorState state, Modifier... modifiers) {
Set<Modifier> toRemove = ImmutableSet.copyOf(modifiers); Set<Modifier> toRemove = ImmutableSet.copyOf(modifiers);
ModifiersTree originalModifiers = getModifiers(tree); ModifiersTree originalModifiers = getModifiers(tree);
if (originalModifiers == null) { if (originalModifiers == null) {
return null; return Optional.empty();
} }
SuggestedFix.Builder fix = SuggestedFix.builder(); SuggestedFix.Builder fix = SuggestedFix.builder();
List<ErrorProneToken> tokens = state.getTokensForNode(originalModifiers); List<ErrorProneToken> tokens = state.getTokensForNode(originalModifiers);
Expand All @@ -218,9 +219,9 @@ public static SuggestedFix removeModifiers(Tree tree, VisitorState state, Modifi
} }
} }
if (empty) { if (empty) {
return null; return Optional.empty();
} }
return fix.build(); return Optional.of(fix.build());
} }


/** /**
Expand Down Expand Up @@ -607,7 +608,7 @@ private static Optional<ExpressionTree> findArgument(
} }
} }
} }
return Optional.absent(); return Optional.empty();
} }


/** /**
Expand Down
Expand Up @@ -16,7 +16,7 @@


package com.google.errorprone.matchers; package com.google.errorprone.matchers;


import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION; import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;


Expand All @@ -29,6 +29,7 @@
import com.google.errorprone.fixes.Fix; import com.google.errorprone.fixes.Fix;
import com.sun.source.tree.Tree; import com.sun.source.tree.Tree;
import java.util.List; import java.util.List;
import java.util.Optional;
import javax.annotation.CheckReturnValue; import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable; import javax.annotation.Nullable;


Expand Down Expand Up @@ -178,21 +179,34 @@ private Builder(
* @throws IllegalArgumentException if {@code fix} is {@code null} * @throws IllegalArgumentException if {@code fix} is {@code null}
*/ */
public Builder addFix(Fix fix) { public Builder addFix(Fix fix) {
checkArgument(fix != null, "fix must not be null"); checkNotNull(fix, "fix must not be null");
if (!fix.isEmpty()) { if (!fix.isEmpty()) {
fixListBuilder.add(fix); fixListBuilder.add(fix);
} }
return this; return this;
} }


/**
* Adds a suggested fix for this {@code Description} if {@code fix} is present. Fixes should be
* added in order of decreasing preference. Adding an empty fix is a no-op.
*
* @param fix a suggested fix for this problem
* @throws IllegalArgumentException if {@code fix} is {@code null}
*/
public Builder addFix(Optional<? extends Fix> fix) {
checkNotNull(fix, "fix must not be null");
fix.ifPresent(this::addFix);
return this;
}

/** /**
* Add each fix in order. * Add each fix in order.
* *
* @param fixes a list of suggested fixes for this problem * @param fixes a list of suggested fixes for this problem
* @throws IllegalArgumentException if {@code fixes} or any of its elements are {@code null} * @throws IllegalArgumentException if {@code fixes} or any of its elements are {@code null}
*/ */
public Builder addAllFixes(List<? extends Fix> fixes) { public Builder addAllFixes(List<? extends Fix> fixes) {
checkArgument(fixes != null, "fixes must not be null"); checkNotNull(fixes, "fixes must not be null");
for (Fix fix : fixes) { for (Fix fix : fixes) {
addFix(fix); addFix(fix);
} }
Expand Down
Expand Up @@ -119,12 +119,12 @@ public Description matchMethod(MethodTree methodTree, VisitorState state) {
} }


// Make method public (if not already public). // Make method public (if not already public).
fixes.add(addModifiers(methodTree, state, Modifier.PUBLIC)); addModifiers(methodTree, state, Modifier.PUBLIC).ifPresent(fixes::add);
// Remove any other visibility modifiers (if present). // Remove any other visibility modifiers (if present).
fixes.add(removeModifiers(methodTree, state, Modifier.PRIVATE, Modifier.PROTECTED)); removeModifiers(methodTree, state, Modifier.PRIVATE, Modifier.PROTECTED).ifPresent(fixes::add);
// Remove static modifier (if present). // Remove static modifier (if present).
// N.B. must occur in separate step because removeModifiers only removes one modifier at a time. // N.B. must occur in separate step because removeModifiers only removes one modifier at a time.
fixes.add(removeModifiers(methodTree, state, Modifier.STATIC)); removeModifiers(methodTree, state, Modifier.STATIC).ifPresent(fixes::add);


return describeMatch(methodTree, mergeFixes(fixes)); return describeMatch(methodTree, mergeFixes(fixes));
} }
Expand Down
Expand Up @@ -48,6 +48,7 @@
import com.sun.source.util.TreeScanner; import com.sun.source.util.TreeScanner;
import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol;
import java.util.List; import java.util.List;
import java.util.Optional;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;


/** @author eaftan@google.com (Eddie Aftandilian) */ /** @author eaftan@google.com (Eddie Aftandilian) */
Expand Down Expand Up @@ -178,10 +179,11 @@ public Boolean reduce(Boolean r1, Boolean r2) {
* </ol> * </ol>
*/ */
private Description describeFixes(MethodTree methodTree, VisitorState state) { private Description describeFixes(MethodTree methodTree, VisitorState state) {
SuggestedFix removeStatic = SuggestedFixes.removeModifiers(methodTree, state, Modifier.STATIC); Optional<SuggestedFix> removeStatic =
SuggestedFixes.removeModifiers(methodTree, state, Modifier.STATIC);
SuggestedFix testFix = SuggestedFix testFix =
SuggestedFix.builder() SuggestedFix.builder()
.merge(removeStatic) .merge(removeStatic.orElse(null))
.addImport(TEST_CLASS) .addImport(TEST_CLASS)
.prefixWith(methodTree, TEST_ANNOTATION) .prefixWith(methodTree, TEST_ANNOTATION)
.build(); .build();
Expand All @@ -194,8 +196,8 @@ private Description describeFixes(MethodTree methodTree, VisitorState state) {


SuggestedFix visibilityFix = SuggestedFix visibilityFix =
SuggestedFix.builder() SuggestedFix.builder()
.merge(SuggestedFixes.removeModifiers(methodTree, state, Modifier.PUBLIC)) .merge(SuggestedFixes.removeModifiers(methodTree, state, Modifier.PUBLIC).orElse(null))
.merge(SuggestedFixes.addModifiers(methodTree, state, Modifier.PRIVATE)) .merge(SuggestedFixes.addModifiers(methodTree, state, Modifier.PRIVATE).orElse(null))
.build(); .build();


// Suggest @Ignore first if test method is named like a purposely disabled test. // Suggest @Ignore first if test method is named like a purposely disabled test.
Expand Down
Expand Up @@ -38,6 +38,7 @@
import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Optional;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;


/** @author cushon@google.com (Liam Miller-Cushon) */ /** @author cushon@google.com (Liam Miller-Cushon) */
Expand Down Expand Up @@ -91,10 +92,10 @@ private Description handleLocalOrParam(VariableTree tree, VisitorState state, Sy
if (Source.instance(state.context).allowEffectivelyFinalInInnerClasses()) { if (Source.instance(state.context).allowEffectivelyFinalInInnerClasses()) {
// In Java 8, the final modifier is never necessary on locals/parameters because // In Java 8, the final modifier is never necessary on locals/parameters because
// effectively final variables can be used anywhere a final variable is required. // effectively final variables can be used anywhere a final variable is required.
Fix fix = SuggestedFixes.removeModifiers(tree, state, Modifier.FINAL); Optional<SuggestedFix> fix = SuggestedFixes.removeModifiers(tree, state, Modifier.FINAL);
// The fix may be null for TWR variables that were not explicitly final // The fix may not be present for TWR variables that were not explicitly final
if (fix != null) { if (fix.isPresent()) {
return buildDescription(tree).setMessage(UNNECESSARY_FINAL).addFix(fix).build(); return buildDescription(tree).setMessage(UNNECESSARY_FINAL).addFix(fix.get()).build();
} }
} }
return Description.NO_MATCH; return Description.NO_MATCH;
Expand Down
Expand Up @@ -28,7 +28,6 @@
import com.google.errorprone.VisitorState; import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker; import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.IfTreeMatcher; import com.google.errorprone.bugpatterns.BugChecker.IfTreeMatcher;
import com.google.errorprone.fixes.Fix;
import com.google.errorprone.fixes.SuggestedFixes; import com.google.errorprone.fixes.SuggestedFixes;
import com.google.errorprone.matchers.Description; import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers; import com.google.errorprone.util.ASTHelpers;
Expand Down Expand Up @@ -98,10 +97,7 @@ private Description handleField(IfTree outerIf, VarSymbol sym, VisitorState stat
Description.Builder builder = buildDescription(outerIf); Description.Builder builder = buildDescription(outerIf);
JCTree fieldDecl = findFieldDeclaration(state.getPath(), sym); JCTree fieldDecl = findFieldDeclaration(state.getPath(), sym);
if (fieldDecl != null) { if (fieldDecl != null) {
Fix fix = SuggestedFixes.addModifiers(fieldDecl, state, Modifier.VOLATILE); builder.addFix(SuggestedFixes.addModifiers(fieldDecl, state, Modifier.VOLATILE));
if (fix != null) {
builder.addFix(fix);
}
} }
return builder.build(); return builder.build();
} }
Expand Down
Expand Up @@ -29,6 +29,7 @@
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode; import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.BugPattern; import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.Category; import com.google.errorprone.BugPattern.Category;
import com.google.errorprone.BugPattern.ProvidesFix;
import com.google.errorprone.BugPattern.SeverityLevel; import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.CompilationTestHelper; import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.VisitorState; import com.google.errorprone.VisitorState;
Expand All @@ -55,6 +56,7 @@
import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.util.Optional;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
Expand Down Expand Up @@ -111,7 +113,7 @@ private Description editModifiers(Tree tree, VisitorState state) {
ASTHelpers.findEnclosingNode(state.getPath(), ClassTree.class), EditModifiers.class); ASTHelpers.findEnclosingNode(state.getPath(), ClassTree.class), EditModifiers.class);
Modifier mod = MODIFIERS_BY_NAME.get(editModifiers.value()); Modifier mod = MODIFIERS_BY_NAME.get(editModifiers.value());
Verify.verifyNotNull(mod, editModifiers.value()); Verify.verifyNotNull(mod, editModifiers.value());
Fix fix; Optional<SuggestedFix> fix;
switch (editModifiers.kind()) { switch (editModifiers.kind()) {
case ADD: case ADD:
fix = SuggestedFixes.addModifiers(tree, state, mod); fix = SuggestedFixes.addModifiers(tree, state, mod);
Expand Down Expand Up @@ -234,7 +236,8 @@ public void removeModifiers() {
category = Category.ONE_OFF, category = Category.ONE_OFF,
name = "CastReturn", name = "CastReturn",
severity = SeverityLevel.ERROR, severity = SeverityLevel.ERROR,
summary = "Adds casts to returned expressions" summary = "Adds casts to returned expressions",
providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION
) )
public static class CastReturn extends BugChecker implements ReturnTreeMatcher { public static class CastReturn extends BugChecker implements ReturnTreeMatcher {


Expand All @@ -257,7 +260,8 @@ public Description matchReturn(ReturnTree tree, VisitorState state) {
category = Category.ONE_OFF, category = Category.ONE_OFF,
name = "CastReturn", name = "CastReturn",
severity = SeverityLevel.ERROR, severity = SeverityLevel.ERROR,
summary = "Adds casts to returned expressions" summary = "Adds casts to returned expressions",
providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION
) )
public static class CastReturnFullType extends BugChecker implements ReturnTreeMatcher { public static class CastReturnFullType extends BugChecker implements ReturnTreeMatcher {


Expand Down Expand Up @@ -395,7 +399,8 @@ public void fullQualifiedName_typeVariable() {
name = "JavadocQualifier", name = "JavadocQualifier",
category = BugPattern.Category.JDK, category = BugPattern.Category.JDK,
summary = "all javadoc links should be qualified", summary = "all javadoc links should be qualified",
severity = ERROR severity = ERROR,
providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION
) )
public static class JavadocQualifier extends BugChecker implements BugChecker.ClassTreeMatcher { public static class JavadocQualifier extends BugChecker implements BugChecker.ClassTreeMatcher {
@Override @Override
Expand Down Expand Up @@ -445,7 +450,13 @@ public void qualifyJavadocTest() throws Exception {
.doTest(TEXT_MATCH); .doTest(TEXT_MATCH);
} }


@BugPattern(name = "SuppressMe", category = Category.ONE_OFF, summary = "", severity = ERROR) @BugPattern(
name = "SuppressMe",
category = Category.ONE_OFF,
summary = "",
severity = ERROR,
providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION
)
static final class SuppressMe extends BugChecker implements LiteralTreeMatcher { static final class SuppressMe extends BugChecker implements LiteralTreeMatcher {
@Override @Override
public Description matchLiteral(LiteralTree tree, VisitorState state) { public Description matchLiteral(LiteralTree tree, VisitorState state) {
Expand Down Expand Up @@ -497,7 +508,13 @@ public void testSuppressWarningsFix() throws IOException {
} }


/** A test bugchecker that deletes any field whose removal doesn't break the compilation. */ /** A test bugchecker that deletes any field whose removal doesn't break the compilation. */
@BugPattern(name = "CompilesWithFixChecker", category = JDK, summary = "", severity = ERROR) @BugPattern(
name = "CompilesWithFixChecker",
category = JDK,
summary = "",
severity = ERROR,
providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION
)
public static class CompilesWithFixChecker extends BugChecker implements VariableTreeMatcher { public static class CompilesWithFixChecker extends BugChecker implements VariableTreeMatcher {
@Override @Override
public Description matchVariable(VariableTree tree, VisitorState state) { public Description matchVariable(VariableTree tree, VisitorState state) {
Expand Down Expand Up @@ -532,7 +549,13 @@ public void compilesWithFixTest() throws IOException {
} }


/** A test bugchecker that deletes an exception from throws. */ /** A test bugchecker that deletes an exception from throws. */
@BugPattern(name = "RemovesExceptionChecker", category = JDK, summary = "", severity = ERROR) @BugPattern(
name = "RemovesExceptionChecker",
category = JDK,
summary = "",
severity = ERROR,
providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION
)
public static class RemovesExceptionsChecker extends BugChecker implements MethodTreeMatcher { public static class RemovesExceptionsChecker extends BugChecker implements MethodTreeMatcher {


private final int index; private final int index;
Expand Down

0 comments on commit ec64a7c

Please sign in to comment.