in java/com/google/javascript/jscomp/JsChecker.java [140:313]
private boolean run() throws IOException {
final JsCheckerState state = new JsCheckerState(label, legacy, testonly, roots, mysterySources);
final Set<String> actuallySuppressed = new HashSet<>();
// read provided files created by this program on deps
for (String dep : deps) {
state.provided.addAll(
JsCheckerHelper.loadClosureJsLibraryInfo(Paths.get(dep))
.getNamespaceList());
}
Map<String, String> labels = new HashMap<>();
labels.put("", label);
Set<String> modules = new LinkedHashSet<>();
for (String source : sources) {
for (String module : convertPathToModuleName(source, state.roots).asSet()) {
modules.add(module);
labels.put(module, label);
state.provides.add(module);
}
}
for (String source : mysterySources) {
for (String module : convertPathToModuleName(source, state.roots).asSet()) {
checkArgument(!module.startsWith("blaze-out/"),
"oh no: %s", state.roots);
modules.add(module);
state.provided.add(module);
}
}
// configure compiler
Compiler compiler = new Compiler();
CompilerOptions options = new CompilerOptions();
options.setLanguage(LanguageMode.STABLE);
options.setStrictModeInput(true);
options.setIncrementalChecks(IncrementalCheckMode.GENERATE_IJS);
options.setCodingConvention(convention.convention);
options.setSkipTranspilationAndCrash(true);
options.setContinueAfterErrors(true);
options.setPrettyPrint(true);
options.setPreserveTypeAnnotations(true);
options.setPreserveDetailedSourceInfo(true);
options.setEmitUseStrict(false);
options.setParseJsDocDocumentation(Config.JsDocParsing.INCLUDE_DESCRIPTIONS_NO_WHITESPACE);
JsCheckerErrorFormatter errorFormatter =
new JsCheckerErrorFormatter(compiler, state.roots, labels);
errorFormatter.setColorize(true);
JsCheckerErrorManager errorManager = new JsCheckerErrorManager(errorFormatter);
compiler.setErrorManager(errorManager);
// configure which error messages appear
if (!legacy) {
for (String error
: Iterables.concat(
Diagnostics.JSCHECKER_ONLY_ERRORS,
Diagnostics.JSCHECKER_EXTRA_ERRORS)) {
options.setWarningLevel(Diagnostics.GROUPS.forName(error), CheckLevel.ERROR);
}
}
final Set<DiagnosticType> suppressions = Sets.newHashSetWithExpectedSize(256);
for (String code : suppress) {
ImmutableSet<DiagnosticType> types = Diagnostics.getDiagnosticTypesForSuppressCode(code);
if (types.isEmpty()) {
System.err.println("ERROR: Bad --suppress value: " + code);
return false;
}
suppressions.addAll(types);
}
options.addWarningsGuard(
new WarningsGuard() {
@Override
public CheckLevel level(JSError error) {
// TODO(jart): Figure out how to support this.
if (error.getType().key
.equals("JSC_CONSTANT_WITHOUT_EXPLICIT_TYPE")) {
return CheckLevel.OFF;
}
// Closure Rules will always ignore these checks no matter what.
if (Diagnostics.IGNORE_ALWAYS.contains(error.getType())) {
return CheckLevel.OFF;
}
// Disable warnings specific to conventions other than the one we're using.
if (!convention.diagnostics.contains(error.getType())) {
for (JsCheckerConvention conv : JsCheckerConvention.values()) {
if (!conv.equals(convention)) {
if (conv.diagnostics.contains(error.getType())) {
suppressions.add(error.getType());
return CheckLevel.OFF;
}
}
}
}
// Disable warnings we've suppressed.
Collection<String> groupNames = Diagnostics.DIAGNOSTIC_GROUPS.get(error.getType());
if (suppressions.contains(error.getType())) {
actuallySuppressed.add(error.getType().key);
actuallySuppressed.addAll(groupNames);
return CheckLevel.OFF;
}
// Ignore linter warnings on generated sources.
if (groupNames.contains("lintChecks")
&& JsCheckerHelper.isGeneratedPath(error.getSourceName())) {
return CheckLevel.OFF;
}
return null;
}
});
// Run the compiler.
compiler.setPassConfig(new JsCheckerPassConfig(state, options));
compiler.disableThreads();
compiler.compile(
ImmutableList.<SourceFile>of(),
getSourceFiles(Iterables.concat(sources, mysterySources)),
options);
// In order for suppress to be maintainable, we need to make sure the suppress codes relating to
// linting were actually suppressed. However we can only offer this safety on the checks over
// which JsChecker has sole dominion. Other suppress codes won't actually be suppressed until
// they've been propagated up to the closure_js_binary rule.
if (!suppress.contains("superfluousSuppress")) {
Set<String> useless =
Sets.intersection(
Sets.difference(ImmutableSet.copyOf(suppress), actuallySuppressed),
Diagnostics.JSCHECKER_ONLY_SUPPRESS_CODES);
if (!useless.isEmpty()) {
errorManager.report(CheckLevel.ERROR,
JSError.make(Diagnostics.SUPERFLUOUS_SUPPRESS, label, Joiner.on(", ").join(useless)));
}
}
// TODO: Make compiler.compile() package private so we don't have to do this.
errorManager.stderr.clear();
errorManager.generateReport();
// write errors
if (!expectFailure) {
for (String line : errorManager.stderr) {
System.err.println(line);
}
}
if (!outputErrors.isEmpty()) {
Files.write(Paths.get(outputErrors), errorManager.stderr, UTF_8);
}
// write .i.js type summary for this library
if (!outputIjsFile.isEmpty()) {
Files.write(Paths.get(outputIjsFile), compiler.toSource().getBytes(UTF_8));
}
// write file full of information about these sauces
if (!output.isEmpty()) {
ClosureJsLibrary.Builder info =
ClosureJsLibrary.newBuilder()
.setLabel(label)
.setLegacy(legacy)
.addAllNamespace(state.provides)
.addAllModule(modules);
if (!legacy) {
for (DiagnosticType suppression : suppressions) {
if (!Diagnostics.JSCHECKER_ONLY_SUPPRESS_CODES.contains(suppression.key)) {
info.addSuppress(suppression.key);
}
}
}
Files.write(Paths.get(output), info.build().toString().getBytes(UTF_8));
}
return errorManager.getErrorCount() == 0;
}