Repository: jshint/jshint
Pull request #3412 (2019-07-08 01:40:19)

[[CHORE]] Optimize `case` clause tracking
Analyzing a `switch` statement involves tracking the first occurance of
a `case` clause. Only noting its presence is necessary, and it is only
necessary in the context of the token's processing logic.

Replace the Array instance with a boolenan flag scoped to the token's
function.

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

Coverage remained the same at 100.0% when pulling **5d783059c2273cba1d0f9214efd1c85fe9850f04 on jugglinmike:case-count** into **2366c92b852adeb963ecb812bc32b258288663dc on jshint:master**.

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

Coverage remained the same at 100.0% when pulling **5d783059c2273cba1d0f9214efd1c85fe9850f04 on jugglinmike:case-count** into **2366c92b852adeb963ecb812bc32b258288663dc on jshint:master**.


Patch:
diff --git a/src/jshint.js b/src/jshint.js
index baf95692..dd2ff862 100644
--- a/src/jshint.js
+++ b/src/jshint.js
@@ -2829,7 +2829,8 @@ var JSHINT = (function() {
       inGenerator = false;
       context &= ~prodParams.preAsync;
 
-      if (state.tokens.next.value === "static") {
+      if (state.tokens.next.value === "static" &&
+        !checkPunctuator(peek(), "(")) {
         isStatic = true;
         advance();
       }
@@ -4372,11 +4373,13 @@ var JSHINT = (function() {
         }
         var id = state.tokens.prev;
         value = expression(context, 10);
-        if (value && value.identifier && value.value === "undefined") {
-          warning("W080", id, id.value);
-        }
-        if (!lone) {
-          destructuringPatternMatch(names, value);
+        if (value) {
+          if (value.identifier && value.value === "undefined") {
+            warning("W080", id, id.value);
+          }
+          if (!lone) {
+            destructuringPatternMatch(names, value);
+          }
         }
       }
 
@@ -4558,12 +4561,14 @@ var JSHINT = (function() {
         }
         id = state.tokens.prev;
         value = expression(context, 10);
-        if (value && !state.funct["(loopage)"] && value.identifier &&
-          value.value === "undefined") {
-          warning("W080", id, id.value);
-        }
-        if (!lone) {
-          destructuringPatternMatch(names, value);
+        if (value) {
+          if (!state.funct["(loopage)"] && value.identifier &&
+            value.value === "undefined") {
+            warning("W080", id, id.value);
+          }
+          if (!lone) {
+            destructuringPatternMatch(names, value);
+          }
         }
       }
 
@@ -4828,6 +4833,7 @@ var JSHINT = (function() {
     var t = state.tokens.next;
     var g = false;
     var noindent = false;
+    var seenCase = false;
 
     state.funct["(breakage)"] += 1;
     advance("(");
@@ -4843,8 +4849,6 @@ var JSHINT = (function() {
     if (!noindent)
       indent += state.option.indent;
 
-    this.cases = [];
-
     for (;;) {
       switch (state.tokens.next.id) {
       case "case":
@@ -4873,7 +4877,8 @@ var JSHINT = (function() {
         }
 
         advance("case");
-        this.cases.push(expression(context, 0));
+        expression(context, 0);
+        seenCase = true;
         increaseComplexityCount();
         g = true;
         advance(":");
@@ -4896,10 +4901,8 @@ var JSHINT = (function() {
         default:
           // Do not display a warning if 'default' is the first statement or if
           // there is a special /* falls through */ comment.
-          if (this.cases.length) {
-            if (!state.tokens.curr.caseFallsThrough) {
-              warning("W086", state.tokens.curr, "default");
-            }
+          if (seenCase && !state.tokens.curr.caseFallsThrough) {
+            warning("W086", state.tokens.curr, "default");
           }
         }
 


Test patch:
diff --git a/tests/unit/parser.js b/tests/unit/parser.js
index b3fd37b4..28c96c0c 100644
--- a/tests/unit/parser.js
+++ b/tests/unit/parser.js
@@ -2493,6 +2493,25 @@ exports["destructuring assignment of valid simple assignment targets"] = functio
   test.done();
 };
 
+exports["regression test for GH-3408"] = function (test) {
+  TestRun(test, "var statement")
+    .addError(1, 9, "Expected an identifier and instead saw ';'.")
+    .addError(1, 10, "Missing semicolon.")
+    .test("var [x]=;", { esversion: 6 });
+
+  TestRun(test, "let statement")
+    .addError(1, 9, "Expected an identifier and instead saw ';'.")
+    .addError(1, 10, "Missing semicolon.")
+    .test("let [x]=;", { esversion: 6 });
+
+  TestRun(test, "const statement")
+    .addError(1, 11, "Expected an identifier and instead saw ';'.")
+    .addError(1, 12, "Missing semicolon.")
+    .test("const [x]=;", { esversion: 6 });
+
+  test.done();
+};
+
 exports["non-identifier PropertyNames in object destructuring"] = function (test) {
   var code = [
     "var { ['x' + 2]: a = 3, 0: b } = { x2: 1, 0: 2 };",
@@ -6825,6 +6844,28 @@ exports["class and method naming"] = function (test) {
       "};"
     ], {esversion: 6});
 
+  TestRun(test, "valid uses of name `static`")
+    .test([
+      "void class {",
+      "  static() {}",
+      "  static static() {}",
+      "  static ['static']() {}",
+      "};",
+      "void class {",
+      "  * static() { yield; }",
+      "  static * static() { yield; }",
+      "  static * ['static']() { yield; }",
+      "};",
+      "void class {",
+      "  get static() {}",
+      "  set static(x) {}",
+      "  static get static() {}",
+      "  static set static(x) {}",
+      "  static get ['static']() {}",
+      "  static set ['static'](x) {}",
+      "};"
+    ], {esversion: 6});
+
   TestRun(test, "invalid use of name `prototype`: static method")
     .addError(2, 10, "A static class method cannot be named 'prototype'.")
     .test([