Repository: eslint/eslint (js)

Issue:
no-unused-vars ignoreRestSiblings flag doesn't work inside function arguments
* **ESLint 3.15.0**
* **Node 7.4.0**
* **npm 4.0.5**

**babel-eslint parser**

**Full configuration:**

```
  {
  "env": {
    "browser": true,
  },
  "globals": {
    "require": false,
    "process": false,
    "ga": false,
    "Promise": false,
    "__SSR__": false,
  },
  "parser": "babel-eslint",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true,
    }
  },
  "plugins": [
    "react",
    "import",
  ],
  "settings": {
    "import/resolver": {
      "webpack": { "config": "webpack-config/dev.js" },
    },
  },
  "rules": {
    "eol-last": ["warn", "always"],
    "indent": ["warn", 2, { "SwitchCase": 1 }],
    "linebreak-style": ["warn", "unix"],
    "default-case": ["warn"],
    "no-console": ["warn"],
    "no-alert": ["warn"],
    "no-debugger": ["warn"],
    "no-dupe-args": ["warn"],
    "no-dupe-keys": ["warn"],
    "no-duplicate-case": ["warn"],
    "no-empty": ["warn"],
    "no-extra-boolean-cast": ["warn"],
    "no-extra-parens": ["warn", "functions"],
    "no-extra-semi": ["warn"],
    "no-irregular-whitespace": ["warn"],
    "no-multiple-empty-lines": ["warn", { "max": 2, "maxEOF": 1 }],
    "no-sparse-arrays": ["warn"],
    "no-undef": ["warn"],
    "no-unexpected-multiline": ["warn"],
    "no-unreachable": ["warn"],
    "no-unused-vars": ["warn", { "vars": "all", "args": "after-used", "ignoreRestSiblings": true }],
    "semi": ["warn", "always"],

    "comma-dangle": ["warn", {
      "arrays": "always-multiline",
      "objects": "always-multiline",
      "imports": "always-multiline",
      "exports": "always-multiline",
      "functions": "always-multiline",
    }],

    "react/no-unused-prop-types": "warn",
    "react/prop-types": "warn",

    "react/jsx-uses-react": "error",
    "react/jsx-uses-vars": "error",
    "react/jsx-space-before-closing": "warn",
    "react/jsx-no-target-blank": "warn",
    "react/jsx-no-undef": "warn",

    "import/no-unresolved": "warn",
    "import/named": "warn",
    "import/default": "warn",
    "import/no-commonjs": "warn",
    "import/no-nodejs-modules": "warn",

  },
}
```


![eslint](https://cloud.githubusercontent.com/assets/9456433/23164328/63752d16-f858-11e6-9ffe-5ac1b75f3379.png)

`"no-unused-vars": ["warn", { "vars": "all", "args": "after-used", "ignoreRestSiblings": true }],` should disable `no-unused-vars` warn, but it doesn't


Pull request description:
Fix: `ignoreRestSiblings` option didn't cover arguments (fixes #8119)
**What is the purpose of this pull request? (put an "X" next to item)**

[X] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))

Fixes #8119 .

**What changes did you make? (Give an overview)**

This PR makes `ignoreRestSiblings` option of `no-unused-vars` rule covering arguments.

**Is there anything you'd like reviewers to focus on?**

