Repository: eslint/eslint
Pull request #14275 (2021-03-31 01:07:08)

Docs: Update issue triage to include blocked column
<!--
    Thank you for contributing!

    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

#### Prerequisites checklist

- [x] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/master/CONTRIBUTING.md).

#### What is the purpose of this pull request? (put an "X" next to an item)

[x] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

#### What changes did you make? (Give an overview)

Explains when to use the "Blocked" column in the triage project.

#### Is there anything you'd like reviewers to focus on?

Does this make sense?



Patch:
diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md
index cbfa30ae2..9b46d2944 100644
--- a/.github/ISSUE_TEMPLATE/BUG_REPORT.md
+++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md
@@ -2,7 +2,7 @@
 name: "\U0001F41E  Bug report"
 about: Report an issue with ESLint or rules bundled with ESLint
 title: ''
-labels: bug, triage
+labels: bug, repro:needed
 assignees: ''
 
 ---
@@ -35,6 +35,8 @@ assignees: ''
 
 **What parser (default, `@babel/eslint-parser`, `@typescript-eslint/parser`, etc.) are you using?**
 
+<!-- if you are using a parser other than ESLint's default, please try using the default parser to verify if the issue is parser-specific -->
+
 **Please show your full configuration:**
 
 <details>
@@ -74,4 +76,3 @@ assignees: ''
 1. 
 
 **Are you willing to submit a pull request to fix this bug?**
-
diff --git a/docs/developer-guide/nodejs-api.md b/docs/developer-guide/nodejs-api.md
index 708d1fa82..ce7271357 100644
--- a/docs/developer-guide/nodejs-api.md
+++ b/docs/developer-guide/nodejs-api.md
@@ -353,6 +353,8 @@ The `LintMessage` value is the information of each linting error. The `messages`
   The rule name that generates this lint message. If this message is generated by the ESLint core rather than rules, this is `null`.
 * `severity` (`1 | 2`)<br>
   The severity of this message. `1` means warning and `2` means error.
+* `fatal` (`boolean | undefined`)<br>
+  `true` if this is a fatal error unrelated to a rule, like a parsing error.
 * `message` (`string`)<br>
   The error message.
 * `line` (`number`)<br>
