Repository: jshint/jshint
Pull request #2063 (2014-12-31 03:58:05)

Add option `elision` to prevent warning about empty array elements
A quick attempt at making it possible to ignore this error, while still enabling it by default.

WDYT? I feel like using this feature is typically an error, unless people specifically ask for it, such as for
engine unit tests or polyfill unit tests.

BREAKING CHANGE:

In projects which do not enable ES3 mode, it is now an error by default to use elision array elements,
also known as empty array elements (such as `[1, , 3, , 5]`)

Fixes gh-2062

rwaldron: Please update the commit message to follow https://github.com/jshint/jshint/blob/master/CONTRIBUTING.md#commit-message-guidelines Thanks!!
ljharb: Yay, thanks @caitp!
caitp: I guess I'll update this commit message now :>
caitp: @rwaldron how do you feel about the breaking change, though? I'm undecided about it

Patch:
diff --git a/src/jshint.js b/src/jshint.js
index 44279acf..0527565e 100644
--- a/src/jshint.js
+++ b/src/jshint.js
@@ -2630,8 +2630,19 @@ var JSHINT = (function () {
     }
     while (state.tokens.next.id !== "(end)") {
       while (state.tokens.next.id === ",") {
-        if (!state.option.inES5())
-          warning("W070");
+        if (!state.option.elision) {
+          if (!state.option.inES5()) {
+            // Maintain compat with old options --- ES5 mode without
+            // elision=true will warn once per comma
+            warning("W070");
+          } else {
+            warning("W128");
+            do {
+              advance(",");
+            } while(state.tokens.next.id === ",");
+            continue;
+          }
+        }
         advance(",");
       }
 
diff --git a/src/messages.js b/src/messages.js
index 803c7a08..09cc6923 100644
--- a/src/messages.js
+++ b/src/messages.js
@@ -201,7 +201,8 @@ var warnings = {
   W124: "A generator function shall contain a yield statement.",
   W125: "This line contains non-breaking spaces: http://jshint.com/doc/options/#nonbsp",
   W126: "Grouping operator is unnecessary for lone expressions.",
-  W127: "Unexpected use of a comma operator."
+  W127: "Unexpected use of a comma operator.",
+  W128: "Empty array elements require elision=true."
 };
 
 var info = {
diff --git a/src/options.js b/src/options.js
index 0fb90e2b..3efdf869 100644
--- a/src/options.js
+++ b/src/options.js
@@ -503,7 +503,13 @@ exports.bool = {
      * * [Draft Specification for ES.next (ECMA-262 Ed.
      *   6)](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts)
      */
-    esnext      : true
+    esnext      : true,
+
+    /**
+     * This option tells JSHint that your code uses ES3 array elision elements,
+     * or empty elements (for example, `[1, , , 4, , , 7]`).
+     */
+    elision     : true,
   },
 
   // Third party globals


Test patch:
diff --git a/tests/unit/core.js b/tests/unit/core.js
index 1fbcef12..f94f4cdc 100644
--- a/tests/unit/core.js
+++ b/tests/unit/core.js
@@ -524,7 +524,7 @@ exports.testSparseArrays = function (test) {
     .test(src, {es3: true});
 
   TestRun(test)
-    .test(src, {}); // es5
+    .test(src, { elision: true }); // es5
 
   test.done();
 };
diff --git a/tests/unit/options.js b/tests/unit/options.js
index d4d234cf..490c5ce1 100644
--- a/tests/unit/options.js
+++ b/tests/unit/options.js
@@ -1766,3 +1766,40 @@ exports.singleGroups = function (test) {
 
   test.done();
 };
+
+exports.elision = function (test) {
+  var code = [
+    "var a = [1,,2];",
+    "var b = [1,,,,2];",
+    "var c = [1,2,];",
+    "var d = [,1,2];",
+    "var e = [,,1,2];",
+  ];
+
+  TestRun(test, "elision=false ES5")
+    .addError(1, "Empty array elements require elision=true.")
+    .addError(2, "Empty array elements require elision=true.")
+    .addError(4, "Empty array elements require elision=true.")
+    .addError(5, "Empty array elements require elision=true.")
+    .test(code, { elision: false, es3: false });
+
+  TestRun(test, "elision=false ES3")
+    .addError(1, "Extra comma. (it breaks older versions of IE)")
+    .addError(2, "Extra comma. (it breaks older versions of IE)")
+    .addError(2, "Extra comma. (it breaks older versions of IE)")
+    .addError(2, "Extra comma. (it breaks older versions of IE)")
+    .addError(3, "Extra comma. (it breaks older versions of IE)")
+    .addError(4, "Extra comma. (it breaks older versions of IE)")
+    .addError(5, "Extra comma. (it breaks older versions of IE)")
+    .addError(5, "Extra comma. (it breaks older versions of IE)")
+    .test(code, { elision: false, es3: true });
+
+  TestRun(test, "elision=true ES5")
+    .test(code, { elision: true, es3: false });
+
+  TestRun(test, "elision=true ES3")
+    .addError(3, "Extra comma. (it breaks older versions of IE)")
+    .test(code, { elision: true, es3: true });
+
+  test.done();
+};
diff --git a/tests/unit/parser.js b/tests/unit/parser.js
index c55ad960..9e1ea181 100644
--- a/tests/unit/parser.js
+++ b/tests/unit/parser.js
@@ -1376,7 +1376,7 @@ exports["destructuring assign of empty values as moz"] = function (test) {
     .addError(2, "'d' is defined but never used.")
     .addError(3, "'e' is defined but never used.")
     .addError(3, "'f' is defined but never used.")
-    .test(code, {moz: true, unused: true, undef: true, laxcomma: true});
+    .test(code, {moz: true, unused: true, undef: true, laxcomma: true, elision: true});
 
   test.done();
 };
@@ -1394,7 +1394,7 @@ exports["destructuring assign of empty values as esnext"] = function (test) {
     .addError(2, "'d' is defined but never used.")
     .addError(3, "'e' is defined but never used.")
     .addError(3, "'f' is defined but never used.")
-    .test(code, {esnext: true, unused: true, undef: true});
+    .test(code, {esnext: true, unused: true, undef: true, elision: true});
 
   test.done();
 };
@@ -1415,7 +1415,7 @@ exports["destructuring assign of empty values as es5"] = function (test) {
     .addError(3, "'destructuring expression' is available in ES6 (use esnext option) or Mozilla JS extensions (use moz).")
     .addError(3, "'e' is defined but never used.")
     .addError(3, "'f' is defined but never used.")
-    .test(code, {unused: true, undef: true}); // es5
+    .test(code, {unused: true, undef: true, elision: true}); // es5
 
   test.done();
 };