Repository: jshint/jshint
Pull request #3378 (2019-03-25 15:28:46)

[[FIX]] Interpret "object rest" ident as a binding
Previously, JSHint interpreted token sequences following the "object
rest" operator as expressions. As noted in the in-line comment, this was
done to improve the chance of recovery in response to a common
programming error. Unfortunately, it also caused valid syntax to be
misinterpreted--the expected identifier was treated as a
IdentifierReference instead of a BindingIdentifier.

Update the parsing strategy to use the expression parsing fallback only
when the input is known to be invalid.

This should resolve gh-3377

coveralls: 
[![Coverage Status](https://coveralls.io/builds/22388032/badge)](https://coveralls.io/builds/22388032)

Coverage increased (+0.0003%) to 98.545% when pulling **a9ebfc3f230ecf0250a2d3e2d8e6aad4308fe31d on jugglinmike:obj-rest-binding** into **07ff25d3f4379415f0b438d9fb7cc6f6cabb7f7a on jshint:master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/22388032/badge)](https://coveralls.io/builds/22388032)

Coverage increased (+0.0003%) to 98.545% when pulling **a9ebfc3f230ecf0250a2d3e2d8e6aad4308fe31d on jugglinmike:obj-rest-binding** into **07ff25d3f4379415f0b438d9fb7cc6f6cabb7f7a on jshint:master**.

jugglinmike: Looks like Node got an upgrade--JSHint is now passing more RegExp parsing tests than it used to. We'll take care of that in gh-3379 and then update this branch accordingly.
rwaldron: @jugglinmike travis is failing on the new hashbang tests: 

```
   17 invalid programs produced a parsing error (in violation of the expectations file):
   test/language/comments/hashbang/escaped-bang-041.js(default)
   test/language/comments/hashbang/escaped-bang-u0021.js(default)
   test/language/comments/hashbang/escaped-bang-u21.js(default)
   test/language/comments/hashbang/escaped-bang-x21.js(default)
   test/language/comments/hashbang/escaped-hash-043.js(default)
   test/language/comments/hashbang/escaped-hash-u0023.js(default)
   test/language/comments/hashbang/escaped-hash-u23.js(default)
   test/language/comments/hashbang/escaped-hash-x23.js(default)
   test/language/comments/hashbang/escaped-hashbang.js(default)
   test/language/comments/hashbang/multi-line-comment.js(default)
   test/language/comments/hashbang/preceding-directive-prologue-sc.js(default)
   test/language/comments/hashbang/preceding-directive-prologue.js(default)
   test/language/comments/hashbang/preceding-empty-statement.js(default)
   test/language/comments/hashbang/preceding-hashbang.js(default)
   test/language/comments/hashbang/preceding-line-comment.js(default)
   test/language/comments/hashbang/preceding-multi-line-comment.js(default)
   test/language/comments/hashbang/preceding-whitespace.js(default)
```
rwaldron: Oh sorry, I understand now that I see gh-3379
rwaldron: @jugglinmike https://github.com/jshint/jshint/pull/3379 has landed. 
jugglinmike: Rebased and passing as expected, @rwaldron 

Patch:
diff --git a/src/jshint.js b/src/jshint.js
index 40cc3db8..60d031a0 100644
--- a/src/jshint.js
+++ b/src/jshint.js
@@ -4135,12 +4135,13 @@ var JSHINT = (function() {
 
           // Due to visual symmetry with the array rest property (and the early
           // design of the language feature), developers may mistakenly assume
-          // any expression is valid in this position.  Parse an expression and
-          // issue an error in order to recover more gracefully from this
-          // condition.
-          expr = expression(context, 10);
-
-          if (expr.type !== "(identifier)") {
+          // any expression is valid in this position. If the next token is not
+          // an identifier, attempt to parse an expression and issue an error.
+          // order to recover more gracefully from this condition.
+          if (state.tokens.next.type === "(identifier)") {
+            id = identifier(context);
+          } else {
+            expr = expression(context, 10);
             error("E030", expr, expr.value);
           }
         } else {


Test patch:
diff --git a/tests/unit/objrestspread.js b/tests/unit/objrestspread.js
index f2489d16..d000a42c 100644
--- a/tests/unit/objrestspread.js
+++ b/tests/unit/objrestspread.js
@@ -115,5 +115,9 @@ exports.rest = function (test) {
     .addError(1, 7, "Expected an identifier and instead saw '{'.")
     .test("({ ...{} } = {});", { esversion: 9 });
 
+  TestRun(test, "gh-3377 - identifier interpreted as new binding, not reference")
+    .addError(1, 10, "'x' is defined but never used.")
+    .test("var { ...x } = {};", { esversion: 9, unused: true, undef: true });
+
   test.done();
 };