Repository: google/closure-compiler
Pull request #1225 (2015-10-22 14:58:58)

Report early errors for invalid increment/decrement target.
Reports something like:

```javascript
test.js:1: ERROR - Parse error. Invalid postfix increment operand.
12++;
^

test.js:2: ERROR - Parse error. Invalid postfix decrement operand.
(+foo)--;
 ^

test.js:3: ERROR - Parse error. Invalid prefix increment operand.
++this;
  ^

test.js:4: ERROR - Parse error. Invalid prefix decrement operand.
--(a + b);
   ^
```

blickly: Can you add a few unit tests to NewParserTest?
rintaro: Done. Please tell me if squash needed.
MatrixFrog: lgtm. @blickly do we still need (or want) squashing to happen?
dimvar: Yes, it's better to have a clean history in the main branch, with each commit being a logical unit of work.

Patch:
diff --git a/contrib/externs/angular-material.js b/contrib/externs/angular-material.js
index b76b3b941..1fb0b5053 100644
--- a/contrib/externs/angular-material.js
+++ b/contrib/externs/angular-material.js
@@ -436,16 +436,3 @@ md.$mdDateLocaleProvider.prototype.weekNumberFormatter = function(weekNum) {};
 
 /** @type {function(Date): string} */
 md.$mdDateLocaleProvider.prototype.longDateFormatter = function(date) {};
-
-
-/******************************************************************************
- * $mdGestureProvider
- *****************************************************************************/
-
-/** @constructor */
-md.$mdGestureProvider = function() {};
-
-/**
- * @return {boolean}
- */
-md.$mdGestureProvider.prototype.skipClickHijack = function() {};
diff --git a/src/com/google/javascript/jscomp/CheckRequiresForConstructors.java b/src/com/google/javascript/jscomp/CheckRequiresForConstructors.java
index a0db4d6a4..01d7f57c5 100644
--- a/src/com/google/javascript/jscomp/CheckRequiresForConstructors.java
+++ b/src/com/google/javascript/jscomp/CheckRequiresForConstructors.java
@@ -90,7 +90,7 @@ class CheckRequiresForConstructors implements HotSwapCompilerPass, NodeTraversal
    */
   @Override
   public void process(Node externs, Node root) {
-    NodeTraversal.traverseRootsEs6(compiler, this, externs, root);
+    NodeTraversal.traverseRoots(compiler, this, externs, root);
   }
 
   @Override
diff --git a/src/com/google/javascript/jscomp/ConstCheck.java b/src/com/google/javascript/jscomp/ConstCheck.java
index 7723b9985..2ca4caf26 100644
--- a/src/com/google/javascript/jscomp/ConstCheck.java
+++ b/src/com/google/javascript/jscomp/ConstCheck.java
@@ -55,7 +55,7 @@ class ConstCheck extends AbstractPostOrderCallback
   @Override
   public void process(Node externs, Node root) {
     Preconditions.checkState(compiler.getLifeCycleStage().isNormalized());
-    NodeTraversal.traverseRootsEs6(compiler, this, externs, root);
+    NodeTraversal.traverseRoots(compiler, this, externs, root);
   }
 
   @Override
