Skip to content

Commit

Permalink
Add notifyWhitelistedConformanceViolation() method to ErrorManager an…
Browse files Browse the repository at this point in the history
…d refactor conformance violation whitelist checking.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=217924889
  • Loading branch information
adob authored and blickly committed Oct 19, 2018
1 parent 707f45e commit f9c61bb
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 103 deletions.
4 changes: 4 additions & 0 deletions src/com/google/javascript/jscomp/CheckConformance.java
Expand Up @@ -239,6 +239,10 @@ public static class InvalidRequirementSpec extends Exception {
InvalidRequirementSpec(String message) { InvalidRequirementSpec(String message) {
super(message); super(message);
} }

InvalidRequirementSpec(String message, Throwable cause) {
super(message, cause);
}
} }


/** /**
Expand Down
193 changes: 96 additions & 97 deletions src/com/google/javascript/jscomp/ConformanceRules.java
Expand Up @@ -22,6 +22,7 @@
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Ascii; import com.google.common.base.Ascii;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
Expand All @@ -35,7 +36,6 @@
import com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec; import com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec;
import com.google.javascript.jscomp.Requirement.Severity; import com.google.javascript.jscomp.Requirement.Severity;
import com.google.javascript.jscomp.Requirement.Type; import com.google.javascript.jscomp.Requirement.Type;
import com.google.javascript.jscomp.Requirement.WhitelistEntry;
import com.google.javascript.jscomp.parsing.JsDocInfoParser; import com.google.javascript.jscomp.parsing.JsDocInfoParser;
import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.JSDocInfo.Visibility; import com.google.javascript.rhino.JSDocInfo.Visibility;
Expand Down Expand Up @@ -115,6 +115,64 @@ public static enum ConformanceLevel {
VIOLATION, VIOLATION,
} }


private static class Whitelist {
@Nullable final ImmutableList<String> prefixes;
@Nullable final Pattern regexp;
@Nullable final Requirement.WhitelistEntry whitelistEntry;

Whitelist(List<String> prefixes, List<String> regexps) throws InvalidRequirementSpec {
this.prefixes = ImmutableList.<String>copyOf(prefixes);
this.regexp = buildPattern(regexps);
this.whitelistEntry = null;
}

Whitelist(Requirement.WhitelistEntry whitelistEntry) throws InvalidRequirementSpec {
this.prefixes = ImmutableList.copyOf(whitelistEntry.getPrefixList());
this.regexp = buildPattern(whitelistEntry.getRegexpList());
this.whitelistEntry = whitelistEntry;
}

/**
* Returns true if the given path matches one of the prefixes or regexps, and false otherwise
*/
boolean matches(String path) {
if (prefixes != null) {
for (String prefix : prefixes) {
if (!path.isEmpty() && path.startsWith(prefix)) {
return true;
}
}
}

return regexp != null && regexp.matcher(path).find();
}

@Nullable
private static Pattern buildPattern(List<String> reqPatterns) throws InvalidRequirementSpec {
if (reqPatterns == null || reqPatterns.isEmpty()) {
return null;
}

// validate the patterns
for (String reqPattern : reqPatterns) {
try {
Pattern.compile(reqPattern);
} catch (PatternSyntaxException e) {
throw new InvalidRequirementSpec("invalid regex pattern", e);
}
}

Pattern pattern = null;
try {
String jointRegExp = "(" + Joiner.on("|").join(reqPatterns) + ")";
pattern = Pattern.compile(jointRegExp);
} catch (PatternSyntaxException e) {
throw new RuntimeException("bad joined regexp", e);
}
return pattern;
}
}

