Skip to content

Commit

Permalink
Refactor Scanner's suppression code a bit to use an immutable suppres…
Browse files Browse the repository at this point in the history
…sion info

object as opposed to holding onto different bits and pieces of the suppression
individually, as well as resolve some TODO's about avoiding garbage.

RELNOTES: N/A

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=226030707
  • Loading branch information
nick-someone authored and ronshapiro committed Dec 24, 2018
1 parent dee5774 commit b56cdf3
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 82 deletions.
Expand Up @@ -18,6 +18,7 @@


import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.errorprone.BugPattern.SeverityLevel; import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.matchers.Suppressible; import com.google.errorprone.matchers.Suppressible;
import com.google.errorprone.util.ASTHelpers; import com.google.errorprone.util.ASTHelpers;
import com.sun.tools.javac.code.Attribute; import com.sun.tools.javac.code.Attribute;
Expand Down Expand Up @@ -59,21 +60,22 @@ public SuppressionHelper(Set<Class<? extends Annotation>> customSuppressionAnnot
this.customSuppressionAnnotations = customSuppressionAnnotations; this.customSuppressionAnnotations = customSuppressionAnnotations;
} }


/** /** Immutable container for information about currently-known-about suppressions. */
* Container for information about suppressions. Either reference field may be null, which @Immutable
* indicates that the suppression sets are unchanged.
*/
public static class SuppressionInfo { public static class SuppressionInfo {
public Set<String> suppressWarningsStrings; public static final SuppressionInfo EMPTY =
public Set<Class<? extends Annotation>> customSuppressions; new SuppressionInfo(ImmutableSet.of(), ImmutableSet.of(), false);
public boolean inGeneratedCode;
public final ImmutableSet<String> suppressWarningsStrings;
public final ImmutableSet<Class<? extends Annotation>> customSuppressions;
public final boolean inGeneratedCode;


public SuppressionInfo( private SuppressionInfo(
Set<String> suppressWarningsStrings, Set<String> suppressWarningsStrings,
Set<Class<? extends Annotation>> customSuppressions, Set<Class<? extends Annotation>> customSuppressions,
boolean inGeneratedCode) { boolean inGeneratedCode) {
this.suppressWarningsStrings = suppressWarningsStrings; this.suppressWarningsStrings = ImmutableSet.copyOf(suppressWarningsStrings);
this.customSuppressions = customSuppressions; this.customSuppressions = ImmutableSet.copyOf(customSuppressions);
this.inGeneratedCode = inGeneratedCode; this.inGeneratedCode = inGeneratedCode;
} }
} }
Expand All @@ -91,87 +93,100 @@ public SuppressionInfo(
* @param sym The {@code Symbol} for the AST node currently being scanned * @param sym The {@code Symbol} for the AST node currently being scanned
* @param suppressWarningsType The {@code Type} for {@code @SuppressWarnings}, as given by javac's * @param suppressWarningsType The {@code Type} for {@code @SuppressWarnings}, as given by javac's
* symbol table * symbol table
* @param suppressionsOnCurrentPath The set of strings in all {@code @SuppressWarnings} * @param toExtend The suppression info to extend from.
* annotations on the current path through the AST * @param state VisitorState for checking the current tree
* @param customSuppressionsOnCurrentPath The set of all custom suppression annotations
*/ */
public SuppressionInfo extendSuppressionSets( public SuppressionInfo extendSuppressionSets(
Symbol sym, Symbol sym, Type suppressWarningsType, SuppressionInfo toExtend, VisitorState state) {
Type suppressWarningsType, boolean newInGeneratedCode = toExtend.inGeneratedCode || isGenerated(sym, state);
Set<String> suppressionsOnCurrentPath, boolean anyModification = newInGeneratedCode != toExtend.inGeneratedCode;
Set<Class<? extends Annotation>> customSuppressionsOnCurrentPath,
boolean inGeneratedCode,
VisitorState state) {


boolean newInGeneratedCode = inGeneratedCode || isGenerated(sym, state); /* Handle custom suppression annotations. */

/** Handle custom suppression annotations. */
Set<Class<? extends Annotation>> newCustomSuppressions = null; Set<Class<? extends Annotation>> newCustomSuppressions = null;
for (Class<? extends Annotation> annotationType : customSuppressionAnnotations) { for (Class<? extends Annotation> annotationType : customSuppressionAnnotations) {
// Don't need to check already-suppressed annos
if (toExtend.customSuppressions.contains(annotationType)) {
continue;
}
if (ASTHelpers.hasAnnotation(sym, annotationType, state)) { if (ASTHelpers.hasAnnotation(sym, annotationType, state)) {
anyModification = true;
if (newCustomSuppressions == null) { if (newCustomSuppressions == null) {
newCustomSuppressions = new HashSet<>(customSuppressionsOnCurrentPath); newCustomSuppressions = new HashSet<>(toExtend.customSuppressions);
} }
newCustomSuppressions.add(annotationType); newCustomSuppressions.add(annotationType);
} }
} }


/** Handle {@code @SuppressWarnings} and {@code @SuppressLint}. */ /* Handle {@code @SuppressWarnings} and {@code @SuppressLint}. */
Set<String> newSuppressions = null; Set<String> newSuppressions = null;
// Iterate over annotations on this symbol, looking for SuppressWarnings // Iterate over annotations on this symbol, looking for SuppressWarnings
for (Attribute.Compound attr : sym.getAnnotationMirrors()) { for (Attribute.Compound attr : sym.getAnnotationMirrors()) {
if ((attr.type.tsym == suppressWarningsType.tsym) if ((attr.type.tsym == suppressWarningsType.tsym)
|| attr.type.tsym.getQualifiedName().contentEquals("android.annotation.SuppressLint")) { || attr.type.tsym.getQualifiedName().contentEquals("android.annotation.SuppressLint")) {
for (Pair<MethodSymbol, Attribute> value : attr.values) { for (Pair<MethodSymbol, Attribute> value : attr.values) {
if (value.fst.name.contentEquals("value")) if (value.fst.name.contentEquals("value")) {
if (value.snd if (value.snd
instanceof Attribute.Array) { // SuppressWarnings/SuppressLint take an array instanceof Attribute.Array) { // SuppressWarnings/SuppressLint take an array
for (Attribute suppress : ((Attribute.Array) value.snd).values) { for (Attribute suppress : ((Attribute.Array) value.snd).values) {
if (newSuppressions == null) { String suppressedWarning = (String) suppress.getValue();
newSuppressions = new HashSet<>(suppressionsOnCurrentPath); if (!toExtend.suppressWarningsStrings.contains(suppressedWarning)) {
anyModification = true;
if (newSuppressions == null) {
newSuppressions = new HashSet<>(toExtend.suppressWarningsStrings);
}
newSuppressions.add(suppressedWarning);
} }
// TODO(eaftan): check return value to see if this was a new warning?
newSuppressions.add((String) suppress.getValue());
} }
} else { } else {
throw new RuntimeException( throw new RuntimeException(
"Expected SuppressWarnings/SuppressLint annotation to take array type"); "Expected SuppressWarnings/SuppressLint annotation to take array type");
} }
}
} }
} }
} }


// Since this is invoked every time we descend into a new node, let's save some garbage
// by returning the same instance if there were no changes.
if (!anyModification) {
return toExtend;
}

if (newCustomSuppressions == null) {
newCustomSuppressions = toExtend.customSuppressions;
}
if (newSuppressions == null) {
newSuppressions = toExtend.suppressWarningsStrings;
}
return new SuppressionInfo(newSuppressions, newCustomSuppressions, newInGeneratedCode); return new SuppressionInfo(newSuppressions, newCustomSuppressions, newInGeneratedCode);
} }


/** /**
* Returns true if this checker should be suppressed on the current tree path. * Returns true if this checker should be suppressed on the current tree path.
* *
* @param suppressible Holds information about the suppressibilty of a checker * @param suppressible Holds information about the suppressibilty of a checker
* @param suppressionsOnCurrentPath The set of strings in all {@code @SuppressWarnings}
* annotations on the current path through the AST
* @param customSuppressionsOnCurrentPath The set of all custom suppression annotations on the
* current path through the AST
* @param severityLevel of the check to be suppressed * @param severityLevel of the check to be suppressed
* @param inGeneratedCode true if the current code is generated * @param suppressionInfo The current set of suppressions.
* @param disableWarningsInGeneratedCode true if warnings in generated code should be suppressed * @param disableWarningsInGeneratedCode true if warnings in generated code should be suppressed
*/ */
public static boolean isSuppressed( public static boolean isSuppressed(
Suppressible suppressible, Suppressible suppressible,
Set<String> suppressionsOnCurrentPath,
Set<Class<? extends Annotation>> customSuppressionsOnCurrentPath,
SeverityLevel severityLevel, SeverityLevel severityLevel,
boolean inGeneratedCode, SuppressionInfo suppressionInfo,
boolean disableWarningsInGeneratedCode) { boolean disableWarningsInGeneratedCode) {
if (inGeneratedCode && disableWarningsInGeneratedCode && severityLevel != SeverityLevel.ERROR) {
if (suppressionInfo.inGeneratedCode
&& disableWarningsInGeneratedCode
&& severityLevel != SeverityLevel.ERROR) {
return true; return true;
} }
if (suppressible.supportsSuppressWarnings() if (suppressible.supportsSuppressWarnings()
&& !Collections.disjoint(suppressible.allNames(), suppressionsOnCurrentPath)) { && !Collections.disjoint(
suppressible.allNames(), suppressionInfo.suppressWarningsStrings)) {
return true; return true;
} }
return !Collections.disjoint( return !Collections.disjoint(
suppressible.customSuppressionAnnotations(), customSuppressionsOnCurrentPath); suppressible.customSuppressionAnnotations(), suppressionInfo.customSuppressions);
} }


private static boolean isGenerated(Symbol sym, VisitorState state) { private static boolean isGenerated(Symbol sym, VisitorState state) {
Expand Down
57 changes: 15 additions & 42 deletions check_api/src/main/java/com/google/errorprone/scanner/Scanner.java
Expand Up @@ -19,6 +19,7 @@
import com.google.errorprone.BugPattern.SeverityLevel; import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.ErrorProneOptions; import com.google.errorprone.ErrorProneOptions;
import com.google.errorprone.SuppressionHelper; import com.google.errorprone.SuppressionHelper;
import com.google.errorprone.SuppressionHelper.SuppressionInfo;
import com.google.errorprone.VisitorState; import com.google.errorprone.VisitorState;
import com.google.errorprone.matchers.Description; import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Suppressible; import com.google.errorprone.matchers.Suppressible;
Expand All @@ -29,7 +30,6 @@
import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;


Expand All @@ -42,32 +42,27 @@
*/ */
public class Scanner extends TreePathScanner<Void, VisitorState> { public class Scanner extends TreePathScanner<Void, VisitorState> {


private Set<String> suppressions = new HashSet<>(); private SuppressionInfo currentSuppressions = SuppressionInfo.EMPTY;
private Set<Class<? extends Annotation>> customSuppressions = new HashSet<>();
private boolean inGeneratedCode = false;
// This must be lazily initialized, because the list of custom suppression annotations will // This must be lazily initialized, because the list of custom suppression annotations will
// not be available until after the subclass's constructor has run. // not be available until after the subclass's constructor has run.
private SuppressionHelper suppressionHelper; private SuppressionHelper suppressionHelper;


private void initSuppressionHelper() { private SuppressionHelper suppressionHelper() {
if (suppressionHelper == null) { if (suppressionHelper == null) {
suppressionHelper = new SuppressionHelper(getCustomSuppressionAnnotations()); suppressionHelper = new SuppressionHelper(getCustomSuppressionAnnotations());
} }
return suppressionHelper;
} }


/** Scan a tree from a position identified by a TreePath. */ /** Scan a tree from a position identified by a TreePath. */
@Override @Override
public Void scan(TreePath path, VisitorState state) { public Void scan(TreePath path, VisitorState state) {
SuppressionHelper.SuppressionInfo prevSuppressionInfo = SuppressionInfo prevSuppressionInfo = updateSuppressions(path.getLeaf(), state);
updateSuppressions(path.getLeaf(), state);

try { try {
return super.scan(path, state); return super.scan(path, state);
} finally { } finally {
// Restore old suppression state. // Restore old suppression state.
suppressions = prevSuppressionInfo.suppressWarningsStrings; currentSuppressions = prevSuppressionInfo;
customSuppressions = prevSuppressionInfo.customSuppressions;
inGeneratedCode = prevSuppressionInfo.inGeneratedCode;
} }
} }


Expand All @@ -78,46 +73,28 @@ public Void scan(Tree tree, VisitorState state) {
return null; return null;
} }


SuppressionHelper.SuppressionInfo prevSuppressionInfo = updateSuppressions(tree, state); SuppressionInfo prevSuppressionInfo = updateSuppressions(tree, state);
try { try {
return super.scan(tree, state); return super.scan(tree, state);
} finally { } finally {
// Restore old suppression state. // Restore old suppression state.
suppressions = prevSuppressionInfo.suppressWarningsStrings; currentSuppressions = prevSuppressionInfo;
customSuppressions = prevSuppressionInfo.customSuppressions;
inGeneratedCode = prevSuppressionInfo.inGeneratedCode;
} }
} }


/** /**
* Updates current suppression state with information for the given {@code tree}. Returns the * Updates current suppression state with information for the given {@code tree}. Returns the
* previous suppression state so that it can be restored when going up the tree. * previous suppression state so that it can be restored when going up the tree.
*/ */
private SuppressionHelper.SuppressionInfo updateSuppressions(Tree tree, VisitorState state) { private SuppressionInfo updateSuppressions(Tree tree, VisitorState state) {
SuppressionHelper.SuppressionInfo prevSuppressionInfo = SuppressionInfo prevSuppressionInfo = currentSuppressions;
new SuppressionHelper.SuppressionInfo(suppressions, customSuppressions, inGeneratedCode);

initSuppressionHelper();

Symbol sym = ASTHelpers.getDeclaredSymbol(tree); Symbol sym = ASTHelpers.getDeclaredSymbol(tree);
if (sym != null) { if (sym != null) {
SuppressionHelper.SuppressionInfo newSuppressions = currentSuppressions =
suppressionHelper.extendSuppressionSets( suppressionHelper()
sym, .extendSuppressionSets(
state.getSymtab().suppressWarningsType, sym, state.getSymtab().suppressWarningsType, currentSuppressions, state);
suppressions,
customSuppressions,
inGeneratedCode,
state);
if (newSuppressions.suppressWarningsStrings != null) {
suppressions = newSuppressions.suppressWarningsStrings;
}
if (newSuppressions.customSuppressions != null) {
customSuppressions = newSuppressions.customSuppressions;
}
inGeneratedCode = newSuppressions.inGeneratedCode;
} }

return prevSuppressionInfo; return prevSuppressionInfo;
} }


Expand All @@ -127,14 +104,10 @@ private SuppressionHelper.SuppressionInfo updateSuppressions(Tree tree, VisitorS
* @param suppressible holds information about the suppressibility of a checker * @param suppressible holds information about the suppressibility of a checker
*/ */
protected boolean isSuppressed(Suppressible suppressible, ErrorProneOptions errorProneOptions) { protected boolean isSuppressed(Suppressible suppressible, ErrorProneOptions errorProneOptions) {
initSuppressionHelper();

return SuppressionHelper.isSuppressed( return SuppressionHelper.isSuppressed(
suppressible, suppressible,
suppressions,
customSuppressions,
severityMap().get(suppressible.canonicalName()), severityMap().get(suppressible.canonicalName()),
inGeneratedCode, currentSuppressions,
errorProneOptions.disableWarningsInGeneratedCode()); errorProneOptions.disableWarningsInGeneratedCode());
} }


Expand Down

0 comments on commit b56cdf3

Please sign in to comment.