Skip to content

Commit

Permalink
Removed ForwardingCompiler. Added a hook to log whitelisted conforman…
Browse files Browse the repository at this point in the history
…ce violations.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=213522265
  • Loading branch information
bangert authored and tjgq committed Sep 19, 2018
1 parent a8b74cc commit 0bbdd5e
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 617 deletions.
12 changes: 7 additions & 5 deletions src/com/google/javascript/jscomp/CheckConformance.java
Expand Up @@ -30,11 +30,13 @@
import java.util.Set; import java.util.Set;


/** /**
* Provides a framework for checking code against a set of user configured * Provides a framework for checking code against a set of user configured conformance rules. The
* conformance rules. The rules are specified by the ConformanceConfig * rules are specified by the ConformanceConfig proto, which allows for both standard checks
* proto, which allows for both standard checks (forbidden properties, * (forbidden properties, variables, or dependencies) and allow for more complex checks using custom
* variables, or dependencies) and allow for more complex checks using * rules than specify
* custom rules than specify *
* <p>Conformance violations are both reported as compiler errors, and are also reported separately
* to the {cI gue@link ErrorManager}
* *
*/ */
@GwtIncompatible("com.google.protobuf") @GwtIncompatible("com.google.protobuf")
Expand Down
26 changes: 17 additions & 9 deletions src/com/google/javascript/jscomp/ConformanceRules.java
Expand Up @@ -128,6 +128,7 @@ public abstract static class AbstractRule implements Rule {
@Nullable final Pattern onlyApplyToRegexp; @Nullable final Pattern onlyApplyToRegexp;
final boolean reportLooseTypeViolations; final boolean reportLooseTypeViolations;
final TypeMatchingStrategy typeMatchingStrategy; final TypeMatchingStrategy typeMatchingStrategy;
final Requirement requirement;


public AbstractRule(AbstractCompiler compiler, Requirement requirement) public AbstractRule(AbstractCompiler compiler, Requirement requirement)
throws InvalidRequirementSpec { throws InvalidRequirementSpec {
Expand All @@ -149,6 +150,7 @@ public AbstractRule(AbstractCompiler compiler, Requirement requirement)
requirement.getOnlyApplyToRegexpList()); requirement.getOnlyApplyToRegexpList());
reportLooseTypeViolations = requirement.getReportLooseTypeViolations(); reportLooseTypeViolations = requirement.getReportLooseTypeViolations();
typeMatchingStrategy = getTypeMatchingStrategy(requirement); typeMatchingStrategy = getTypeMatchingStrategy(requirement);
this.requirement = requirement;
} }