diff --git a/src/com/google/javascript/jscomp/Es6RewriteBlockScopedDeclaration.java b/src/com/google/javascript/jscomp/Es6RewriteBlockScopedDeclaration.java
index 55909d96c..6ac258637 100644
--- a/src/com/google/javascript/jscomp/Es6RewriteBlockScopedDeclaration.java
+++ b/src/com/google/javascript/jscomp/Es6RewriteBlockScopedDeclaration.java
@@ -103,15 +103,15 @@ public final class Es6RewriteBlockScopedDeclaration extends AbstractPostOrderCal
 
   @Override
   public void process(Node externs, Node root) {
-    NodeTraversal.traverseRootsEs6(compiler, new CollectUndeclaredNames(), externs, root);
-    NodeTraversal.traverseRootsEs6(compiler, this, externs, root);
-    NodeTraversal.traverseRootsEs6(compiler, new Es6RenameReferences(renameMap), externs, root);
+    NodeTraversal.traverseRoots(compiler, new CollectUndeclaredNames(), externs, root);
+    NodeTraversal.traverseRoots(compiler, this, externs, root);
+    NodeTraversal.traverseRoots(compiler, new Es6RenameReferences(renameMap), externs, root);
 
     LoopClosureTransformer transformer = new LoopClosureTransformer();
-    NodeTraversal.traverseRootsEs6(compiler, transformer, externs, root);
+    NodeTraversal.traverseRoots(compiler, transformer, externs, root);
     transformer.transformLoopClosure();
     varify();
-    NodeTraversal.traverseRootsEs6(
+    NodeTraversal.traverseRoots(
         compiler, new RewriteBlockScopedFunctionDeclaration(), externs, root);
   }
 
diff --git a/src/com/google/javascript/jscomp/ImplicitNullabilityCheck.java b/src/com/google/javascript/jscomp/ImplicitNullabilityCheck.java
index 7da008d9c..da3dd3355 100644
--- a/src/com/google/javascript/jscomp/ImplicitNullabilityCheck.java
+++ b/src/com/google/javascript/jscomp/ImplicitNullabilityCheck.java
@@ -46,7 +46,7 @@ public final class ImplicitNullabilityCheck extends AbstractPostOrderCallback
 
   @Override
   public void process(Node externs, Node root) {
-    NodeTraversal.traverseRootsEs6(compiler, this, externs, root);
+    NodeTraversal.traverseRoots(compiler, this, externs, root);
   }
 
   /**
diff --git a/src/com/google/javascript/jscomp/MethodCompilerPass.java b/src/com/google/javascript/jscomp/MethodCompilerPass.java
index 2ac559162..f444a9101 100644
--- a/src/com/google/javascript/jscomp/MethodCompilerPass.java
+++ b/src/com/google/javascript/jscomp/MethodCompilerPass.java
@@ -74,8 +74,8 @@ abstract class MethodCompilerPass implements CompilerPass {
       NodeTraversal.traverseEs6(compiler, externs, new GetExternMethods());
     }
 
-    NodeTraversal.traverseRootsEs6(compiler, new GatherSignatures(), externs, root);
-    NodeTraversal.traverseRootsEs6(compiler, getActingCallback(), externs, root);
+    NodeTraversal.traverseRoots(compiler, new GatherSignatures(), externs, root);
+    NodeTraversal.traverseRoots(compiler, getActingCallback(), externs, root);
   }
 
   /**
diff --git a/src/com/google/javascript/jscomp/NodeTraversal.java b/src/com/google/javascript/jscomp/NodeTraversal.java
index 5223d3d18..44be76eed 100644
--- a/src/com/google/javascript/jscomp/NodeTraversal.java
+++ b/src/com/google/javascript/jscomp/NodeTraversal.java
@@ -547,9 +547,7 @@ public class NodeTraversal {
 
   /**
    * Traverses a node recursively.
-   * @deprecated Use traverseEs6 whenever possible.
    */
-  @Deprecated
   public static void traverse(AbstractCompiler compiler, Node root, Callback cb) {
     NodeTraversal t = new NodeTraversal(compiler, cb);
     t.traverse(root);
@@ -569,19 +567,12 @@ public class NodeTraversal {
     t.traverse(root);
   }
 
-  @Deprecated
   public static void traverseRoots(
       AbstractCompiler compiler, Callback cb, Node externs, Node root) {
     NodeTraversal t = new NodeTraversal(compiler, cb);
     t.traverseRoots(externs, root);
   }
 
-  public static void traverseRootsEs6(
-      AbstractCompiler compiler, Callback cb, Node externs, Node root) {
-    NodeTraversal t = new NodeTraversal(compiler, cb, new Es6SyntacticScopeCreator(compiler));
-    t.traverseRoots(externs, root);
-  }
-
   static void traverseRootsTyped(AbstractCompiler compiler, Callback cb, Node externs, Node root) {
     NodeTraversal t = new NodeTraversal(compiler, cb, SyntacticScopeCreator.makeTyped(compiler));
     t.traverseRoots(externs, root);
diff --git a/src/com/google/javascript/jscomp/Normalize.java b/src/com/google/javascript/jscomp/Normalize.java
index 0e3e6a2fa..c723a95c1 100644
--- a/src/com/google/javascript/jscomp/Normalize.java
+++ b/src/com/google/javascript/jscomp/Normalize.java
@@ -281,7 +281,7 @@ class Normalize implements CompilerPass {
       Node externsAndJs = root.getParent();
       Preconditions.checkState(externsAndJs != null);
       Preconditions.checkState(externsAndJs.hasChild(externs));
-      NodeTraversal.traverseRootsEs6(compiler, this, externs, root);
+      NodeTraversal.traverseRoots(compiler, this, externs, root);
     }
 
     private Map<String, Boolean> constantMap = new HashMap<>();
diff --git a/src/com/google/javascript/jscomp/StrictModeCheck.java b/src/com/google/javascript/jscomp/StrictModeCheck.java
index 4392a73f9..6d0963f6a 100644
--- a/src/com/google/javascript/jscomp/StrictModeCheck.java
+++ b/src/com/google/javascript/jscomp/StrictModeCheck.java
@@ -103,7 +103,7 @@ class StrictModeCheck extends AbstractPostOrderCallback
   }
 
   @Override public void process(Node externs, Node root) {
-    NodeTraversal.traverseRootsEs6(compiler, this, externs, root);
+    NodeTraversal.traverseRoots(compiler, this, externs, root);
     NodeTraversal.traverseEs6(compiler, root, new NonExternChecks());
   }
 
diff --git a/src/com/google/javascript/jscomp/parsing/IRFactory.java b/src/com/google/javascript/jscomp/parsing/IRFactory.java
index 94a397b14..7475097dd 100644
--- a/src/com/google/javascript/jscomp/parsing/IRFactory.java
+++ b/src/com/google/javascript/jscomp/parsing/IRFactory.java
@@ -1792,6 +1792,8 @@ class IRFactory {
               msg,
               sourceName,
               operand.getLineno(), 0);
+        } else if (type == Token.INC || type == Token.DEC) {
+          return createIncrDecrNode(type, false, operand);
         }
 
         return newNode(type, operand);
@@ -1800,8 +1802,26 @@ class IRFactory {
 
     Node processPostfixExpression(PostfixExpressionTree exprNode) {
       int type = transformPostfixTokenType(exprNode.operator.type);
-      Node node = newNode(type, transform(exprNode.operand));
-      node.putBooleanProp(Node.INCRDECR_PROP, true);
+      Node operand = transform(exprNode.operand);
+      if (type == Token.INC || type == Token.DEC) {
+        return createIncrDecrNode(type, true, operand);
+      }
+      Node node = newNode(type, operand);
+      return node;
+    }
+
+    private Node createIncrDecrNode(int type, boolean postfix, Node operand) {
+      if (!operand.isValidAssignmentTarget()) {
+        errorReporter.error(
+            SimpleFormat.format("Invalid %s %s operand.",
+                (postfix ? "postfix" : "prefix"),
+                (type == Token.INC ? "increment" : "decrement")),
+            sourceName,
+            operand.getLineno(),
+            operand.getCharno());
+      }
+      Node node = newNode(type, operand);
+      node.putBooleanProp(Node.INCRDECR_PROP, postfix);
       return node;
     }
 


Test patch:
diff --git a/test/com/google/javascript/jscomp/MakeDeclaredNamesUniqueTest.java b/test/com/google/javascript/jscomp/MakeDeclaredNamesUniqueTest.java
index a951335ad..e8e9a20fc 100644
--- a/test/com/google/javascript/jscomp/MakeDeclaredNamesUniqueTest.java
+++ b/test/com/google/javascript/jscomp/MakeDeclaredNamesUniqueTest.java
@@ -43,7 +43,7 @@ public final class MakeDeclaredNamesUniqueTest extends Es6CompilerTestCase {
             renamer = new MakeDeclaredNamesUnique(new InlineRenamer(compiler.getCodingConvention(),
                 compiler.getUniqueNameIdSupplier(), localNamePrefix, removeConst, true, null));
           }
-          NodeTraversal.traverseRootsEs6(compiler, renamer, externs, root);
+          NodeTraversal.traverseRoots(compiler, renamer, externs, root);
         }
       };
     } else {
diff --git a/test/com/google/javascript/jscomp/VarCheckTest.java b/test/com/google/javascript/jscomp/VarCheckTest.java
index d3f288147..caebb37d3 100644
--- a/test/com/google/javascript/jscomp/VarCheckTest.java
+++ b/test/com/google/javascript/jscomp/VarCheckTest.java
@@ -519,7 +519,7 @@ public final class VarCheckTest extends Es6CompilerTestCase {
 
     @Override
     public void process(Node externs, Node root) {
-      NodeTraversal.traverseRootsEs6(compiler,
+      NodeTraversal.traverseRoots(compiler,
           new AbstractPostOrderCallback() {
             @Override
             public void visit(NodeTraversal t, Node n, Node parent) {
diff --git a/test/com/google/javascript/jscomp/parsing/NewParserTest.java b/test/com/google/javascript/jscomp/parsing/NewParserTest.java
index fb5eab208..4124ec50b 100644
--- a/test/com/google/javascript/jscomp/parsing/NewParserTest.java
+++ b/test/com/google/javascript/jscomp/parsing/NewParserTest.java
@@ -939,6 +939,43 @@ public final class NewParserTest extends BaseJSTypeTestCase {
     }
   }
 
+  public void testPostfixExpression() {
+    parse("a++");
+    parse("a.b--");
+    parse("a[0]++");
+
+    parseError("a()++", "Invalid postfix increment operand.");
+    parseError("(new C)--", "Invalid postfix decrement operand.");
+    parseError("this++", "Invalid postfix increment operand.");
+    parseError("(a--)++", "Invalid postfix increment operand.");
+    parseError("(+a)++", "Invalid postfix increment operand.");
+    parseError("[1,2]++", "Invalid postfix increment operand.");
+    parseError("'literal'++", "Invalid postfix increment operand.");
+  }
+
+  public void testUnaryExpression() {
+    mode = LanguageMode.ECMASCRIPT6;
+    parse("delete a.b");
+    parse("delete a[0]");
+    parse("void f()");
+    parse("typeof new C");
+    parse("++a[0]");
+    parse("--a.b");
+    parse("+{a: 1}");
+    parse("-[1,2]");
+    parse("~'42'");
+    parse("!super.a");
+
+    parseError("delete f()", "Invalid delete operand. Only properties can be deleted.");
+    parseError("++a++", "Invalid prefix increment operand.");
+    parseError("--{a: 1}", "Invalid prefix decrement operand.");
+    parseError("++this", "Invalid prefix increment operand.");
+    parseError("++(-a)", "Invalid prefix increment operand.");
+    parseError("++{a: 1}", "Invalid prefix increment operand.");
+    parseError("++'literal'", "Invalid prefix increment operand.");
+    parseError("++delete a.b", "Invalid prefix increment operand.");
+  }
+
   public void testAutomaticSemicolonInsertion() {
     // var statements
     assertNodeEquality(