/** /**
* A conformance rule implementation to support things common to all rules such as whitelisting * A conformance rule implementation to support things common to all rules such as whitelisting
* and reporting. * and reporting.
Expand All @@ -123,10 +181,8 @@ public abstract static class AbstractRule implements Rule {
final AbstractCompiler compiler; final AbstractCompiler compiler;
final String message; final String message;
final Severity severity; final Severity severity;
final ImmutableList<String> whitelist; final ImmutableList<Whitelist> whitelists;
final ImmutableList<String> onlyApplyTo; @Nullable final Whitelist onlyApplyTo;
@Nullable final ImmutableList<Pattern> whitelistRegexps;
@Nullable final Pattern onlyApplyToRegexp;
final boolean reportLooseTypeViolations; final boolean reportLooseTypeViolations;
final TypeMatchingStrategy typeMatchingStrategy; final TypeMatchingStrategy typeMatchingStrategy;
final Requirement requirement; final Requirement requirement;
Expand All @@ -144,26 +200,25 @@ public AbstractRule(AbstractCompiler compiler, Requirement requirement)
severity = requirement.getSeverity(); severity = requirement.getSeverity();
} }


ImmutableList.Builder<String> whitelistBuilder = new ImmutableList.Builder<>(); // build whitelists
ImmutableList.Builder<Pattern> whitelistRegexpsBuilder = new ImmutableList.Builder<>(); ImmutableList.Builder<Whitelist> whitelistsBuilder = new ImmutableList.Builder<>();
for (WhitelistEntry entry : requirement.getWhitelistEntryList()) { for (Requirement.WhitelistEntry entry : requirement.getWhitelistEntryList()) {
whitelistBuilder.addAll(entry.getPrefixList()); whitelistsBuilder.add(new Whitelist(entry));
if (entry.getRegexpCount() > 0) {
whitelistRegexpsBuilder.add(buildPattern(entry.getRegexpList()));
}
} }


whitelistBuilder.addAll(requirement.getWhitelistList()); if (requirement.getWhitelistCount() > 0 || requirement.getWhitelistRegexpCount() > 0) {
if (requirement.getWhitelistRegexpCount() > 0) { Whitelist whitelist =
whitelistRegexpsBuilder.add(buildPattern(requirement.getWhitelistRegexpList())); new Whitelist(requirement.getWhitelistList(), requirement.getWhitelistRegexpList());
whitelistsBuilder.add(whitelist);
} }
whitelists = whitelistsBuilder.build();


whitelist = whitelistBuilder.build(); if (requirement.getOnlyApplyToCount() > 0 || requirement.getOnlyApplyToRegexpCount() > 0) {
whitelistRegexps = whitelistRegexpsBuilder.build(); onlyApplyTo =

new Whitelist(requirement.getOnlyApplyToList(), requirement.getOnlyApplyToRegexpList());
onlyApplyTo = ImmutableList.copyOf(requirement.getOnlyApplyToList()); } else {
onlyApplyToRegexp = buildPattern( onlyApplyTo = null;
requirement.getOnlyApplyToRegexpList()); }
reportLooseTypeViolations = requirement.getReportLooseTypeViolations(); reportLooseTypeViolations = requirement.getReportLooseTypeViolations();
typeMatchingStrategy = getTypeMatchingStrategy(requirement); typeMatchingStrategy = getTypeMatchingStrategy(requirement);
this.requirement = requirement; this.requirement = requirement;
Expand All @@ -184,87 +239,22 @@ private static TypeMatchingStrategy getTypeMatchingStrategy(Requirement requirem
} }
} }


@Nullable
private static Pattern buildPattern(List<String> reqPatterns)
throws InvalidRequirementSpec {
if (reqPatterns == null || reqPatterns.isEmpty()) {
return null;
}

// validate the patterns
for (String reqPattern : reqPatterns) {
try {
Pattern.compile(reqPattern);
} catch (PatternSyntaxException e) {
throw new InvalidRequirementSpec("invalid regex pattern");
}
}

Pattern pattern = null;
try {
String jointRegExp = "(" + Joiner.on("|").join(reqPatterns) + ")";
pattern = Pattern.compile(jointRegExp);
} catch (PatternSyntaxException e) {
throw new RuntimeException("bad joined regexp", e);
}
return pattern;
}

/** /**
* @return Whether the code represented by the Node conforms to the * @return Whether the code represented by the Node conforms to the
* rule. * rule.
*/ */
protected abstract ConformanceResult checkConformance( protected abstract ConformanceResult checkConformance(
NodeTraversal t, Node n); NodeTraversal t, Node n);


/** /** Returns the first Whitelist entry that matches the given path, and null otherwise. */
* @return Whether the specified Node should be checked for conformance, according to this @Nullable
* rule's whitelist configuration. private Whitelist findWhitelistForPath(String path) {
*/ for (Whitelist whitelist : whitelists) {
protected final boolean isWhitelistedByRequirement(Node n) { if (whitelist.matches(path)) {
String srcfile = NodeUtil.getSourceName(n); return whitelist;
if (srcfile == null) {
return true;
} else if (!onlyApplyTo.isEmpty() || onlyApplyToRegexp != null) {
return pathIsInListOrRegexp(srcfile, onlyApplyTo, onlyApplyToRegexp)
&& !pathIsInListOrRegexps(srcfile, whitelist, whitelistRegexps);
} else {
return !pathIsInListOrRegexps(srcfile, whitelist, whitelistRegexps);
}
}

private static boolean pathIsInListOrRegexps(
String srcfile, ImmutableList<String> list, @Nullable ImmutableList<Pattern> regexps) {
if (pathIsInList(srcfile, list)) {
return true;
}
if (regexps == null) {
return false;
}
for (Pattern regexp : regexps) {
if (regexp.matcher(srcfile).find()) {
return true;
}
}
return false;
}

private static boolean pathIsInListOrRegexp(
String srcfile, ImmutableList<String> list, @Nullable Pattern regexp) {
if (pathIsInList(srcfile, list)) {
return true;
}
return regexp != null && regexp.matcher(srcfile).find();
}

private static boolean pathIsInList(String srcfile, ImmutableList<String> list) {
for (int i = 0; i < list.size(); i++) {
String entry = list.get(i);
if (!entry.isEmpty() && srcfile.startsWith(entry)) {
return true;
} }
} }
return false; return null;
} }


