Repository: google/closure-compiler
Pull request #592 (2014-08-23 08:39:58)

Add --checks-only
This is at least a partial resolution for https://github.com/google/closure-compiler/issues/478.

Specifying the `--no-output` (EDIT: this has been changed to `--checks-only`) command line parameter skips output and runs only checks.
It works in with each of the compilation levels.

---

Alternate approaches I considered:

* **Add a new compilation level** However, unlike GCC's `-O`, Closure's compilation level determines a lot more than just the optimizations. For example, `checkTypes` is enabled by default `ADVANCED_OPTIMIZATIONS`, but not `SIMPLE_OPTIMIZATIONS`, and never for `WHITESPACE_ONLY`.

* **Use `skipNormalOutputs` in `CommandLineConfig`**. This  sounds similar, except it also skips checks. (I believe it is meant for outputting only dependency info.)

---

Performance benefits:

* 6000-line file with `WHITESPACE_ONLY` -- from 2.6 to 1.7 seconds
* 6000-line file with `SIMPLE_OPTIMIZATIONS` -- from 6.1 seconds to 3.8 seconds
* Large project with `ADVANCED_OPTIMIZATIONS` -- from 135 seconds to 70 seconds

concavelenz: @MatrixFrog Do you have any outstanding concerns?  I think we should merge this.
MatrixFrog: There is the general problem that we have a few different "only" flags and it can get confusing. But this is a good one to have so I'm not too worried about it.

Patch:
diff --git a/src/com/google/javascript/jscomp/AbstractCommandLineRunner.java b/src/com/google/javascript/jscomp/AbstractCommandLineRunner.java
index fe0518efe..4a72c4477 100644
--- a/src/com/google/javascript/jscomp/AbstractCommandLineRunner.java
+++ b/src/com/google/javascript/jscomp/AbstractCommandLineRunner.java
@@ -925,7 +925,7 @@ abstract class AbstractCommandLineRunner<A extends Compiler,
       outputBundle();
       outputModuleGraphJson();
       return 0;
-    } else if (result.success) {
+    } else if (!options.checksOnly && result.success) {
       outputModuleGraphJson();
       if (modules == null) {
         outputSingleBinary();
diff --git a/src/com/google/javascript/jscomp/CommandLineRunner.java b/src/com/google/javascript/jscomp/CommandLineRunner.java
index 9c11a85e2..f0de1f999 100644
--- a/src/com/google/javascript/jscomp/CommandLineRunner.java
+++ b/src/com/google/javascript/jscomp/CommandLineRunner.java
@@ -359,6 +359,10 @@ public class CommandLineRunner extends
     private String compilationLevel = "SIMPLE";
     private CompilationLevel compilationLevelParsed = null;
 
+    @Option(name = "--checks-only",
+        usage = "Don't generate output. Run checks, but no compiler passes.")
+    private boolean checksOnly = false;
+
     @Option(name = "--use_types_for_optimization",
         hidden = true,
         handler = BooleanOptionHandler.class,
@@ -1129,6 +1133,8 @@ public class CommandLineRunner extends
       level.setDebugOptionsForCompilationLevel(options);
     }
 
+    options.setChecksOnly(flags.checksOnly);
+
     if (flags.useTypesForOptimization) {
       level.setTypeBasedOptimizationOptions(options);
     }
diff --git a/src/com/google/javascript/jscomp/Compiler.java b/src/com/google/javascript/jscomp/Compiler.java
index da2470574..158e8faf5 100644
--- a/src/com/google/javascript/jscomp/Compiler.java
+++ b/src/com/google/javascript/jscomp/Compiler.java
@@ -760,7 +760,7 @@ public class Compiler extends AbstractCompiler {
       }
 
       // IDE-mode is defined to stop here, before the heavy rewriting begins.
-      if (!options.ideMode) {
+      if (!options.checksOnly && !options.ideMode) {
         optimize();
       }
     }
diff --git a/src/com/google/javascript/jscomp/CompilerOptions.java b/src/com/google/javascript/jscomp/CompilerOptions.java
index 22e977081..3e4df10d6 100644
--- a/src/com/google/javascript/jscomp/CompilerOptions.java
+++ b/src/com/google/javascript/jscomp/CompilerOptions.java
@@ -758,6 +758,8 @@ public class CompilerOptions implements Serializable, Cloneable {
   /** Record function information */
   public boolean recordFunctionInformation;
 
+  public boolean checksOnly;
+
   public boolean generateExports;
 
   boolean exportLocalPropertyDefinitions;
@@ -1095,6 +1097,7 @@ public class CompilerOptions implements Serializable, Cloneable {
     moveFunctionDeclarations = false;
     appNameStr = "";
     recordFunctionInformation = false;
+    checksOnly = false;
     generateExports = false;
     exportLocalPropertyDefinitions = false;
     cssRenamingMap = null;
@@ -1527,6 +1530,10 @@ public class CompilerOptions implements Serializable, Cloneable {
     this.runtimeTypeCheck = false;
   }
 
+  public void setChecksOnly(boolean checksOnly) {
+    this.checksOnly = checksOnly;
+  }
+
   public void setGenerateExports(boolean generateExports) {
     this.generateExports = generateExports;
   }


Test patch:
diff --git a/test/com/google/javascript/jscomp/CommandLineRunnerTest.java b/test/com/google/javascript/jscomp/CommandLineRunnerTest.java
index 32e8b1a08..195bb6bf9 100644
--- a/test/com/google/javascript/jscomp/CommandLineRunnerTest.java
+++ b/test/com/google/javascript/jscomp/CommandLineRunnerTest.java
@@ -1174,6 +1174,26 @@ public final class CommandLineRunnerTest extends TestCase {
         CheckMissingReturn.MISSING_RETURN_STATEMENT);
   }
 
+  public void testChecksOnlySkipsOptimizations() {
+    args.add("--checks-only");
+    test("var foo = 1 + 1;",
+      "var foo = 1 + 1;");
+  }
+
+  public void testChecksOnlyWithParseError() {
+    args.add("--compilation_level=WHITESPACE_ONLY");
+    args.add("--checks-only");
+    test("val foo = 1;",
+      RhinoErrorReporter.PARSE_ERROR);
+  }
+
+  public void testChecksOnlyWithWarning() {
+    args.add("--checks-only");
+    args.add("--warning_level=VERBOSE");
+    test("/** @deprecated */function foo() {}; foo();",
+      CheckAccessControls.DEPRECATED_NAME);
+  }
+
   public void testGenerateExports() {
     args.add("--generate_exports=true");
     test("/** @export */ foo.prototype.x = function() {};",