Repository: jshint/jshint
Pull request #3311 (2018-07-22 20:44:12)

[[FIX]] Tolerate division following closing brace
This resolves gh-3308

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

Coverage remained the same at 98.753% when pulling **062643e3e70436dae7e0f5af462bd807a6768980 on jugglinmike:brace-then-divide** into **ff71d3cbcb4f7c4a88b1a50130890564e6440484 on jshint:master**.


Patch:
diff --git a/src/jshint.js b/src/jshint.js
index 612fa7a2..d7984701 100644
--- a/src/jshint.js
+++ b/src/jshint.js
@@ -1764,6 +1764,9 @@ var JSHINT = (function() {
         metrics.statementCount += a.length;
 
         indent -= state.option.indent;
+      } else if (isfunc) {
+        // Ensure property is set for functions with empty bodies.
+        state.funct["(isStrict)"] = state.isStrict();
       }
 
       advance("}", t);
@@ -3055,11 +3058,6 @@ var JSHINT = (function() {
       warning("W124", state.tokens.curr);
     }
 
-    if (state.funct["(isStrict)"] === true &&
-      (name === "arguments" || name === "eval")) {
-      error("E008", token);
-    }
-
     state.funct["(metrics)"].verifyMaxStatementsPerFunction();
     state.funct["(metrics)"].verifyMaxComplexityPerFunction();
     state.funct["(unusedOption)"] = state.option.unused;
@@ -3876,27 +3874,40 @@ var JSHINT = (function() {
     if (inblock) {
       warning("W082", state.tokens.curr);
     }
-    var i = optionalidentifier();
+    var nameToken = optionalidentifier() ? state.tokens.curr : null;
 
-    if (i === undefined) {
+    if (!nameToken) {
       warning("W025");
     } else {
-      state.funct["(scope)"].addlabel(i, {
+      state.funct["(scope)"].addlabel(nameToken.value, {
         type: generator ? "generator function" : "function",
         token: state.tokens.curr,
         initialized: true });
 
       if (inexport) {
-        state.funct["(scope)"].setExported(i, state.tokens.prev);
+        state.funct["(scope)"].setExported(nameToken.value, state.tokens.prev);
       }
     }
 
-    doFunction({
-      name: i,
+    var f = doFunction({
+      name: nameToken && nameToken.value,
       statement: this,
       type: generator ? "generator" : null,
       ignoreLoopFunc: inblock // a declaration may already have warned
     });
+
+    // If the function declaration is strict because the surrounding code is
+    // strict, the invalid name will trigger E008 when the scope manager
+    // attempts to create a binding in the strict environment record. An error
+    // should only be signaled here when the function itself enables strict
+    // mode (the scope manager will not report an error because a declaration
+    // does not introduce a binding into the function's environment record).
+    var enablesStrictMode = f["(isStrict)"] && !state.isStrict();
+    if (nameToken && (f["(name)"] === "arguments" || f["(name)"] === "eval") &&
+      enablesStrictMode) {
+      error("E008", nameToken);
+    }
+
     if (state.tokens.next.id === "(" && state.tokens.next.line === state.tokens.curr.line) {
       error("E039");
     }
@@ -3914,8 +3925,17 @@ var JSHINT = (function() {
       generator = true;
     }
 
-    var i = optionalidentifier();
-    doFunction({ name: i, type: generator ? "generator" : null });
+    var nameToken = optionalidentifier() ? state.tokens.curr : null;
+
+    var f = doFunction({
+      name: nameToken && nameToken.value,
+      type: generator ? "generator" : null
+    });
+
+    if (nameToken && (f["(name)"] === "arguments" || f["(name)"] === "eval") &&
+      f["(isStrict)"]) {
+      error("E008", nameToken);
+    }
     return this;
   });
 
diff --git a/src/lex.js b/src/lex.js
index 2d361de6..066a59ec 100644
--- a/src/lex.js
+++ b/src/lex.js
@@ -1690,6 +1690,7 @@ Lexer.prototype = {
         case "~":
         case "#":
         case "]":
+        case "}":
         case "++":
         case "--":
           this.prereg = false;


Test patch:
diff --git a/tests/test262/expectations.txt b/tests/test262/expectations.txt
index ec074e3c..c45eeb00 100644
--- a/tests/test262/expectations.txt
+++ b/tests/test262/expectations.txt
@@ -652,10 +652,6 @@ test/language/expressions/exponentiation/exp-operator-precedence-update-expressi
 test/language/expressions/exponentiation/exp-operator.js(default)
 test/language/expressions/exponentiation/exp-operator.js(strict mode)
 test/language/expressions/delete/11.4.1-5-a-5gs.js(strict mode)
-test/language/expressions/division/S11.5.2_A2.2_T1.js(default)
-test/language/expressions/division/S11.5.2_A2.2_T1.js(strict mode)
-test/language/expressions/division/S11.5.2_A3_T1.5.js(default)
-test/language/expressions/division/S11.5.2_A3_T1.5.js(strict mode)
 test/language/expressions/instanceof/S11.8.6_A3.js(default)
 test/language/expressions/instanceof/S11.8.6_A3.js(strict mode)
 test/language/expressions/postfix-decrement/11.3.2-2-3-s.js(default)
diff --git a/tests/unit/parser.js b/tests/unit/parser.js
index b553e1d9..7d0d55e5 100644
--- a/tests/unit/parser.js
+++ b/tests/unit/parser.js
@@ -643,6 +643,8 @@ exports.regexp = function (test) {
   TestRun(test).test("var a = 1; var b = a-- / 10;", {esnext: true});
   TestRun(test).test("var a = 1; var b = a-- / 10;", {moz: true});
 
+  TestRun(test, "gh-3308").test("void (function() {} / 0);");
+
   test.done();
 };
 
@@ -3679,17 +3681,49 @@ exports["strict violation - use of arguments and eval"] = function (test) {
   TestRun(test, "as function declaration binding (invalid)")
     .addError(1, 10, "Strict violation.")
     .addError(2, 10, "Strict violation.")
+    .addError(5, 12, "Strict violation.")
+    .addError(6, 12, "Strict violation.")
+    .addError(10, 12, "Strict violation.")
+    .addError(10, 26, "Unnecessary directive \"use strict\".")
+    .addError(11, 12, "Strict violation.")
+    .addError(11, 21, "Unnecessary directive \"use strict\".")
     .test([
       "function arguments() { 'use strict'; }",
-      "function eval() { 'use strict'; }"
+      "function eval() { 'use strict'; }",
+      "(function() {",
+      "  'use strict';",
+      "  function arguments() {}",
+      "  function eval() {}",
+      "}());",
+      "(function() {",
+      "  'use strict';",
+      "  function arguments() { 'use strict'; }",
+      "  function eval() { 'use strict'; }",
+      "}());"
     ]);
 
   TestRun(test, "as function expression binding (invalid)")
     .addError(1, 15, "Strict violation.")
     .addError(2, 15, "Strict violation.")
+    .addError(5, 17, "Strict violation.")
+    .addError(6, 17, "Strict violation.")
+    .addError(10, 17, "Strict violation.")
+    .addError(10, 31, "Unnecessary directive \"use strict\".")
+    .addError(11, 17, "Strict violation.")
+    .addError(11, 26, "Unnecessary directive \"use strict\".")
     .test([
       "void function arguments() { 'use strict'; };",
-      "void function eval() { 'use strict'; };"
+      "void function eval() { 'use strict'; };",
+      "(function() {",
+      "  'use strict';",
+      "  void function arguments() {};",
+      "  void function eval() {};",
+      "}());",
+      "(function() {",
+      "  'use strict';",
+      "  void function arguments() { 'use strict'; };",
+      "  void function eval() { 'use strict'; };",
+      "}());"
     ]);
 
   test.done();