diff --git a/docs/developer-guide/working-with-rules.md b/docs/developer-guide/working-with-rules.md
index 95ff18ea6..2e699c088 100644
--- a/docs/developer-guide/working-with-rules.md
+++ b/docs/developer-guide/working-with-rules.md
@@ -375,7 +375,7 @@ context.report({
 {% endraw %}
 ```
 
-Note: Suggestions will be applied as a stand-alone change, without triggering multipass fixes. Each suggestion should focus on a singular change in the code and should not try to conform to user defined styles. For example, if a suggestion is adding a new statement into the codebase, it should not try to match correct indentation, or confirm to user preferences on presence/absence of semicolons. All of those things can be corrected by multipass autofix when the user triggers it.
+Note: Suggestions will be applied as a stand-alone change, without triggering multipass fixes. Each suggestion should focus on a singular change in the code and should not try to conform to user defined styles. For example, if a suggestion is adding a new statement into the codebase, it should not try to match correct indentation, or conform to user preferences on presence/absence of semicolons. All of those things can be corrected by multipass autofix when the user triggers it.
 
 Best practices for suggestions:
 
diff --git a/docs/maintainer-guide/issues.md b/docs/maintainer-guide/issues.md
index 2d4199bc8..6273016bc 100644
--- a/docs/maintainer-guide/issues.md
+++ b/docs/maintainer-guide/issues.md
@@ -30,6 +30,7 @@ All of ESLint's issues, across all GitHub repositories, are managed on our [Tria
 * **Evaluating** - the dev team is evaluating these issues to determine whether to move forward or not
 * **Waiting for RFC** - the next step in the process is for an RFC to be written
 * **RFC Opened** - an RFC is opened to address these issues
+* **Blocked** - the issue can't move forward due to some dependency
 * **Ready to Implement** - these issues have all of the details necessary to start implementation
 * **PR Opened** - there is an open pull request for each of these issues
 * **Completed** - the issue has been closed (either via pull request merge or by the team manually closing the issue)
@@ -77,6 +78,7 @@ When an issue has been moved to the "Ready for Dev Team" column, any dev team me
     * **Breaking Changes:** if you suspect or can verify that a change would be breaking, label it as "Breaking".
     * **Duplicates:** if you can verify the issue is a duplicate, add a comment mentioning the duplicate issue (such as, "Duplicate of #1234") and close the issue.
 1. Regardless of the above, always leave a comment. Don't just add labels, engage with the person who opened the issue by asking a question (request more information if necessary) or stating your opinion of the issue. If it's a verified bug, ask if the user would like to submit a pull request.
+1. If the issue can't be implemented because it needs an external dependency to be updated or needs to wait for another issue to be resolved, move the issue to the "Blocked" column.
 1. If the issue has been accepted and an RFC is required as the next step, move the issue to the "Waiting for RFC" column and comment on the issue that an RFC is needed.
 
 **Note:** "Good first issue" issues are intended to help new contributors feel welcome and empowered to make a contribution to ESLint. To ensure that new contributors are given a chance to work on these issues, issues labeled "good first issue" must be open for 30 days *from the day the issue was labeled* before a team member is permitted to work on them.
diff --git a/docs/rules/no-multi-assign.md b/docs/rules/no-multi-assign.md
index 41bba9ad6..7ef705675 100644
--- a/docs/rules/no-multi-assign.md
+++ b/docs/rules/no-multi-assign.md
@@ -43,6 +43,38 @@ let a = c;
 let b = c;
 ```
 
+## Options
+
+This rule has an object option:
+
+* `"ignoreNonDeclaration"`: When set to `true`, the rule allows chains that don't include initializing a variable in a declaration. Default is `false`.
+
+### ignoreNonDeclaration
+
+Examples of **correct** code for the `{ "ignoreNonDeclaration": true }` option:
+
+```js
+/*eslint no-multi-assign: ["error", { "ignoreNonDeclaration": true }]*/
+
+let a;
+let b;
+a = b = "baz";
+
+const x = {};
+const y = {};
+x.one = y.one = 1;
+```
+
+Examples of **incorrect** code for the `{ "ignoreNonDeclaration": true }` option:
+
+```js
+/*eslint no-multi-assign: ["error", { "ignoreNonDeclaration": true }]*/
+
+let a = b = "baz";
+
+const foo = bar = 1;
+```
+
 ## Related Rules
 
 * [max-statements-per-line](max-statements-per-line.md)
diff --git a/docs/rules/no-unused-vars.md b/docs/rules/no-unused-vars.md
index f730e6661..30647031e 100644
--- a/docs/rules/no-unused-vars.md
+++ b/docs/rules/no-unused-vars.md
@@ -214,8 +214,11 @@ Examples of **correct** code for the `{ "ignoreRestSiblings": true }` option:
 
 ```js
 /*eslint no-unused-vars: ["error", { "ignoreRestSiblings": true }]*/
-// 'type' is ignored because it has a rest property sibling.
-var { type, ...coords } = data;
+// 'foo' and 'bar' were ignored because they have a rest property sibling.
+var { foo, ...coords } = data;
+
+var bar;
+({ bar, ...coords } = data);
 ```
 
 ### argsIgnorePattern
diff --git a/lib/rules/no-multi-assign.js b/lib/rules/no-multi-assign.js
index ab6430c19..d2606a150 100644
--- a/lib/rules/no-multi-assign.js
+++ b/lib/rules/no-multi-assign.js
@@ -21,7 +21,16 @@ module.exports = {
             url: "https://eslint.org/docs/rules/no-multi-assign"
         },
 
-        schema: [],
+        schema: [{
+            type: "object",
+            properties: {
+                ignoreNonDeclaration: {
+                    type: "boolean",
+                    default: false
+                }
+            },
+            additionalProperties: false
+        }],
 
         messages: {
             unexpectedChain: "Unexpected chained assignment."
@@ -33,10 +42,14 @@ module.exports = {
         //--------------------------------------------------------------------------
         // Public
         //--------------------------------------------------------------------------
+        const options = context.options[0] || {
+            ignoreNonDeclaration: false
+        };
+        const targetParent = options.ignoreNonDeclaration ? ["VariableDeclarator"] : ["AssignmentExpression", "VariableDeclarator"];
 
         return {
             AssignmentExpression(node) {
-                if (["AssignmentExpression", "VariableDeclarator"].indexOf(node.parent.type) !== -1) {
+                if (targetParent.indexOf(node.parent.type) !== -1) {
                     context.report({
                         node,
                         messageId: "unexpectedChain"
diff --git a/lib/rules/no-unused-vars.js b/lib/rules/no-unused-vars.js
index 4dc6dc2ba..32589099c 100644
--- a/lib/rules/no-unused-vars.js
+++ b/lib/rules/no-unused-vars.js
@@ -196,6 +196,17 @@ module.exports = {
 
         }
 
+        /**
+         * Checks whether a node is a sibling of the rest property or not.
+         * @param {ASTNode} node a node to check
+         * @returns {boolean} True if the node is a sibling of the rest property, otherwise false.
+         */
+        function hasRestSibling(node) {
+            return node.type === "Property" &&
+                node.parent.type === "ObjectPattern" &&
+                REST_PROPERTY_TYPE.test(node.parent.properties[node.parent.properties.length - 1].type);
+        }
+
         /**
          * Determines if a variable has a sibling rest property
          * @param {Variable} variable eslint-scope variable object.
@@ -204,16 +215,10 @@ module.exports = {
          */
         function hasRestSpreadSibling(variable) {
             if (config.ignoreRestSiblings) {
-                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)
-                    );
-                });
+                const hasRestSiblingDefinition = variable.defs.some(def => hasRestSibling(def.name.parent));
+                const hasRestSiblingReference = variable.references.some(ref => hasRestSibling(ref.identifier.parent));
+
+                return hasRestSiblingDefinition || hasRestSiblingReference;
             }
 
             return false;
diff --git a/package.json b/package.json
index ebbc13d59..8f1308965 100644
--- a/package.json
+++ b/package.json
@@ -118,8 +118,8 @@
     "markdownlint": "^0.19.0",
     "markdownlint-cli": "^0.22.0",
     "memfs": "^3.0.1",
-    "mocha": "^7.1.1",
-    "mocha-junit-reporter": "^1.23.0",
+    "mocha": "^8.3.2",
+    "mocha-junit-reporter": "^2.0.0",
     "node-polyfill-webpack-plugin": "^1.0.3",
     "npm-license": "^0.3.3",
     "nyc": "^15.0.1",


Test patch:
diff --git a/tests/lib/rules/no-multi-assign.js b/tests/lib/rules/no-multi-assign.js
index dc907465c..c534f55b3 100644
--- a/tests/lib/rules/no-multi-assign.js
+++ b/tests/lib/rules/no-multi-assign.js
@@ -51,7 +51,9 @@ ruleTester.run("no-mutli-assign", rule, {
         { code: "for(let a = 0, b = 0;;){}", parserOptions: { ecmaVersion: 6 } },
         { code: "for(const a = 0, b = 0;;){}", parserOptions: { ecmaVersion: 6 } },
         { code: "export let a, b;", parserOptions: { ecmaVersion: 6, sourceType: "module" } },
-        { code: "export let a,\n b = 0;", parserOptions: { ecmaVersion: 6, sourceType: "module" } }
+        { code: "export let a,\n b = 0;", parserOptions: { ecmaVersion: 6, sourceType: "module" } },
+        { code: "const x = {};const y = {};x.one = y.one = 1;", options: [{ ignoreNonDeclaration: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "let a, b;a = b = 1", options: [{ ignoreNonDeclaration: true }], parserOptions: { ecmaVersion: 6 } }
     ],
 
     invalid: [
@@ -137,6 +139,39 @@ ruleTester.run("no-mutli-assign", rule, {
             errors: [
                 errorAt(1, 5, "AssignmentExpression")
             ]
+        },
+        {
+            code: "const x = {};\nconst y = x.one = 1;",
+            options: [{ ignoreNonDeclaration: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                errorAt(2, 11, "AssignmentExpression")
+            ]
+
+        },
+        {
+            code: "let a, b;a = b = 1",
+            options: [{}],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                errorAt(1, 14, "AssignmentExpression")
+            ]
+        },
+        {
+            code: "let x, y;x = y = 'baz'",
+            options: [{ ignoreNonDeclaration: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                errorAt(1, 14, "AssignmentExpression")
+            ]
+        },
+        {
+            code: "const a = b = 1",
+            options: [{ ignoreNonDeclaration: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                errorAt(1, 11, "AssignmentExpression")
+            ]
         }
     ]
 });
diff --git a/tests/lib/rules/no-unused-vars.js b/tests/lib/rules/no-unused-vars.js
index 061ac08d5..db041d46b 100644
--- a/tests/lib/rules/no-unused-vars.js
+++ b/tests/lib/rules/no-unused-vars.js
@@ -291,6 +291,13 @@ ruleTester.run("no-unused-vars", rule, {
             parserOptions: { ecmaVersion: 2018 }
         },
 
+        // https://github.com/eslint/eslint/issues/14163
+        {
+            code: "let foo, rest;\n({ foo, ...rest } = something);\nconsole.log(rest);",
+            options: [{ ignoreRestSiblings: true }],
+            parserOptions: { ecmaVersion: 2020 }
+        },
+
         // https://github.com/eslint/eslint/issues/10952
         "/*eslint use-every-a:1*/ !function(b, a) { return 1 }",
 
@@ -588,6 +595,23 @@ ruleTester.run("no-unused-vars", rule, {
                 }
             ]
         },
+        {
+            code: "let type, coords;\n({ type, ...coords } = data);\n console.log(type)",
+            options: [{ ignoreRestSiblings: true }],
+            parserOptions: { ecmaVersion: 2018 },
+            errors: [
+                {
+                    line: 2,
+                    column: 13,
+                    messageId: "unusedVar",
+                    data: {
+                        varName: "coords",
+                        action: "assigned a value",
+                        additional: ""
+                    }
+                }
+            ]
+        },
 
         // Unused rest property without ignoreRestSiblings
         {