private static TypeMatchingStrategy getTypeMatchingStrategy(Requirement requirement) { private static TypeMatchingStrategy getTypeMatchingStrategy(Requirement requirement) {
Expand Down Expand Up @@ -200,10 +202,10 @@ protected abstract ConformanceResult checkConformance(
NodeTraversal t, Node n); NodeTraversal t, Node n);


/** /**
* @return Whether the specified Node should be checked for conformance, * @return Whether the specified Node should be checked for conformance, according to this
* according to this rule's whitelist configuration. * rule's whitelist configuration.
*/ */
protected final boolean shouldCheckConformance(Node n) { protected final boolean isWhitelistedByRequirement(Node n) {
String srcfile = NodeUtil.getSourceName(n); String srcfile = NodeUtil.getSourceName(n);
if (srcfile == null) { if (srcfile == null) {
return true; return true;
Expand All @@ -229,19 +231,18 @@ private static boolean pathIsInListOrRegexp(
@Override @Override
public final void check(NodeTraversal t, Node n) { public final void check(NodeTraversal t, Node n) {
ConformanceResult result = checkConformance(t, n); ConformanceResult result = checkConformance(t, n);
if (result.level != ConformanceLevel.CONFORMANCE if (result.level != ConformanceLevel.CONFORMANCE) {
&& shouldCheckConformance(n)) { report(n, result);
report(t, n, result);
} }
} }


/** /**
* Report a conformance warning for the given node. * Report a conformance warning for the given node.
*
* @param n The node representing the violating code. * @param n The node representing the violating code.
* @param result The result representing the confidence of the violation. * @param result The result representing the confidence of the violation.
*/ */
protected void report( protected void report(Node n, ConformanceResult result) {
NodeTraversal t, Node n, ConformanceResult result) {
DiagnosticType msg; DiagnosticType msg;
if (severity == Severity.ERROR) { if (severity == Severity.ERROR) {
// Always report findings that are errors, even if the types are too loose to be certain. // Always report findings that are errors, even if the types are too loose to be certain.
Expand All @@ -258,7 +259,14 @@ protected void report(
String separator = (result.note.isEmpty()) String separator = (result.note.isEmpty())
? "" ? ""
: "\n"; : "\n";
t.report(n, msg, message, separator, result.note); JSError err = JSError.make(n, msg, message, separator, result.note);

// Note that shouldReportConformanceViolation has to be called first; this allows
// logging violations that would otherwise be whitelisted.
if (compiler.getErrorManager().shouldReportConformanceViolation(requirement, err)
&& isWhitelistedByRequirement(n)) {
compiler.report(err);
}
} }
} }


Expand Down
58 changes: 22 additions & 36 deletions src/com/google/javascript/jscomp/ConformanceWhitelister.java
Expand Up @@ -27,65 +27,51 @@ public class ConformanceWhitelister {
private ConformanceWhitelister() {} private ConformanceWhitelister() {}


public static ImmutableSet<String> getViolatingPaths( public static ImmutableSet<String> getViolatingPaths(
AbstractCompiler compiler, Node externs, Node ast, Requirement requirement) { Compiler compiler, Node externs, Node ast, Requirement requirement) {
return getConformanceErrors(compiler, externs, ast, requirement) return getConformanceErrors(compiler, externs, ast, requirement)
.stream() .stream()
.map(e -> e.sourceName) .map(e -> e.sourceName)
.collect(ImmutableSet.toImmutableSet()); .collect(ImmutableSet.toImmutableSet());
} }


public static ImmutableSet<Node> getViolatingNodes( public static ImmutableSet<Node> getViolatingNodes(
AbstractCompiler compiler, Node externs, Node ast, Requirement requirement) { Compiler compiler, Node externs, Node ast, Requirement requirement) {
return getConformanceErrors(compiler, externs, ast, requirement) return getConformanceErrors(compiler, externs, ast, requirement)
.stream() .stream()
.map(e -> e.node) .map(e -> e.node)
.collect(ImmutableSet.toImmutableSet()); .collect(ImmutableSet.toImmutableSet());
} }


public static ImmutableList<JSError> getConformanceErrors( public static ImmutableList<JSError> getConformanceErrors(
AbstractCompiler compiler, Node externs, Node ast, Requirement requirement) { Compiler compiler, Node externs, Node ast, Requirement requirement) {
ConformanceViolationRecordingCompiler recordingCompiler =
new ConformanceViolationRecordingCompiler(compiler);
// Remove existing prefix whitelist entries, but keep regexps (which we don't re-add either).
// TODO(bangert): Check that each regex matches one entry?
Requirement cleanedRequirement = Requirement cleanedRequirement =
requirement requirement
.toBuilder() .toBuilder()
.clearWhitelist() .clearWhitelist()
.clearWhitelistRegexp()
.setSeverity(Severity.ERROR) .setSeverity(Severity.ERROR)
.build(); // So we only have one type of error. .build(); // So we only have one type of error.

ConformanceConfig cleanedConfig = ConformanceConfig cleanedConfig =
ConformanceConfig.newBuilder().addRequirement(cleanedRequirement).build(); ConformanceConfig.newBuilder().addRequirement(cleanedRequirement).build();
CheckConformance check =
new CheckConformance(recordingCompiler, ImmutableList.of(cleanedConfig));
check.process(externs, ast);

return recordingCompiler.getConformanceErrors();
}


private static class ConformanceViolationRecordingCompiler extends ForwardingCompiler { ErrorManager oldErrorManager = compiler.getErrorManager();
private final ImmutableList.Builder<JSError> conformanceErrors; final ImmutableList.Builder<JSError> errors = ImmutableList.builder();

try {
private ConformanceViolationRecordingCompiler(AbstractCompiler abstractCompiler) { // TODO(bangert): handle invalid conformance requirements
super(abstractCompiler); compiler.setErrorManager(
conformanceErrors = ImmutableList.builder(); new ThreadSafeDelegatingErrorManager(oldErrorManager) {
@Override
public synchronized boolean shouldReportConformanceViolation(
Requirement requirement, JSError diagnostic) {
errors.add(diagnostic);
return false;
}
});
CheckConformance check = new CheckConformance(compiler, ImmutableList.of(cleanedConfig));
check.process(externs, ast);
} finally {
compiler.setErrorManager(oldErrorManager);
} }

return errors.build();
ImmutableList<JSError> getConformanceErrors() {
return conformanceErrors.build();
}

@Override
public void report(JSError error) {
if (error.getType().equals(CheckConformance.CONFORMANCE_ERROR)) {
conformanceErrors.add(error);
} else if (error.getType().equals(CheckConformance.INVALID_REQUIREMENT_SPEC)) {
throw new IllegalArgumentException("Invalid conformance requirement" + error.description);
} else {
super.report(error);
}
}

} }
} }
8 changes: 8 additions & 0 deletions src/com/google/javascript/jscomp/ErrorManager.java
Expand Up @@ -83,4 +83,12 @@ default boolean hasHaltingErrors() {
} }
return false; return false;
} }

/**
* Return true if the conformance violation should be reported. This is called before checking the
* whitelist inside {@code requirement}, so it can be used to log whitelisted violations.
*/
default boolean shouldReportConformanceViolation(Requirement requirement, JSError diagnostic) {
return true;
}
} }

0 comments on commit 0bbdd5e

Please sign in to comment.