Nothing in particular.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7925ec4c6..2352177f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,12 @@
+v3.16.1 - February 22, 2017
+
+* ff8a80c Fix: duplicated autofix output for inverted fix ranges (fixes #8116) (#8117) (Teddy Katz)
+* a421897 Docs: fix typo in arrow-parens.md (#8132) (Will Chen)
+* 22d7fbf Chore: fix invalid redeclared variables in tests (#8130) (Teddy Katz)
+* 8d95598 Chore: fix output assertion typos in rule tests (#8129) (Teddy Katz)
+* 9fa2559 Docs: Add missing quotes in key-spacing rule (#8121) (Glenn Reyes)
+* f3a6ced Build: package.json update for eslint-config-eslint release (ESLint Jenkins)
+
 v3.16.0 - February 20, 2017
 
 * d89d0b4 Update: fix quotes false negative for string literals as template tags (#8107) (Teddy Katz)
diff --git a/docs/rules/arrow-parens.md b/docs/rules/arrow-parens.md
index 075de1f0b..36e804481 100644
--- a/docs/rules/arrow-parens.md
+++ b/docs/rules/arrow-parens.md
@@ -58,7 +58,7 @@ String options are:
 
 Object properties for variants of the `"as-needed"` option:
 
-* `"requireForBlockBody": true` modifies the as-needed rule in order to require parens if the function body is in an intructions block (surrounded by braces).
+* `"requireForBlockBody": true` modifies the as-needed rule in order to require parens if the function body is in an instructions block (surrounded by braces).
 
 ### always
 
diff --git a/docs/rules/key-spacing.md b/docs/rules/key-spacing.md
index feb053375..e9284da3b 100644
--- a/docs/rules/key-spacing.md
+++ b/docs/rules/key-spacing.md
@@ -16,8 +16,8 @@ This rule has an object option:
 * `"beforeColon": true` requires at least one space between the key and the colon in object literals.
 * `"afterColon": true` (default) requires at least one space between the colon and the value in object literals.
 * `"afterColon": false` disallows spaces between the colon and the value in object literals.
-* `"mode": strict` (default) enforces exactly one space before or after colons in object literals.
-* `"mode": minimum` enforces one or more spaces before or after colons in object literals.
+* `"mode": "strict"` (default) enforces exactly one space before or after colons in object literals.
+* `"mode": "minimum"` enforces one or more spaces before or after colons in object literals.
 * `"align": "value"` enforces horizontal alignment of values in object literals.
 * `"align": "colon"` enforces horizontal alignment of both colons and values in object literals.
 * `"align"` with an object value allows for fine-grained spacing when values are being aligned in object literals.
diff --git a/lib/rules/no-unused-vars.js b/lib/rules/no-unused-vars.js
index 5dd632fdb..f9cf2da31 100644
--- a/lib/rules/no-unused-vars.js
+++ b/lib/rules/no-unused-vars.js
@@ -66,6 +66,7 @@ module.exports = {
 
         const DEFINED_MESSAGE = "'{{name}}' is defined but never used.";
         const ASSIGNED_MESSAGE = "'{{name}}' is assigned a value but never used.";
+        const REST_PROPERTY_TYPE = /^(?:Experimental)?RestProperty$/;
 
         const config = {
             vars: "all",
@@ -139,17 +140,16 @@ module.exports = {
          */
         function hasRestSpreadSibling(variable) {
             if (config.ignoreRestSiblings) {
-                const restProperties = new Set(["ExperimentalRestProperty", "RestProperty"]);
-
-                return variable.defs
-                    .filter(def => def.name.type === "Identifier")
-                    .some(def => (
-                        def.node.id &&
-                        def.node.id.type === "ObjectPattern" &&
-                        def.node.id.properties.length &&
-                        restProperties.has(def.node.id.properties[def.node.id.properties.length - 1].type) &&  // last property is a rest property
-                        !restProperties.has(def.name.parent.type)  // variable is sibling of the rest property
-                    ));
+                return variable.defs.some(def => {
+                    const propertyNode = def.name.parent;
+                    const patternNode = propertyNode.parent;
+
+                    return (
+                        propertyNode.type === "Property" &&
+                        patternNode.type === "ObjectPattern" &&
+                        REST_PROPERTY_TYPE.test(patternNode.properties[patternNode.properties.length - 1].type)
+                    );
+                });
             }
 
             return false;
diff --git a/lib/rules/padded-blocks.js b/lib/rules/padded-blocks.js
index 094ba0a71..27657360e 100644
--- a/lib/rules/padded-blocks.js
+++ b/lib/rules/padded-blocks.js
@@ -188,6 +188,9 @@ module.exports = {
                         node,
                         loc: { line: openBrace.loc.start.line, column: openBrace.loc.start.column },
                         fix(fixer) {
+
+                            // FIXME: The start of this range is sometimes larger than the end.
+                            // https://github.com/eslint/eslint/issues/8116
                             return fixer.replaceTextRange([openBrace.end, nextToken.start - nextToken.loc.start.column], "\n");
                         },
                         message: NEVER_MESSAGE
diff --git a/lib/util/source-code-fixer.js b/lib/util/source-code-fixer.js
index 8b8936204..ab42e9529 100644
--- a/lib/util/source-code-fixer.js
+++ b/lib/util/source-code-fixer.js
@@ -108,7 +108,13 @@ SourceCodeFixer.applyFixes = function(sourceCode, messages) {
             // Make output to this fix.
             output += text.slice(Math.max(0, lastPos), Math.max(0, start));
             output += fix.text;
-            lastPos = end;
+
+            /*
+             * If the start of the range is larger than the end for some reason, make sure
+             * the text between the end and the start doesn't get duplicated.
+             * https://github.com/eslint/eslint/issues/8116
+             */
+            lastPos = Math.max(start, end);
         }
         output += text.slice(Math.max(0, lastPos));
 
diff --git a/package.json b/package.json
index 531cc4fff..cb4fbb502 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint",
-  "version": "3.16.0",
+  "version": "3.16.1",
   "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
   "description": "An AST-based pattern checker for JavaScript.",
   "bin": {


Test patch:
diff --git a/tests/lib/rules/comma-dangle.js b/tests/lib/rules/comma-dangle.js
index d73132ffd..5e9adb33f 100644
--- a/tests/lib/rules/comma-dangle.js
+++ b/tests/lib/rules/comma-dangle.js
@@ -760,7 +760,7 @@ ruleTester.run("comma-dangle", rule, {
         },
         {
             code: "var foo = { bar: 'baz', }",
-            ouput: "var foo = { bar: 'baz' }",
+            output: "var foo = { bar: 'baz' }",
             options: ["only-multiline"],
             errors: [
                 {
@@ -799,7 +799,7 @@ ruleTester.run("comma-dangle", rule, {
         },
         {
             code: "foo({ bar: 'baz', qux: 'quux', });",
-            ouput: "foo({ bar: 'baz', qux: 'quux' });",
+            output: "foo({ bar: 'baz', qux: 'quux' });",
             options: ["only-multiline"],
             errors: [
                 {
diff --git a/tests/lib/rules/computed-property-spacing.js b/tests/lib/rules/computed-property-spacing.js
index eb0fca8cf..98f34b376 100644
--- a/tests/lib/rules/computed-property-spacing.js
+++ b/tests/lib/rules/computed-property-spacing.js
@@ -156,7 +156,7 @@ ruleTester.run("computed-property-spacing", rule, {
         },
         {
             code: "obj[ foo ]",
-            outptu: "obj[foo]",
+            output: "obj[foo]",
             options: ["never"],
             errors: [
                 {
diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js
index 11c8e3dc6..93378ae1b 100644
--- a/tests/lib/rules/indent.js
+++ b/tests/lib/rules/indent.js
@@ -900,16 +900,16 @@ ruleTester.run("indent", rule, {
             "        a: 1,\n" +
             "        b: 2\n" +
             "      };\n" +
-            "let abc = 5,\n" +
-            "  c = 2,\n" +
-            "  xyz = \n" +
+            "let abc2 = 5,\n" +
+            "  c2 = 2,\n" +
+            "  xyz2 = \n" +
             "  {\n" +
             "    a: 1,\n" +
             "    b: 2\n" +
             "  };\n" +
-            "var abc = 5,\n" +
-            "    c = 2,\n" +
-            "    xyz = \n" +
+            "var abc3 = 5,\n" +
+            "    c3 = 2,\n" +
+            "    xyz3 = \n" +
             "    {\n" +
             "      a: 1,\n" +
             "      b: 2\n" +
@@ -2806,19 +2806,19 @@ ruleTester.run("indent", rule, {
             "      a: 1\n" +
             "    }),\n" +
             "    b = 4;\n" +
-            "const a = new Test({\n" +
+            "const c = new Test({\n" +
             "      a: 1\n" +
             "    }),\n" +
-            "    b = 4;\n",
+            "    d = 4;\n",
             output:
             "var a = new Test({\n" +
             "      a: 1\n" +
             "    }),\n" +
             "    b = 4;\n" +
-            "const a = new Test({\n" +
+            "const c = new Test({\n" +
             "    a: 1\n" +
             "  }),\n" +
-            "  b = 4;\n",
+            "  d = 4;\n",
             options: [2, { VariableDeclarator: { var: 2 } }],
             parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([
diff --git a/tests/lib/rules/newline-after-var.js b/tests/lib/rules/newline-after-var.js
index 7763e856d..853cfe880 100644
--- a/tests/lib/rules/newline-after-var.js
+++ b/tests/lib/rules/newline-after-var.js
@@ -285,7 +285,7 @@ ruleTester.run("newline-after-var", rule, {
         { code: TWO_BLANKS, output: NO_BLANK, options: ["never"], errors: [NEVER_ERROR] },
         { code: THREE_BLANKS, output: NO_BLANK, options: ["never"], errors: [NEVER_ERROR] },
         { code: ONE_BLANK_WITH_TRAILING_WS, output: NO_BLANK_WITH_TRAILING_WS, options: ["never"], errors: [NEVER_ERROR] },
-        { code: ONE_BLANK_WITH_INLINE_COMMENT, NO_BLANK_WITH_INLINE_COMMENT, options: ["never"], errors: [NEVER_ERROR] },
+        { code: ONE_BLANK_WITH_INLINE_COMMENT, output: NO_BLANK_WITH_INLINE_COMMENT, options: ["never"], errors: [NEVER_ERROR] },
         { code: MULTI_VAR_ONE_BLANK, output: MULTI_VAR_NO_BLANK, options: ["never"], errors: [NEVER_ERROR] },
         { code: MULTI_DEC_ONE_BLANK, output: MULTI_DEC_NO_BLANK, options: ["never"], errors: [NEVER_ERROR] },
         { code: MULTI_LINE_ONE_BLANK, output: MULTI_LINE_NO_BLANK, options: ["never"], errors: [NEVER_ERROR] },
diff --git a/tests/lib/rules/no-redeclare.js b/tests/lib/rules/no-redeclare.js
index 3babc8397..72ab03ae6 100644
--- a/tests/lib/rules/no-redeclare.js
+++ b/tests/lib/rules/no-redeclare.js
@@ -39,7 +39,7 @@ ruleTester.run("no-redeclare", rule, {
     ],
     invalid: [
         { code: "var a = 3; var a = 10;", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "'a' is already defined.", type: "Identifier" }] },
-        { code: "switch(foo) { case a: let b = 3;\ncase b: let b = 4}", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "'b' is already defined.", type: "Identifier" }] },
+        { code: "switch(foo) { case a: var b = 3;\ncase b: var b = 4}", errors: [{ message: "'b' is already defined.", type: "Identifier" }] },
         { code: "var a = 3; var a = 10;", errors: [{ message: "'a' is already defined.", type: "Identifier" }] },
         { code: "var a = {}; var a = [];", errors: [{ message: "'a' is already defined.", type: "Identifier" }] },
         { code: "var a; function a() {}", errors: [{ message: "'a' is already defined.", type: "Identifier" }] },
diff --git a/tests/lib/rules/no-shadow.js b/tests/lib/rules/no-shadow.js
index fbc6d781d..c7bdb9bb4 100644
--- a/tests/lib/rules/no-shadow.js
+++ b/tests/lib/rules/no-shadow.js
@@ -27,7 +27,7 @@ ruleTester.run("no-shadow", rule, {
         { code: "class A {}", parserOptions: { ecmaVersion: 6 } },
         { code: "class A { constructor() { var a; } }", parserOptions: { ecmaVersion: 6 } },
         { code: "(function() { var A = class A {}; })()", parserOptions: { ecmaVersion: 6 } },
-        { code: "{ var a; } let a;", parserOptions: { ecmaVersion: 6 } }, // this case reports `no-redeclare`, not shadowing.
+        { code: "{ var a; } var a;", parserOptions: { ecmaVersion: 6 } }, // this case reports `no-redeclare`, not shadowing.
         { code: "{ let a; } let a;", options: [{ hoist: "never" }], parserOptions: { ecmaVersion: 6 } },
         { code: "{ let a; } var a;", options: [{ hoist: "never" }], parserOptions: { ecmaVersion: 6 } },
         { code: "{ let a; } function a() {}", options: [{ hoist: "never" }], parserOptions: { ecmaVersion: 6 } },
diff --git a/tests/lib/rules/no-unused-vars.js b/tests/lib/rules/no-unused-vars.js
index 8ab2208f4..a966ebb10 100644
--- a/tests/lib/rules/no-unused-vars.js
+++ b/tests/lib/rules/no-unused-vars.js
@@ -287,7 +287,13 @@ ruleTester.run("no-unused-vars", rule, {
         {
             code: "class Foo { set bar(UNUSED) {} } console.log(Foo)",
             parserOptions: { ecmaVersion: 6 }
-        }
+        },
+
+        // https://github.com/eslint/eslint/issues/8119
+        includeRestPropertyParser({
+            code: "(({a, ...rest}) => rest)",
+            options: [{ args: "all", ignoreRestSiblings: true }]
+        })
     ],
     invalid: [
         { code: "function foox() { return foox(); }", errors: [definedError("foox")] },
@@ -396,6 +402,13 @@ ruleTester.run("no-unused-vars", rule, {
             ]
         }),
 
+        // https://github.com/eslint/eslint/issues/8119
+        includeRestPropertyParser({
+            code: "(({a, ...rest}) => {})",
+            options: [{ args: "all", ignoreRestSiblings: true }],
+            errors: ["'rest' is defined but never used."]
+        }),
+
         // https://github.com/eslint/eslint/issues/3714
         {
             code: "/* global a$fooz,$foo */\na$fooz;",
diff --git a/tests/lib/rules/padded-blocks.js b/tests/lib/rules/padded-blocks.js
index e38c7109c..561ec3afa 100644
--- a/tests/lib/rules/padded-blocks.js
+++ b/tests/lib/rules/padded-blocks.js
@@ -463,6 +463,12 @@ ruleTester.run("padded-blocks", rule, {
                     line: 9
                 }
             ]
+        },
+        {
+            code: "function foo() { // a\n\n  b;\n}",
+            output: "function foo() {\n // a\n\n  b;\n}",
+            options: ["never"],
+            errors: [NEVER_MESSAGE]
         }
     ]
 });
diff --git a/tests/lib/rules/prefer-const.js b/tests/lib/rules/prefer-const.js
index c38828ae3..495427667 100644
--- a/tests/lib/rules/prefer-const.js
+++ b/tests/lib/rules/prefer-const.js
@@ -92,13 +92,7 @@ ruleTester.run("prefer-const", rule, {
         {
             code: "let x; function foo() { bar(x); } x = 0;",
             options: [{ ignoreReadBeforeAssign: true }]
-        },
-
-        // https://github.com/eslint/eslint/issues/7712
-        // https://github.com/ternjs/acorn/issues/487
-        // This should be a SyntaxError, but espree parses it correctly. Don't throw an error if the variable has multiple declarations.
-        "let foo; const foo = 1;"
-
+        }
     ],
     invalid: [
         {
diff --git a/tests/lib/rules/sort-imports.js b/tests/lib/rules/sort-imports.js
index 92f6f64eb..4abe7a906 100644
--- a/tests/lib/rules/sort-imports.js
+++ b/tests/lib/rules/sort-imports.js
@@ -57,7 +57,7 @@ ruleTester.run("sort-imports", rule, {
         {
             code:
                 "import {a, b} from 'bar.js';\n" +
-                "import {b, c} from 'foo.js';"
+                "import {c, d} from 'foo.js';"
         },
         {
             code:
@@ -72,7 +72,7 @@ ruleTester.run("sort-imports", rule, {
         {
             code:
                 "import a, * as b from 'foo.js';\n" +
-                "import b from 'bar.js';"
+                "import c from 'bar.js';"
         },
         {
             code:
@@ -147,7 +147,7 @@ ruleTester.run("sort-imports", rule, {
         {
             code:
                 "import {b, c} from 'foo.js';\n" +
-                "import {a, b} from 'bar.js';",
+                "import {a, d} from 'bar.js';",
             errors: [expectedError]
         },
         {
diff --git a/tests/lib/util/source-code-fixer.js b/tests/lib/util/source-code-fixer.js
index dad6ece64..cb599c7b2 100644
--- a/tests/lib/util/source-code-fixer.js
+++ b/tests/lib/util/source-code-fixer.js
@@ -122,6 +122,13 @@ const INSERT_AT_END = {
         message: "nofix2",
         line: 1,
         column: 7
+    },
+    REVERSED_RANGE = {
+        message: "reversed range",
+        fix: {
+            range: [3, 0],
+            text: " "
+        }
     };
 
 //------------------------------------------------------------------------------
@@ -407,6 +414,12 @@ describe("SourceCodeFixer", () => {
                 assert.equal(result.messages.length, 0);
             });
 
+            it("should handle reversed ranges gracefully", () => {
+                const result = SourceCodeFixer.applyFixes(sourceCode, [REVERSED_RANGE]);
+
+                assert.equal(result.output, "\uFEFFvar  answer = 6 * 7;");
+            });
+
         });
 
         describe("Text Replacement", () => {