Repository: jshint/jshint (js)

Issue:
Trailing comma is allowed in ObjectBindingPattern
https://people.mozilla.org/~jorendorff/es6-draft.html#sec-destructuring-binding-patterns

Fixing this will fix https://github.com/STRML/JSXHint/issues/50

Pull request description:
[[FIX]] parse trailing comma in ObjectBindingPattern
Closes #2220

Patch:
diff --git a/src/jshint.js b/src/jshint.js
index 050c3626..23ecdecd 100644
--- a/src/jshint.js
+++ b/src/jshint.js
@@ -3301,15 +3301,15 @@ var JSHINT = (function() {
     }
     var nextInnerDE = function() {
       var ident;
-      if (_.contains(["[", "{"], state.tokens.next.value)) {
+      if (checkPunctuators(state.tokens.next, ["[", "{"])) {
         ids = destructuringExpression();
         for (var id in ids) {
           id = ids[id];
           identifiers.push({ id: id.id, token: id.token });
         }
-      } else if (state.tokens.next.value === ",") {
+      } else if (checkPunctuators(state.tokens.next, [","])) {
         identifiers.push({ id: null, token: state.tokens.curr });
-      } else if (state.tokens.next.value === "(") {
+      } else if (checkPunctuators(state.tokens.next, ["("])) {
         advance("(");
         nextInnerDE();
         advance(")");
@@ -3319,27 +3319,37 @@ var JSHINT = (function() {
           identifiers.push({ id: ident, token: state.tokens.curr });
       }
     };
-    if (state.tokens.next.value === "[") {
+    if (checkPunctuators(state.tokens.next, ["["])) {
       advance("[");
       nextInnerDE();
-      while (state.tokens.next.value !== "]") {
+      while (!checkPunctuators(state.tokens.next, ["]"])) {
         advance(",");
+        if (checkPunctuators(state.tokens.next, ["]"])) {
+          // Trailing commas are not allowed in ArrayBindingPattern
+          warning("W130", state.tokens.next);
+          break;
+        }
         nextInnerDE();
       }
       advance("]");
-    } else if (state.tokens.next.value === "{") {
+    } else if (checkPunctuators(state.tokens.next, ["{"])) {
       advance("{");
       id = identifier();
-      if (state.tokens.next.value === ":") {
+      if (checkPunctuators(state.tokens.next, [":"])) {
         advance(":");
         nextInnerDE();
       } else {
         identifiers.push({ id: id, token: state.tokens.curr });
       }
-      while (state.tokens.next.value !== "}") {
+      while (!checkPunctuators(state.tokens.next, ["}"])) {
         advance(",");
+        if (checkPunctuators(state.tokens.next, ["}"])) {
+          // Trailing comma
+          // ObjectBindingPattern: { BindingPropertyList , }
+          break;
+        }
         id = identifier();
-        if (state.tokens.next.value === ":") {
+        if (checkPunctuators(state.tokens.next, [":"])) {
           advance(":");
           nextInnerDE();
         } else {
diff --git a/src/messages.js b/src/messages.js
index f5e65749..cb5af1b5 100644
--- a/src/messages.js
+++ b/src/messages.js
@@ -204,7 +204,8 @@ var warnings = {
   W127: "Unexpected use of a comma operator.",
   W128: "Empty array elements require elision=true.",
   W129: "'{a}' is defined in a future version of JavaScript. Use a " +
-    "different variable name to avoid migration issues."
+    "different variable name to avoid migration issues.",
+  W130: "Trailing ',' is not valid in array destructuring assignment."
 };
 
 var info = {


Test patch:
diff --git a/tests/unit/parser.js b/tests/unit/parser.js
index d6d524d2..b41c1723 100644
--- a/tests/unit/parser.js
+++ b/tests/unit/parser.js
@@ -5494,3 +5494,60 @@ exports.dereferenceDelete = function (test) {
 
   test.done();
 };
+
+exports.trailingCommaInObjectBindingPattern = function (test) {
+  var code = [
+    'function fn(O) {',
+    '  var {a, b, c,} = O;',
+    '}',
+    'fn({ a: 1, b: 2, c: 3 });'
+  ];
+
+  TestRun(test)
+    .test(code, { esnext: true });
+
+  test.done();
+};
+
+
+exports.trailingCommaInObjectBindingPatternParameters = function (test) {
+  var code = [
+    'function fn({a, b, c,}) { }',
+    'fn({ a: 1, b: 2, c: 3 });'
+  ];
+
+  TestRun(test)
+    .test(code, { esnext: true });
+
+  test.done();
+};
+
+
+exports.trailingCommaInArrayBindingPattern = function (test) {
+  var code = [
+    'function fn(O) {',
+    '  var [a, b, c,] = O;',
+    '}',
+    'fn([1, 2, 3]);'
+  ];
+
+  TestRun(test)
+    .addError(2, "Trailing ',' is not valid in array destructuring assignment.")
+    .test(code, { esnext: true });
+
+  test.done();
+};
+
+
+exports.trailingCommaInArrayBindingPatternParameters = function (test) {
+  var code = [
+    'function fn([a, b, c,]) { }',
+    'fn([1, 2, 3]);'
+  ];
+
+  TestRun(test)
+    .addError(1, "Trailing ',' is not valid in array destructuring assignment.")
+    .test(code, { esnext: true });
+
+  test.done();
+};