Skip to content

Commit

Permalink
Make BugCheckers immutable.
Browse files Browse the repository at this point in the history
Severity override is now accomplished by keeping a separate immutable map
between checkers and severity levels.

-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=84496729
  • Loading branch information
cushon committed Jan 28, 2015
1 parent 51f9679 commit 9ba17df
Show file tree
Hide file tree
Showing 17 changed files with 218 additions and 160 deletions.
16 changes: 13 additions & 3 deletions annotation/src/main/java/com/google/errorprone/BugPattern.java
Expand Up @@ -121,12 +121,22 @@ public enum Category {
SeverityLevel severity(); SeverityLevel severity();


public enum SeverityLevel { public enum SeverityLevel {
ERROR, ERROR(true),
WARNING, WARNING(true),
/** /**
* Should not be used for general code. * Should not be used for general code.
*/ */
NOT_A_PROBLEM NOT_A_PROBLEM(false);

private final boolean enabled;

SeverityLevel(boolean enabled) {
this.enabled = enabled;
}

public boolean enabled() {
return enabled;
}
} }


MaturityLevel maturity(); MaturityLevel maturity();
Expand Down
17 changes: 9 additions & 8 deletions core/src/main/java/com/google/errorprone/BugCheckerSupplier.java
Expand Up @@ -22,6 +22,7 @@
import com.google.errorprone.BugPattern.Suppressibility; import com.google.errorprone.BugPattern.Suppressibility;
import com.google.errorprone.bugpatterns.BugChecker; import com.google.errorprone.bugpatterns.BugChecker;


import java.util.Map;
import java.util.Objects; import java.util.Objects;


/** /**
Expand Down Expand Up @@ -59,21 +60,21 @@ public static BugCheckerSupplier fromInstance(BugChecker checker) {
public abstract String canonicalName(); public abstract String canonicalName();


/** /**
* The {@link SeverityLevel} of the {@link BugChecker}, e.g. {@link SeverityLevel#ERROR}. * The default {@link SeverityLevel} of the {@link BugChecker}, e.g. {@link SeverityLevel#ERROR}.
*/ */
public abstract SeverityLevel severity(); public abstract SeverityLevel defaultSeverity();

/** /**
* The {@link Suppressibility} of the {@link BugChecker}, e.g. * The {@link Suppressibility} of the {@link BugChecker}, e.g.
* {@link Suppressibility#UNSUPPRESSIBLE} * {@link Suppressibility#UNSUPPRESSIBLE}
*/ */
public abstract Suppressibility suppressibility(); public abstract Suppressibility suppressibility();


/** /**
* Returns a new {@link BugCheckerSupplier} in which the previous {@link SeverityLevel} * The per-compilation {@link SeverityLevel} of the {@link BugChecker}, e.g.
* has been overridden to the given value. * {@link SeverityLevel#ERROR}.
*/ */
public abstract BugCheckerSupplier overrideSeverity(SeverityLevel severity); public abstract SeverityLevel severity(Map<String, SeverityLevel> severityMap);


/** /**
* The {@link MaturityLevel} of the {@link BugChecker}, e.g. {@link MaturityLevel#EXPERIMENTAL}. * The {@link MaturityLevel} of the {@link BugChecker}, e.g. {@link MaturityLevel#EXPERIMENTAL}.
Expand All @@ -85,7 +86,7 @@ public boolean equals(Object obj) {
if (obj instanceof BugCheckerSupplier) { if (obj instanceof BugCheckerSupplier) {
BugCheckerSupplier that = (BugCheckerSupplier) obj; BugCheckerSupplier that = (BugCheckerSupplier) obj;
return this.canonicalName().equals(that.canonicalName()) return this.canonicalName().equals(that.canonicalName())
&& this.severity().equals(that.severity()) && this.defaultSeverity().equals(that.defaultSeverity())
&& this.maturity().equals(that.maturity()) && this.maturity().equals(that.maturity())
&& this.suppressibility().equals(that.suppressibility()); && this.suppressibility().equals(that.suppressibility());
} }
Expand All @@ -94,6 +95,6 @@ public boolean equals(Object obj) {


@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(canonicalName(), severity(), maturity(), suppressibility()); return Objects.hash(canonicalName(), defaultSeverity(), maturity(), suppressibility());
} }
} }
Expand Up @@ -179,6 +179,6 @@ private VisitorState createVisitorState(CompilationUnitTree compilation) {
log, log,
((JCCompilationUnit) compilation).endPositions, ((JCCompilationUnit) compilation).endPositions,
compilation.getSourceFile()); compilation.getSourceFile());
return new VisitorState(context, logReporter); return new VisitorState(context, logReporter, errorProneScanner.severityMap());
} }
} }
Expand Up @@ -22,6 +22,8 @@
import com.google.errorprone.BugPattern.Suppressibility; import com.google.errorprone.BugPattern.Suppressibility;
import com.google.errorprone.bugpatterns.BugChecker; import com.google.errorprone.bugpatterns.BugChecker;


import java.util.Map;

/** /**
* A {@link BugCheckerSupplier} that supplies {@link BugChecker}s given a class. * A {@link BugCheckerSupplier} that supplies {@link BugChecker}s given a class.
*/ */
Expand Down Expand Up @@ -56,11 +58,9 @@ private FromClassBugCheckerSupplier(Class<? extends BugChecker> checkerClass,
@Override @Override
public BugChecker get() { public BugChecker get() {
try { try {
BugChecker checker = checkerClass.newInstance(); return checkerClass.newInstance();
checker.setSeverity(severity);
return checker;
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new RuntimeException( throw new LinkageError(
"Could not reflectively create error prone checker " + checkerClass.getName(), e); "Could not reflectively create error prone checker " + checkerClass.getName(), e);
} }
} }
Expand All @@ -69,16 +69,15 @@ public BugChecker get() {
public String canonicalName() { public String canonicalName() {
return canonicalName; return canonicalName;
} }

@Override @Override
public SeverityLevel severity() { public SeverityLevel severity(Map<String, SeverityLevel> severityMap) {
return severity; return severityMap.get(canonicalName);
} }


@Override @Override
public BugCheckerSupplier overrideSeverity(SeverityLevel severity) { public SeverityLevel defaultSeverity() {
return new FromClassBugCheckerSupplier( return severity;
this.checkerClass, this.canonicalName, severity, this.maturity, this.suppressibility);
} }


@Override @Override
Expand Down
Expand Up @@ -22,35 +22,21 @@
import com.google.errorprone.BugPattern.Suppressibility; import com.google.errorprone.BugPattern.Suppressibility;
import com.google.errorprone.bugpatterns.BugChecker; import com.google.errorprone.bugpatterns.BugChecker;


import java.util.Map;

/** /**
* A {@link BugCheckerSupplier} that supplies the {@link BugChecker} instance that was passed * A {@link BugCheckerSupplier} that supplies the {@link BugChecker} instance that was passed
* in its constructor. * in its constructor.
*/ */
class FromInstanceBugCheckerSupplier extends BugCheckerSupplier { class FromInstanceBugCheckerSupplier extends BugCheckerSupplier {
private final BugChecker checker; private final BugChecker checker;
private final SeverityLevel severity;


FromInstanceBugCheckerSupplier(BugChecker checker) { FromInstanceBugCheckerSupplier(BugChecker checker) {
this(Preconditions.checkNotNull(checker), checker.severity()); this.checker = Preconditions.checkNotNull(checker);
}

private FromInstanceBugCheckerSupplier(BugChecker checker, SeverityLevel severity) {
this.checker = checker;
this.severity = severity;
} }


@Override @Override
public BugChecker get() { public BugChecker get() {
/* Note that we mutate the severity of the BugChecker instance here, which is not ideal.
* Ideally BugChecker instances would be immutable, and we would ask for a copy of this
* BugChecker with a different severity.
*
* Instead, we make BugCheckerSupplier and ScannerSupplier immutable. When we process severity
* overrides, we do not store a reference to the BugCheckerSupplier with the overridden
* severity; we keep only the original BugCheckerSupplier. This ensures that the default
* severity is used for subsequent compilations.
*/
checker.setSeverity(severity);
return checker; return checker;
} }


Expand All @@ -60,13 +46,13 @@ public String canonicalName() {
} }


@Override @Override
public SeverityLevel severity() { public SeverityLevel defaultSeverity() {
return severity; return checker.defaultSeverity();
} }

@Override @Override
public BugCheckerSupplier overrideSeverity(SeverityLevel severity) { public SeverityLevel severity(Map<String, SeverityLevel> severityMap) {
return new FromInstanceBugCheckerSupplier(this.checker, severity); return checker.severity(severityMap);
} }


@Override @Override
Expand Down
32 changes: 26 additions & 6 deletions core/src/main/java/com/google/errorprone/VisitorState.java
Expand Up @@ -16,6 +16,8 @@


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


import com.google.common.collect.ImmutableMap;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.matchers.Description; import com.google.errorprone.matchers.Description;


import com.sun.source.tree.Tree; import com.sun.source.tree.Tree;
Expand All @@ -38,6 +40,8 @@
import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Names;


import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.Map;


/** /**
* @author alexeagle@google.com (Alex Eagle) * @author alexeagle@google.com (Alex Eagle)
Expand All @@ -47,6 +51,7 @@ public class VisitorState {
private final DescriptionListener descriptionListener; private final DescriptionListener descriptionListener;
public final Context context; public final Context context;
private final TreePath path; private final TreePath path;
private final Map<String, SeverityLevel> severityMap;


// The default no-op implementation of DescriptionListener. We use this instead of null so callers // The default no-op implementation of DescriptionListener. We use this instead of null so callers
// of getDescriptionListener() don't have to do null-checking. // of getDescriptionListener() don't have to do null-checking.
Expand All @@ -55,22 +60,28 @@ public class VisitorState {
}; };


public VisitorState(Context context) { public VisitorState(Context context) {
this(context, null, NULL_LISTENER); this(context, null, NULL_LISTENER, ImmutableMap.<String, SeverityLevel>of());
} }


public VisitorState(Context context, DescriptionListener listener) { public VisitorState(Context context, DescriptionListener listener) {
this(context, null, listener); this(context, null, listener, Collections.<String, SeverityLevel>emptyMap());
}

public VisitorState(Context context, DescriptionListener listener,
Map<String, SeverityLevel> severityMap) {
this(context, null, listener, severityMap);
} }


private VisitorState(Context context, TreePath path, private VisitorState(Context context, TreePath path,
DescriptionListener descriptionListener) { DescriptionListener descriptionListener, Map<String, SeverityLevel> severityMap) {
this.context = context; this.context = context;
this.path = path; this.path = path;
this.descriptionListener = descriptionListener; this.descriptionListener = descriptionListener;
this.severityMap = severityMap;
} }


public VisitorState withPath(TreePath path) { public VisitorState withPath(TreePath path) {
return new VisitorState(context, path, descriptionListener); return new VisitorState(context, path, descriptionListener, severityMap);
} }


public TreePath getPath() { public TreePath getPath() {
Expand All @@ -89,8 +100,17 @@ public Symtab getSymtab() {
return Symtab.instance(context); return Symtab.instance(context);
} }


public DescriptionListener getDescriptionListener() { public void reportMatch(Description description) {
return descriptionListener; // TODO(user): creating Descriptions with the default severity and updating them here isn't
// ideal (we could forget to do the update), so consider removing severity from Description.
// Instead, there could be another method on the listener that took a description and a
// (separate) SeverityLevel. Adding the method to the interface would require updating the
// existing implementations, though. Wait for default methods?
SeverityLevel override = severityMap.get(description.checkName);
if (override != null) {
description = description.applySeverityOverride(override);
}
descriptionListener.onDescribed(description);
} }


public Name getName(String nameStr) { public Name getName(String nameStr) {
Expand Down
Expand Up @@ -16,6 +16,8 @@


package com.google.errorprone.bugpatterns; package com.google.errorprone.bugpatterns;


import static com.google.common.base.MoreObjects.firstNonNull;

import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.errorprone.BugPattern; import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.MaturityLevel; import com.google.errorprone.BugPattern.MaturityLevel;
Expand Down Expand Up @@ -84,6 +86,7 @@
import java.io.Serializable; import java.io.Serializable;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;


/** /**
Expand Down Expand Up @@ -117,10 +120,9 @@ public abstract class BugChecker implements Suppressible, Serializable {
private final String message; private final String message;


/** /**
* The type of diagnostic (error or warning) to emit when this check triggers. Initialized to * The default type of diagnostic (error or warning) to emit when this check triggers.
* the {@code severity} attribute from its {@code BugPattern}, but may be overridden.
*/ */
private SeverityLevel severity; private final SeverityLevel defaultSeverity;


/** /**
* The maturity of this checker. Used to decide whether to enable this check. Corresponds to * The maturity of this checker. Used to decide whether to enable this check. Corresponds to
Expand Down Expand Up @@ -163,7 +165,7 @@ public BugChecker() {
.build(); .build();
message = pattern.summary(); message = pattern.summary();
maturity = pattern.maturity(); maturity = pattern.maturity();
severity = pattern.severity(); defaultSeverity = pattern.severity();
linkUrl = createLinkUrl(pattern); linkUrl = createLinkUrl(pattern);
suppressibility = pattern.suppressibility(); suppressibility = pattern.suppressibility();
if (suppressibility == Suppressibility.CUSTOM_ANNOTATION) { if (suppressibility == Suppressibility.CUSTOM_ANNOTATION) {
Expand Down Expand Up @@ -233,12 +235,12 @@ public MaturityLevel maturity() {
return maturity; return maturity;
} }


public SeverityLevel severity() { public SeverityLevel defaultSeverity() {
return severity; return defaultSeverity;
} }

public void setSeverity(SeverityLevel severity) { public SeverityLevel severity(Map<String, SeverityLevel> severities) {
this.severity = severity; return firstNonNull(severities.get(canonicalName), defaultSeverity);
} }


public String linkUrl() { public String linkUrl() {
Expand Down
Expand Up @@ -195,12 +195,7 @@ private static Map<String, Type> indexTypeByName(List<? extends VariableTree> pa


private void reportMatch( private void reportMatch(
Tree diagnosticPosition, VisitorState state, Tree toReplace, String replaceWith) { Tree diagnosticPosition, VisitorState state, Tree toReplace, String replaceWith) {
report(describeMatch(diagnosticPosition, replace(toReplace, replaceWith)), state); state.reportMatch(describeMatch(diagnosticPosition, replace(toReplace, replaceWith)));
}

// Cloned from Scanner. This also appears in ThreadSafe. See the TODO there.
private void report(Description description, VisitorState state) {
state.getDescriptionListener().onDescribed(description);
} }


private static boolean referencesIdentifierWithName( private static boolean referencesIdentifierWithName(
Expand Down
Expand Up @@ -134,6 +134,6 @@ private void report(Description description, VisitorState state) {
if (description == null || description == Description.NO_MATCH) { if (description == null || description == Description.NO_MATCH) {
return; return;
} }
state.getDescriptionListener().onDescribed(description); state.reportMatch(description);
} }
} }

0 comments on commit 9ba17df

Please sign in to comment.