@Override @Override
Expand Down Expand Up @@ -300,10 +290,19 @@ protected void report(Node n, ConformanceResult result) {
: "\n"; : "\n";
JSError err = JSError.make(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 String path = NodeUtil.getSourceName(n);
// logging violations that would otherwise be whitelisted. Whitelist whitelist = path != null ? findWhitelistForPath(path) : null;
if (compiler.getErrorManager().shouldReportConformanceViolation(requirement, err) boolean shouldReport =
&& isWhitelistedByRequirement(n)) { compiler
.getErrorManager()
.shouldReportConformanceViolation(
requirement,
whitelist != null
? Optional.fromNullable(whitelist.whitelistEntry)
: Optional.absent(),
err);

if (shouldReport && whitelist == null && (onlyApplyTo == null || onlyApplyTo.matches(path))) {
compiler.report(err); compiler.report(err);
} }
} }
Expand Down
5 changes: 4 additions & 1 deletion src/com/google/javascript/jscomp/ConformanceWhitelister.java
Expand Up @@ -16,6 +16,7 @@
package com.google.javascript.jscomp; package com.google.javascript.jscomp;


import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.javascript.jscomp.Requirement.Severity; import com.google.javascript.jscomp.Requirement.Severity;
Expand Down Expand Up @@ -63,7 +64,9 @@ public static ImmutableList<JSError> getConformanceErrors(
new ThreadSafeDelegatingErrorManager(oldErrorManager) { new ThreadSafeDelegatingErrorManager(oldErrorManager) {
@Override @Override
public synchronized boolean shouldReportConformanceViolation( public synchronized boolean shouldReportConformanceViolation(
Requirement requirement, JSError diagnostic) { Requirement requirement,
Optional<Requirement.WhitelistEntry> whitelistEntry,
JSError diagnostic) {
errors.add(diagnostic); errors.add(diagnostic);
return false; return false;
} }
Expand Down
10 changes: 7 additions & 3 deletions src/com/google/javascript/jscomp/ErrorManager.java
Expand Up @@ -16,6 +16,9 @@


package com.google.javascript.jscomp; package com.google.javascript.jscomp;


import com.google.common.base.Optional;
import com.google.javascript.jscomp.Requirement.WhitelistEntry;

/** /**
* The error manager is in charge of storing, organizing and displaying * The error manager is in charge of storing, organizing and displaying
* errors and warnings generated by the compiler. * errors and warnings generated by the compiler.
Expand Down Expand Up @@ -85,10 +88,11 @@ default boolean hasHaltingErrors() {
} }


/** /**
* Return true if the conformance violation should be reported. This is called before checking the * Return true if the conformance violation should be reported. This is called even if the
* whitelist inside {@code requirement}, so it can be used to log whitelisted violations. * violation is whitelisted.
*/ */
default boolean shouldReportConformanceViolation(Requirement requirement, JSError diagnostic) { default boolean shouldReportConformanceViolation(
Requirement requirement, Optional<WhitelistEntry> whitelistEntry, JSError diagnostic) {
return true; return true;
} }
} }
Expand Up @@ -16,6 +16,8 @@


package com.google.javascript.jscomp; package com.google.javascript.jscomp;


import com.google.common.base.Optional;

/** /**
* A simple delegating {@link ErrorManager} that provides a thread-safe wrapper * A simple delegating {@link ErrorManager} that provides a thread-safe wrapper
* for the one being delegated. * for the one being delegated.
Expand Down Expand Up @@ -74,7 +76,9 @@ public synchronized double getTypedPercent() {


@Override @Override
public synchronized boolean shouldReportConformanceViolation( public synchronized boolean shouldReportConformanceViolation(
Requirement requirement, JSError diagnostic) { Requirement requirement,
return delegated.shouldReportConformanceViolation(requirement, diagnostic); Optional<Requirement.WhitelistEntry> whitelistEntry,
JSError diagnostic) {
return delegated.shouldReportConformanceViolation(requirement, whitelistEntry, diagnostic);
} }
} }
Expand Up @@ -24,4 +24,15 @@ public enum Severity {
WARNING, WARNING,
ERROR ERROR
} }

/** No-op replacement for {@code Requirement.WhitelistEntry} */
public static class WhitelistEntry {
/** No-op replacement for {@code Requirement.WhitelistEntry.Reason} */
public enum Reason {
UNSPECIFIED,
LEGACY,
OUT_OF_SCOPE,
MANUALLY_REVIEWED
}
}
} }

0 comments on commit f9c61bb

Please sign in to comment.