Repository: jshint/jshint (js)

Issue:
add new Jasmine 2.1 globals to Jasmine environment option
Jasmine 2.1 added a handful of new globals that can be added to JSHint's "jasmine" environment option.

The new globals are:

* `beforeAll`
* `afterAll`
* `fail`
* `fdescribe`
* `fit`

https://github.com/jasmine/jasmine/blob/v2.1.0/release_notes/2.1.0.md

Pull request:
Add new Jasmine 2.1 globals to Jasmine option
Resolves #2023.

Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3e69e2ff..9ad155c3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,7 +7,8 @@ to a snippet, graffiti outside of Anton's apartment, etc.
 
 However, before sending a patch, please make sure that the following applies:
 
-* Your commit message follows the [Commit Message Guidelines](#commit-message-guidelines).
+* Your commit message links to that issue.
+* Your commit message is very descriptive ([example](https://github.com/jshint/jshint/commit/5751c5ed249b7a035758a3ae876cfa1a360fd144)).
 * Your patch doesn't have useless merge commits.
 * Your coding style is similar to ours (see below).
 * Your patch is 100% tested. We don't accept any test regressions.
@@ -96,78 +97,3 @@ token.isPunctuator(";") && report.addWarning("W001");
 if (token.isPunctuator(";"))
   report.addWarning("W001");
 ```
-
-Commit Message Guidelines
--------------------------
-
-### Overview
-
-Commit messages are written in a simple format which clearly describes the purpose of a change.
-
-The format in general should look like this:
-
-```
-[[TYPE]] <Short description>
-<Blank line>
-
-<Body / Detailed description>
-
-<Footer>
-```
-
-Line lengths in commit messages are not strict, but good commit messages should have headers of no
-more than 60 characters, and bodies/footers wrapped at 100 columns. This renders nicely on Github's
-UI.
-
-### Header
-
-The first line is the commit message header, which will indicate the type of change, and a general
-description of the change. This should fit within 60 characters, ideally. For instance:
-
-```
-[[FIX]] Ignore "nocomma" when parsing object literals
-```
-
-The title `[[FIX]]` indicates that the change is a bugfix, while the remainder clarifies what the
-change actually contains.
-
-Several commit types are used by jshint:
-
-1. `[[FIX]]` --- Commit fixes a bug or regression
-2. `[[FEAT]]` --- Commit introduces new functionality
-3. `[[DOCS]]` --- Commit modifies documentation. Docs commits should only touch comments in source code, or scripts and assets which are used to generate the documentation.
-
-### Body
-
-`<Body>` is a detailed commit message explaining exactly what has changed, and a summary of the
-reason why. Lines in the body should be wrapped to 100 characters for best rendering.
-
-For a historical example, see this [example](https://github.com/jshint/jshint/commit/                                     5751c5ed249b7a035758a3ae876cfa1a360fd144) 
-
-### Footer
-
-`<Footer>` contains a description of any breaking changes, no matter how subtle, as well as a list
-of issues affected or fixed by this commit. Lines in the footer should be wrapped to 100 characters
-for best rendering.
-
-For instance:
-
-```
-[[FEAT]] Enable `norecurs` option by default
-
-Commit 124124a7f introduced an option which forbids recursion. We liked it so much, we've enabled
-it by default.
-
-BREAKING CHANGE:
-
-This change will break the CI builds of many applications and frameworks.
-
-In order to work around this issue, you will need to re-engineer your applications and frameworks
-to avoid making recursive calls. Use Arrays as stacks rather than relying on the VM call stack.
-
-Fixes #1000009
-Closes #888888
-Closes #77777
-```
-
-
diff --git a/dist/jshint-rhino.js b/dist/jshint-rhino.js
index d8d56ce7..5b5ac404 100755
--- a/dist/jshint-rhino.js
+++ b/dist/jshint-rhino.js
@@ -4988,7 +4988,7 @@ exports.bool = {
     /**
      * This option requires all `for in` loops to filter object's items. The
      * for in statement allows for looping through the names of all of the
-     * properties of an object including those inherited through the prototype
+     * properties of an object including those inherited throught the prototype
      * chain. This behavior can lead to unexpected items in your object so it
      * is generally safer to always filter inherited properties out as shown in
      * the example:
@@ -6675,12 +6675,10 @@ exports.mocha = {
   beforeEach  : false,
   afterEach   : false,
   // TDD
-  suite         : false,
-  test          : false,
-  setup         : false,
-  teardown      : false,
-  suiteSetup    : false,
-  suiteTeardown : false
+  suite       : false,
+  test        : false,
+  setup       : false,
+  teardown    : false
 };
 
 exports.jasmine = {
diff --git a/dist/jshint.js b/dist/jshint.js
index ffd1144d..840ad4aa 100644
--- a/dist/jshint.js
+++ b/dist/jshint.js
@@ -4986,7 +4986,7 @@ exports.bool = {
     /**
      * This option requires all `for in` loops to filter object's items. The
      * for in statement allows for looping through the names of all of the
-     * properties of an object including those inherited through the prototype
+     * properties of an object including those inherited throught the prototype
      * chain. This behavior can lead to unexpected items in your object so it
      * is generally safer to always filter inherited properties out as shown in
      * the example:
@@ -6673,12 +6673,10 @@ exports.mocha = {
   beforeEach  : false,
   afterEach   : false,
   // TDD
-  suite         : false,
-  test          : false,
-  setup         : false,
-  teardown      : false,
-  suiteSetup    : false,
-  suiteTeardown : false
+  suite       : false,
+  test        : false,
+  setup       : false,
+  teardown    : false
 };
 
 exports.jasmine = {
diff --git a/src/jshint.js b/src/jshint.js
index 6fd9eecf..0d0f3440 100644
--- a/src/jshint.js
+++ b/src/jshint.js
@@ -1169,10 +1169,14 @@ var JSHINT = (function () {
     var x = symbol(s, 42);
 
     x.led = function (left) {
+      if (!state.option.esnext) {
+        warning("W119", state.tokens.curr, "arrow function syntax (=>)");
+      }
+
       nobreaknonadjacent(state.tokens.prev, state.tokens.curr);
 
       this.left = left;
-      this.right = doFunction(undefined, undefined, false, { loneArg: left });
+      this.right = doFunction(undefined, undefined, false, left);
       return this;
     };
     return x;
@@ -1451,14 +1455,6 @@ var JSHINT = (function () {
       }
     } else {
       error("E030", state.tokens.next, state.tokens.next.value);
-
-      // The token should be consumed after a warning is issued so the parser
-      // can continue as though an identifier were found. The semicolon token
-      // should not be consumed in this way so that the parser interprets it as
-      // a statement delimeter;
-      if (state.tokens.next.id !== ";") {
-        advance();
-      }
     }
   }
 
@@ -1881,18 +1877,6 @@ var JSHINT = (function () {
       var f;
       var block;
 
-      // If this identifier is the lone parameter to a shorthand "fat arrow"
-      // function definition, i.e.
-      //
-      //     x => x;
-      //
-      // ...it should not be considered as a variable in the current scope. It
-      // will be added to the scope of the new function when the next token is
-      // parsed, so it can be safely ignored for now.
-      if (state.tokens.next.id === "=>") {
-        return this;
-      }
-
       if (typeof s === "function") {
         // Protection against accidental inheritance.
         s = undefined;
@@ -2443,8 +2427,8 @@ var JSHINT = (function () {
       }
       if (!left.identifier && left.id !== "." && left.id !== "[" &&
           left.id !== "(" && left.id !== "&&" && left.id !== "||" &&
-          left.id !== "?" && !(state.option.esnext && left["(name)"])) {
-        warning("W067", that);
+          left.id !== "?" && !(state.option.esnext && left.id === "=>")) {
+        warning("W067", left);
       }
     }
 
@@ -2454,11 +2438,13 @@ var JSHINT = (function () {
 
   prefix("(", function () {
     var bracket, brackets = [];
-    var pn = state.tokens.next, pn1, i = -1;
+    var pn, pn1, i = 0;
     var ret, triggerFnExpr;
     var parens = 1;
 
     do {
+      pn = peek(i);
+
       if (pn.value === "(") {
         parens += 1;
       } else if (pn.value === ")") {
@@ -2466,27 +2452,19 @@ var JSHINT = (function () {
       }
 
       i += 1;
-      pn1 = pn;
-      pn = peek(i);
-    } while (!(parens === 0 && pn1.value === ")") &&
-             pn.value !== ";" && pn.type !== "(end)");
+      pn1 = peek(i);
+    } while (!(parens === 0 && pn.value === ")") &&
+             pn1.value !== "=>" && pn1.value !== ";" && pn1.type !== "(end)");
 
     if (state.tokens.next.id === "function") {
       triggerFnExpr = state.tokens.next.immed = true;
     }
 
-    // If the balanced grouping operator is followed by a "fat arrow", the
-    // current token marks the beginning of a "fat arrow" function and parsing
-    // should proceed accordingly.
-    if (pn.value === "=>") {
-      return doFunction(null, null, null, { parsedParen: true });
-    }
-
     var exprs = [];
 
     if (state.tokens.next.id !== ")") {
       for (;;) {
-        if (pn.value === "=>" && _.contains(["{", "["], state.tokens.next.value)) {
+        if (pn1.value === "=>" && _.contains(["{", "["], state.tokens.next.value)) {
           bracket = state.tokens.next;
           bracket.left = destructuringExpression();
           brackets.push(bracket);
@@ -2704,25 +2682,40 @@ var JSHINT = (function () {
     return id;
   }
 
-  function functionparams(fatarrow) {
-    var next;
+  function functionparams(parsed) {
+    var curr, next;
     var params = [];
     var ident;
     var tokens = [];
     var t;
     var pastDefault = false;
-    var loneArg = fatarrow && fatarrow.loneArg;
 
-    if (loneArg && loneArg.identifier === true) {
-      addlabel(loneArg.value, { type: "unused", token: loneArg });
-      return [loneArg];
+    if (parsed) {
+      if (Array.isArray(parsed)) {
+        for (var i in parsed) {
+          curr = parsed[i];
+          if (curr.value === "...") {
+            if (!state.option.esnext) {
+              warning("W119", curr, "spread/rest operator");
+            }
+            continue;
+          } else if (curr.value !== ",") {
+            params.push(curr.value);
+            addlabel(curr.value, { type: "unused", token: curr, param: true });
+          }
+        }
+        return params;
+      } else {
+        if (parsed.identifier === true) {
+          addlabel(parsed.value, { type: "unused", token: parsed, param: true });
+          return [parsed];
+        }
+      }
     }
 
     next = state.tokens.next;
 
-    if (!fatarrow || !fatarrow.parsedParen) {
-      advance("(");
-    }
+    advance("(");
 
     if (state.tokens.next.id === ")") {
       advance(")");
@@ -2848,18 +2841,7 @@ var JSHINT = (function () {
     };
   }
 
-  /**
-   * @param {Object} [fatarrow] In the case that the function being parsed
-   *                            takes the "fat arrow" form, this object will
-   *                            contain details about the in-progress parsing
-   *                            operation.
-   * @param {Token} [fatarrow.loneArg] The argument to the function in cases
-   *                                   where it was defined using the single-
-   *                                   argument shorthand.
-   * @param {bool} [fatarrow.parsedParen] Whether the opening parenthesis has
-   *                                      already been parsed.
-   */
-  function doFunction(name, statement, generator, fatarrow) {
+  function doFunction(name, statement, generator, fatarrowparams) {
     var f;
     var oldOption = state.option;
     var oldIgnored = state.ignored;
@@ -2884,20 +2866,19 @@ var JSHINT = (function () {
       addlabel(name, { type: "function" });
     }
 
-    funct["(params)"] = functionparams(fatarrow);
+    funct["(params)"] = functionparams(fatarrowparams);
     funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]);
 
-    if (fatarrow) {
-      if (!state.option.esnext) {
-        warning("W119", state.tokens.curr, "arrow function syntax (=>)");
-      }
+    // So we parse fat-arrow functions after we encounter =>. So basically
+    // doFunction is called with the left side of => as its last argument.
+    // This means that the parser, at that point, had already added its
+    // arguments to the undefs array and here we undo that.
 
-      if (!fatarrow.loneArg) {
-        advance("=>");
-      }
-    }
+    JSHINT.undefs = _.filter(JSHINT.undefs, function (item) {
+      return !_.contains(_.union(fatarrowparams), item[2]);
+    });
 
-    block(false, true, true, !!fatarrow);
+    block(false, true, true, fatarrowparams ? true : false);
 
     if (!state.option.noyield && generator &&
         funct["(generator)"] !== "yielded") {
diff --git a/src/vars.js b/src/vars.js
index ce63baf1..0d5b34e6 100644
--- a/src/vars.js
+++ b/src/vars.js
@@ -675,12 +675,10 @@ exports.mocha = {
   beforeEach  : false,
   afterEach   : false,
   // TDD
-  suite         : false,
-  test          : false,
-  setup         : false,
-  teardown      : false,
-  suiteSetup    : false,
-  suiteTeardown : false
+  suite       : false,
+  test        : false,
+  setup       : false,
+  teardown    : false
 };
 
 exports.jasmine = {
@@ -697,5 +695,11 @@ exports.jasmine = {
   // Jasmine 1.3
   runs        : false,
   waitsFor    : false,
-  waits       : false
+  waits       : false,
+  // Jasmine 2.1
+  beforeAll   : false,
+  afterAll    : false,
+  fail        : false,
+  fdescribe   : false,
+  fit         : false
 };


Test patch:
diff --git a/tests/unit/parser.js b/tests/unit/parser.js
index 7baa03ad..f455a70d 100644
--- a/tests/unit/parser.js
+++ b/tests/unit/parser.js
@@ -820,6 +820,8 @@ exports["destructuring var in function scope"] = function (test) {
     .addError(9,  "'a' is already defined.")
     .addError(9,  "'bar' is already defined.")
     .addError(10,  "Expected an identifier and instead saw '1'.")
+    .addError(10,  "Expected ',' and instead saw '1'.")
+    .addError(10,  "Expected an identifier and instead saw ']'.")
     .addError(11, "Expected ',' and instead saw ';'.")
     .addError(11, "'a' is already defined.")
     .addError(11, "'b' is already defined.")
@@ -994,6 +996,8 @@ exports["destructuring var errors"] = function (test) {
 
   TestRun(test)
     .addError(9,  "Expected an identifier and instead saw '1'.")
+    .addError(9,  "Expected ',' and instead saw '1'.")
+    .addError(9,  "Expected an identifier and instead saw ']'.")
     .addError(10, "Expected ',' and instead saw ';'.")
     .addError(11, "Expected ']' to match '[' from line 11 and instead saw ';'.")
     .addError(11, "Missing semicolon.")
@@ -1213,6 +1217,8 @@ exports["destructuring const errors"] = function (test) {
     .addError(2, "const 'b' has already been declared.")
     .addError(2, "const 'c' has already been declared.")
     .addError(3, "Expected an identifier and instead saw '1'.")
+    .addError(3, "Expected ',' and instead saw '1'.")
+    .addError(3, "Expected an identifier and instead saw ']'.")
     .addError(4, "Expected ',' and instead saw ';'.")
     .addError(5, "Expected ']' to match '[' from line 5 and instead saw ';'.")
     .addError(5, "Missing semicolon.")
@@ -3693,53 +3699,6 @@ exports["fat arrows support"] = function (test) {
   test.done();
 };
 
-exports["fat arrow nested function scoping"] = function (test) {
-  var code = [
-    "(() => {",
-    "  for (var i = 0; i < 10; i++) {",
-    "    var x;",
-    "  }",
-    "  var arrow = (x) => {",
-    "    return x;",
-    "  };",
-    "  var arrow2 = (x) => x;",
-    "  arrow();",
-    "  arrow2();",
-    "})();",
-    "(function() {",
-    "  for (var i = 0; i < 10; i++) {",
-    "    var x;",
-    "  }",
-    "  var arrow = (x) => {",
-    "    return x;",
-    "  };",
-    "  var arrow2 = (x) => x;",
-    "  arrow();",
-    "  arrow2();",
-    "})();"
-  ];
-
-  TestRun(test)
-    .test(code, {esnext: true});
-
-  test.done();
-};
-
-exports["default arguments in fat arrow functions"] = function (test) {
-  TestRun(test)
-    .test("(x = 0) => { return x; };", { expr: true, unused: true, esnext: true });
-
-  test.done();
-};
-
-exports["expressions in place of arrow function parameters"] = function (test) {
-  TestRun(test)
-    .addError(1, "Expected an identifier and instead saw '1'.")
-    .test("(1) => {};", { expr: true, esnext: true });
-
-  test.done();
-};
-
 var conciseMethods = exports.conciseMethods = {};
 
 conciseMethods.basicSupport = function (test) {