Repository: eslint/eslint
Pull request #15649 (2022-02-26 10:09:46)

feat: add `destructuredArrayIgnorePattern` option in `no-unused-vars`
<!--
    Thank you for contributing!

    ESLint adheres to the [JS Foundation Code of Conduct](https://eslint.org/conduct).
-->

#### Prerequisites checklist

- [x] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/HEAD/CONTRIBUTING.md).

#### What is the purpose of this pull request? (put an "X" next to an item)

<!--
    The following template is intentionally not a markdown checkbox list for the reasons
    explained in https://github.com/eslint/eslint/pull/12848#issuecomment-580302888
-->

[x] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-proposal.md))
[x] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-change-proposal.md))
[ ] Add autofix 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)

Fix https://github.com/eslint/eslint/issues/15611

Added a new option `destructuredArrayIgnorePattern` to the `no-unused-vars` rule.

```js
// With the following option set within eslintrc rules
// "no-unused-vars": ["error", { "destructuredArrayIgnorePattern": "^_" }],

const array = ['a', 'b', 'c'];

const [a, _b, c] = array;  // No "no-unused-vars" error on _b

const newArray = [a, c];

```


#### Is there anything you'd like reviewers to focus on?

No
<!-- markdownlint-disable-file MD004 -->


eslint-github-bot[bot]: Hi @snitin315!, thanks for the Pull Request

The **pull request title** isn't properly formatted. We ask that you update the message to match this format, as we use it to generate changelogs and automate releases.

- The length of the commit message must be less than or equal to 72

**To Fix:** You can fix this problem by clicking 'Edit' next to the pull request title at the top of this page.

Read more about contributing to ESLint [here](https://eslint.org/docs/developer-guide/contributing/)


Patch:
diff --git a/README.md b/README.md
index de20af780..e224151d6 100644
--- a/README.md
+++ b/README.md
@@ -292,9 +292,9 @@ The following companies, organizations, and individuals support ESLint's ongoing
 <!--sponsorsstart-->
 <h3>Platinum Sponsors</h3>
 <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
-<p><a href="https://contra.com"><img src="https://images.opencollective.com/contra1/c70f93f/logo.png" alt="Contra" height="96"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://google.com/chrome"><img src="https://images.opencollective.com/chrome/dc55bd4/logo.png" alt="Chrome's Web Framework & Tools Performance Fund" height="96"></a> <a href="https://www.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a></p><h3>Silver Sponsors</h3>
+<p><a href="https://contra.com"><img src="https://images.opencollective.com/contra1/c70f93f/logo.png" alt="Contra" height="96"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://www.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a></p><h3>Silver Sponsors</h3>
 <p><a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a></p><h3>Bronze Sponsors</h3>
-<p><a href="https://launchdarkly.com"><img src="https://images.opencollective.com/launchdarkly/574bb9e/logo.png" alt="launchdarkly" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Practice Ignition" height="32"></a></p>
+<p><a href="https://launchdarkly.com"><img src="https://images.opencollective.com/launchdarkly/574bb9e/logo.png" alt="launchdarkly" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a></p>
 <!--sponsorsend-->
 
 ## <a name="technology-sponsors"></a>Technology Sponsors
diff --git a/docs/rules/computed-property-spacing.md b/docs/rules/computed-property-spacing.md
index 9f60ab804..beae78394 100644
--- a/docs/rules/computed-property-spacing.md
+++ b/docs/rules/computed-property-spacing.md
@@ -16,6 +16,10 @@ var a = "prop";
 var obj = {
   [a]: "value" // computed property key in object literal (ECMAScript 6)
 };
+
+var obj = { prop: "value" };
+var a = "prop";
+var { [a]: x } = obj; // computed property key in object destructuring pattern (ECMAScript 6)
 ```
 
 ## Rule Details
diff --git a/docs/rules/no-constant-condition.md b/docs/rules/no-constant-condition.md
index 3ecf499ea..810c17bea 100644
--- a/docs/rules/no-constant-condition.md
+++ b/docs/rules/no-constant-condition.md
@@ -42,6 +42,14 @@ if (new Boolean(x)) {
     doSomethingAlways();
 }
 
+if (Boolean(1)) {
+    doSomethingAlways();
+}
+
+if (undefined) {
+    doSomethingUnfinished();
+}
+
 if (x ||= true) {
     doSomethingAlways();
 }
diff --git a/docs/rules/no-unused-vars.md b/docs/rules/no-unused-vars.md
index 09761271b..ddf74823e 100644
--- a/docs/rules/no-unused-vars.md
+++ b/docs/rules/no-unused-vars.md
@@ -238,6 +238,43 @@ function foo(x, _y) {
 foo();
 ```
 
+### destructuredArrayIgnorePattern
+
+The `destructuredArrayIgnorePattern` option specifies exceptions not to check for usage: elements of array destructuring patterns whose names match a regexp pattern. For example, variables whose names begin with an underscore.
+
+Examples of **correct** code for the `{ "destructuredArrayIgnorePattern": "^_" }` option:
+
+```js
+/*eslint no-unused-vars: ["error", { "destructuredArrayIgnorePattern": "^_" }]*/
+
+const [a, _b, c] = ["a", "b", "c"];
+console.log(a+c);
+
+const { x: [_a, foo] } = bar;
+console.log(foo);
+
+function baz([_c, x]) {
+    x;
+}
+baz();
+
+function test({p: [_q, r]}) {
+    r;
+}
+test();
+
+let _m, n;
+foo.forEach(item => {
+    [_m, n] = item;
+    console.log(n);
+});
+
+let _o, p;
+_o = 1;
+[_o, p] = foo;
+p;
+```
+
 ### caughtErrors
 
 The `caughtErrors` option is used for `catch` block arguments validation.
diff --git a/docs/rules/sort-imports.md b/docs/rules/sort-imports.md
index 087a008bb..5ba4509a2 100644
--- a/docs/rules/sort-imports.md
+++ b/docs/rules/sort-imports.md
@@ -7,6 +7,7 @@ The import statement is used to import members (functions, objects or primitives
 ```js
 // single - Import single member.
 import myMember from "my-module.js";
+import {myOtherMember} from "my-other-module.js";
 
 // multiple - Import multiple members.
 import {foo, bar} from "my-module.js";
@@ -72,7 +73,7 @@ import * as foo from 'foo.js';
 import {alpha, beta} from 'alpha.js';
 import {delta, gamma} from 'delta.js';
 import a from 'baz.js';
-import b from 'qux.js';
+import {b} from 'qux.js';
 
 /*eslint sort-imports: "error"*/
 import a from 'foo.js';
@@ -84,6 +85,7 @@ import 'foo.js'
 import * as bar from 'bar.js';
 import {a, b} from 'baz.js';
 import c from 'qux.js';
+import {d} from 'quux.js';
 
 /*eslint sort-imports: "error"*/
 import {a, b, c} from 'foo.js'
@@ -108,6 +110,10 @@ import {a, b} from 'bar.js';
 import a from 'foo.js';
 import {b, c} from 'bar.js';
 
+/*eslint sort-imports: "error"*/
+import {a} from 'foo.js';
+import {b, c} from 'bar.js';
+
 /*eslint sort-imports: "error"*/
 import a from 'foo.js';
 import * as b from 'bar.js';
diff --git a/docs/user-guide/configuring/configuration-files.md b/docs/user-guide/configuring/configuration-files.md
index cc1d5bf3c..d3368e767 100644
--- a/docs/user-guide/configuring/configuration-files.md
+++ b/docs/user-guide/configuring/configuration-files.md
@@ -358,6 +358,8 @@ module.exports = {
 
 <b>v4.1.0+.</b> Sometimes a more fine-controlled configuration is necessary, for example, if the configuration for files within the same directory has to be different. Therefore you can provide configurations under the `overrides` key that will only apply to files that match specific glob patterns, using the same format you would pass on the command line (e.g., `app/**/*.test.js`).
 
+Glob patterns in overrides use [minimatch syntax](https://github.com/isaacs/minimatch).
+
 ### How do overrides work?
 
 It is possible to override settings based on file glob patterns in your configuration by using the `overrides` key. An example of using the `overrides` key is as follows:
diff --git a/docs/user-guide/configuring/rules.md b/docs/user-guide/configuring/rules.md
index c33a64ad4..205a5f119 100644
--- a/docs/user-guide/configuring/rules.md
+++ b/docs/user-guide/configuring/rules.md
@@ -144,6 +144,8 @@ console.log('bar');
 /* eslint-enable no-alert, no-console */
 ```
 
+**Note:** `/* eslint-enable */` without any specific rules listed will cause all disabled rules to be re-enabled.
+
 To disable rule warnings in an entire file, put a `/* eslint-disable */` block comment at the top of the file:
 
 ```js
@@ -160,6 +162,14 @@ You can also disable or enable specific rules for an entire file:
 alert('foo');
 ```
 
+To ensure that a rule is never applied (regardless of any future enable/disable lines):
+
+```js
+/* eslint no-alert: "off" */
+
+alert('foo');
+```
+
 To disable all rules on a specific line, use a line or block comment in one of the following formats:
 
 ```js
diff --git a/lib/cli-engine/formatters/html.js b/lib/cli-engine/formatters/html.js
index e28996f6c..b63659971 100644
--- a/lib/cli-engine/formatters/html.js
+++ b/lib/cli-engine/formatters/html.js
@@ -39,6 +39,8 @@ function pageTemplate(it) {
     <head>
         <meta charset="UTF-8">
         <title>ESLint Report</title>
+        <link rel="icon" type="image/png" sizes="any" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAHaAAAB2gGFomX7AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAABD1JREFUWMPFl11sk2UUx3/nbYtjxS1MF7MLMTECMgSTtSSyrQkLhAj7UBPnDSEGoxegGzMwojhXVpmTAA5iYpSoMQa8GBhFOrMFk03buei6yRAlcmOM0SEmU9d90b19jxcM1o5+sGnsc/e+z/l6ztf/HFFVMnns6QieeOCHBePGsHM+wrOtvLG2C4WRVDSSygNV7sCjlspxwDnPB44aols/DXk+mbMBmx/6OseITF1CuOtfevkPh2Uu+/jbdX8lujSScRlT5r7/QDlAfsRmfzmpnkQ/H3H13gf6bBrBn1uqK8WylgEnU8eZmk1repbfchJG1TyKyIKEwuBHFd3lD3naY3O1siiwXsVoBV2VgM1ht/QQUJk2ByqKghsQziYQ8ifKgexIXmuyzC4r67Y7R+xPAfuB/Nn3Cpva+0s7khpQVtZtd4bt51BWxtBYAiciprG7c7D4SixzU9PYalDL6110Ifb/w8W9eY7JqFeFHbO8fPGyLHwwFHJNJTSgwtVTB9oaw9BlQ+tO93vOxypoaQnfEYlI43SeCHDC4TDq9+51/h5fxr33q0ZfV9g04wat9Q943rjJgCp3952W2i8Bi6eDvdsfKj0cK/DYMRyXL4/sUJUmIHd2zYMezsvLaamp4WpcWN3BXSiHpuMwbGbZlnZ8tXY4rgosy+G7oRwQ0cAsd28YGgqfU5UjCZQDLALxDg+Hv/P5Rqvj4hwrS8izXzWb4spwc1GgENFnkpWRzxeuB+ssUHgLdb9UVdt8vpGdKQpze7n7y1U3DBChNRUuqOo9c+0+qpKKxyZqtAIYla7gY4JszAAQri93BSsMRZoyBcUC+w3Q3AyOA4sNhAOZ0q7Iq0b2vUNvK5zPgP+/H8+Zetdoa6uOikhdGurxebwvJY8Iz3V1rTMNAH+opEuQj5KTT/qA1yC+wyUjBm12OidaUtCcPNNX2h0Hx2JG69VulANZAJZJwfU7rzd/FHixuXniTdM0m4GtSQT7bTartqEh9yfImUEzkwKZmTwmo5a5JwkYBfcDL01/RkR5y8iWhtPBknB8ZxwtU9UjwOrrKCeizzc25nTGg1F/turEHoU9wMLpDvWKf8DTmNCAKnd/tqUTF4ElMXJ+A5rWDJS+41WsGWzALhJ+ErBWrLj9g+pqojHxlXJX8HGUg0BsR/x1yhxf3jm4cSzpQFLp6tmi6PEE7g1ZhtZ91ufpSZUAFa6gC+UoQslNaSmypT1U8mHKiUgEKS8KfgF4EpYunFI16tsHin+OG0LcgQK7yj7g6cSzpva2D3hKVNG0Y3mVO1BkqfSlmJrHBQ4uvM12gJHc6ETW8HZVfMRmXvyxxNC1Z/o839zyXlDuCr4nsC11J+MXueaVJWn6yPv+/pJtc9oLTNN4AeTvNGByd3rlhE2x9s5pLwDoHCy+grDzWmOZ95lUtLYj5Bma126Y8eX0/zj/ADxGyViSg4BXAAAAAElFTkSuQmCC">
+        <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PScwIDAgMjk0LjgyNSAyNTguOTgyJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPg0KPHBhdGggZmlsbD0nIzgwODBGMicgZD0nTTk3LjAyMSw5OS4wMTZsNDguNDMyLTI3Ljk2MmMxLjIxMi0wLjcsMi43MDYtMC43LDMuOTE4LDBsNDguNDMzLDI3Ljk2MiBjMS4yMTEsMC43LDEuOTU5LDEuOTkzLDEuOTU5LDMuMzkzdjU1LjkyNGMwLDEuMzk5LTAuNzQ4LDIuNjkzLTEuOTU5LDMuMzk0bC00OC40MzMsMjcuOTYyYy0xLjIxMiwwLjctMi43MDYsMC43LTMuOTE4LDAgbC00OC40MzItMjcuOTYyYy0xLjIxMi0wLjctMS45NTktMS45OTQtMS45NTktMy4zOTR2LTU1LjkyNEM5NS4wNjMsMTAxLjAwOSw5NS44MSw5OS43MTYsOTcuMDIxLDk5LjAxNicvPg0KPHBhdGggZmlsbD0nIzRCMzJDMycgZD0nTTI3My4zMzYsMTI0LjQ4OEwyMTUuNDY5LDIzLjgxNmMtMi4xMDItMy42NC01Ljk4NS02LjMyNS0xMC4xODgtNi4zMjVIODkuNTQ1IGMtNC4yMDQsMC04LjA4OCwyLjY4NS0xMC4xOSw2LjMyNWwtNTcuODY3LDEwMC40NWMtMi4xMDIsMy42NDEtMi4xMDIsOC4yMzYsMCwxMS44NzdsNTcuODY3LDk5Ljg0NyBjMi4xMDIsMy42NCw1Ljk4Niw1LjUwMSwxMC4xOSw1LjUwMWgxMTUuNzM1YzQuMjAzLDAsOC4wODctMS44MDUsMTAuMTg4LTUuNDQ2bDU3Ljg2Ny0xMDAuMDEgQzI3NS40MzksMTMyLjM5NiwyNzUuNDM5LDEyOC4xMjgsMjczLjMzNiwxMjQuNDg4IE0yMjUuNDE5LDE3Mi44OThjMCwxLjQ4LTAuODkxLDIuODQ5LTIuMTc0LDMuNTlsLTczLjcxLDQyLjUyNyBjLTEuMjgyLDAuNzQtMi44ODgsMC43NC00LjE3LDBsLTczLjc2Ny00Mi41MjdjLTEuMjgyLTAuNzQxLTIuMTc5LTIuMTA5LTIuMTc5LTMuNTlWODcuODQzYzAtMS40ODEsMC44ODQtMi44NDksMi4xNjctMy41OSBsNzMuNzA3LTQyLjUyN2MxLjI4Mi0wLjc0MSwyLjg4Ni0wLjc0MSw0LjE2OCwwbDczLjc3Miw0Mi41MjdjMS4yODMsMC43NDEsMi4xODYsMi4xMDksMi4xODYsMy41OVYxNzIuODk4eicvPg0KPC9zdmc+">
         <style>
             body {
                 font-family:Arial, "Helvetica Neue", Helvetica, sans-serif;
diff --git a/lib/rules/comma-dangle.js b/lib/rules/comma-dangle.js
index 232b27cb3..063e1cb69 100644
--- a/lib/rules/comma-dangle.js
+++ b/lib/rules/comma-dangle.js
@@ -243,8 +243,18 @@ module.exports = {
                     node: lastItem,
                     loc: trailingToken.loc,
                     messageId: "unexpected",
-                    fix(fixer) {
-                        return fixer.remove(trailingToken);
+                    *fix(fixer) {
+                        yield fixer.remove(trailingToken);
+
+                        /*
+                         * Extend the range of the fix to include surrounding tokens to ensure
+                         * that the element after which the comma is removed stays _last_.
+                         * This intentionally makes conflicts in fix ranges with rules that may be
+                         * adding or removing elements in the same autofix pass.
+                         * https://github.com/eslint/eslint/issues/15660
+                         */
+                        yield fixer.insertTextBefore(sourceCode.getTokenBefore(trailingToken), "");
+                        yield fixer.insertTextAfter(sourceCode.getTokenAfter(trailingToken), "");
                     }
                 });
             }
@@ -282,8 +292,18 @@ module.exports = {
                         end: astUtils.getNextLocation(sourceCode, trailingToken.loc.end)
                     },
                     messageId: "missing",
-                    fix(fixer) {
-                        return fixer.insertTextAfter(trailingToken, ",");
+                    *fix(fixer) {
+                        yield fixer.insertTextAfter(trailingToken, ",");
+
+                        /*
+                         * Extend the range of the fix to include surrounding tokens to ensure
+                         * that the element after which the comma is inserted stays _last_.
+                         * This intentionally makes conflicts in fix ranges with rules that may be
+                         * adding or removing elements in the same autofix pass.
+                         * https://github.com/eslint/eslint/issues/15660
+                         */
+                        yield fixer.insertTextBefore(trailingToken, "");
+                        yield fixer.insertTextAfter(sourceCode.getTokenAfter(trailingToken), "");
                     }
                 });
             }
diff --git a/lib/rules/no-constant-condition.js b/lib/rules/no-constant-condition.js
index 8adc9bca4..3d08c689a 100644
--- a/lib/rules/no-constant-condition.js
+++ b/lib/rules/no-constant-condition.js
@@ -120,12 +120,30 @@ module.exports = {
             return false;
         }
 
+        /**
+         * Checks if an identifier is a reference to a global variable.
+         * @param {ASTNode} node An identifier node to check.
+         * @returns {boolean} `true` if the identifier is a reference to a global variable.
+         */
+        function isReferenceToGlobalVariable(node) {
+            const scope = context.getScope();
+            const reference = scope.references.find(ref => ref.identifier === node);
+
+            return Boolean(
+                reference &&
+                reference.resolved &&
+                reference.resolved.scope.type === "global" &&
+                reference.resolved.defs.length === 0
+            );
+        }
+
         /**
          * Checks if a node has a constant truthiness value.
          * @param {ASTNode} node The AST node to check.
-         * @param {boolean} inBooleanPosition `false` if checking branch of a condition.
-         *  `true` in all other cases. When `false`, checks if -- for both string and
-         *  number -- if coerced to that type, the value will be constant.
+         * @param {boolean} inBooleanPosition `true` if checking the test of a
+         * condition. `false` in all other cases. When `false`, checks if -- for
+         * both string and number -- if coerced to that type, the value will
+         * be constant.
          * @returns {Bool} true when node's truthiness is constant
          * @private
          */
@@ -215,6 +233,15 @@ module.exports = {
                     return isConstant(node.expressions[node.expressions.length - 1], inBooleanPosition);
                 case "SpreadElement":
                     return isConstant(node.argument, inBooleanPosition);
+                case "CallExpression":
+                    if (node.callee.type === "Identifier" && node.callee.name === "Boolean") {
+                        if (node.arguments.length === 0 || isConstant(node.arguments[0], true)) {
+                            return isReferenceToGlobalVariable(node.callee);
+                        }
+                    }
+                    return false;
+                case "Identifier":
+                    return node.name === "undefined" && isReferenceToGlobalVariable(node);
 
                 // no default
             }
diff --git a/lib/rules/no-unused-vars.js b/lib/rules/no-unused-vars.js
index eaf7a8b91..54e33705f 100644
--- a/lib/rules/no-unused-vars.js
+++ b/lib/rules/no-unused-vars.js
@@ -67,6 +67,9 @@ module.exports = {
                             },
                             caughtErrorsIgnorePattern: {
                                 type: "string"
+                            },
+                            destructuredArrayIgnorePattern: {
+                                type: "string"
                             }
                         },
                         additionalProperties: false
@@ -114,6 +117,10 @@ module.exports = {
                 if (firstOption.caughtErrorsIgnorePattern) {
                     config.caughtErrorsIgnorePattern = new RegExp(firstOption.caughtErrorsIgnorePattern, "u");
                 }
+
+                if (firstOption.destructuredArrayIgnorePattern) {
+                    config.destructuredArrayIgnorePattern = new RegExp(firstOption.destructuredArrayIgnorePattern, "u");
+                }
             }
         }
 
@@ -155,7 +162,14 @@ module.exports = {
          * @returns {UnusedVarMessageData} The message data to be used with this unused variable.
          */
         function getAssignedMessageData(unusedVar) {
-            const additional = config.varsIgnorePattern ? `. Allowed unused vars must match ${config.varsIgnorePattern.toString()}` : "";
+            const def = unusedVar.defs[0];
+            let additional = "";
+
+            if (config.destructuredArrayIgnorePattern && def && def.name.parent.type === "ArrayPattern") {
+                additional = `. Allowed unused elements of array destructuring patterns must match ${config.destructuredArrayIgnorePattern.toString()}`;
+            } else if (config.varsIgnorePattern) {
+                additional = `. Allowed unused vars must match ${config.varsIgnorePattern.toString()}`;
+            }
 
             return {
                 varName: unusedVar.name,
@@ -584,6 +598,19 @@ module.exports = {
 
                     if (def) {
                         const type = def.type;
+                        const refUsedInArrayPatterns = variable.references.some(ref => ref.identifier.parent.type === "ArrayPattern");
+
+                        // skip elements of array destructuring patterns
+                        if (
+                            (
+                                def.name.parent.type === "ArrayPattern" ||
+                                refUsedInArrayPatterns
+                            ) &&
+                            config.destructuredArrayIgnorePattern &&
+                            config.destructuredArrayIgnorePattern.test(def.name.name)
+                        ) {
+                            continue;
+                        }
 
                         // skip catch variables
                         if (type === "CatchClause") {
diff --git a/lib/rules/valid-typeof.js b/lib/rules/valid-typeof.js
index 604635812..cb85cd9cb 100644
--- a/lib/rules/valid-typeof.js
+++ b/lib/rules/valid-typeof.js
@@ -19,6 +19,8 @@ module.exports = {
             url: "https://eslint.org/docs/rules/valid-typeof"
         },
 
+        hasSuggestions: true,
+
         schema: [
             {
                 type: "object",
@@ -33,7 +35,8 @@ module.exports = {
         ],
         messages: {
             invalidValue: "Invalid typeof comparison value.",
-            notString: "Typeof comparisons should be to string literals."
+            notString: "Typeof comparisons should be to string literals.",
+            suggestString: 'Use `"{{type}}"` instead of `{{type}}`.'
         }
     },
 
@@ -44,6 +47,21 @@ module.exports = {
 
         const requireStringLiterals = context.options[0] && context.options[0].requireStringLiterals;
 
+        let globalScope;
+
+        /**
+         * Checks whether the given node represents a reference to a global variable that is not declared in the source code.
+         * These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
+         * @param {ASTNode} node `Identifier` node to check.
+         * @returns {boolean} `true` if the node is a reference to a global variable.
+         */
+        function isReferenceToGlobalVariable(node) {
+            const variable = globalScope.set.get(node.name);
+
+            return variable && variable.defs.length === 0 &&
+                variable.references.some(ref => ref.identifier === node);
+        }
+
         /**
          * Determines whether a node is a typeof expression.
          * @param {ASTNode} node The node
@@ -59,6 +77,10 @@ module.exports = {
 
         return {
 
+            Program() {
+                globalScope = context.getScope();
+            },
+
             UnaryExpression(node) {
                 if (isTypeofExpression(node)) {
                     const parent = context.getAncestors().pop();
@@ -72,6 +94,20 @@ module.exports = {
                             if (VALID_TYPES.indexOf(value) === -1) {
                                 context.report({ node: sibling, messageId: "invalidValue" });
                             }
+                        } else if (sibling.type === "Identifier" && sibling.name === "undefined" && isReferenceToGlobalVariable(sibling)) {
+                            context.report({
+                                node: sibling,
+                                messageId: requireStringLiterals ? "notString" : "invalidValue",
+                                suggest: [
+                                    {
+                                        messageId: "suggestString",
+                                        data: { type: "undefined" },
+                                        fix(fixer) {
+                                            return fixer.replaceText(sibling, '"undefined"');
+                                        }
+                                    }
+                                ]
+                            });
                         } else if (requireStringLiterals && !isTypeofExpression(sibling)) {
                             context.report({ node: sibling, messageId: "notString" });
                         }


Test patch:
diff --git a/tests/lib/rules/comma-dangle.js b/tests/lib/rules/comma-dangle.js
index 2ef7a008e..b02a74a55 100644
--- a/tests/lib/rules/comma-dangle.js
+++ b/tests/lib/rules/comma-dangle.js
@@ -10,6 +10,7 @@
 //------------------------------------------------------------------------------
 
 const path = require("path"),
+    { unIndent } = require("../../_utils"),
     rule = require("../../../lib/rules/comma-dangle"),
     { RuleTester } = require("../../../lib/rule-tester");
 
@@ -35,6 +36,29 @@ function parser(name) {
 
 const ruleTester = new RuleTester();
 
+ruleTester.defineRule("add-named-import", {
+    meta: {
+        fixable: "code"
+    },
+    create(context) {
+        return {
+            ImportDeclaration(node) {
+                const sourceCode = context.getSourceCode();
+                const closingBrace = sourceCode.getLastToken(node, token => token.value === "}");
+                const addComma = sourceCode.getTokenBefore(closingBrace).value !== ",";
+
+                context.report({
+                    message: "Add I18nManager.",
+                    node,
+                    fix(fixer) {
+                        return fixer.insertTextBefore(closingBrace, `${addComma ? "," : ""}I18nManager`);
+                    }
+                });
+            }
+        };
+    }
+});
+
 ruleTester.run("comma-dangle", rule, {
     valid: [
         "var foo = { bar: 'baz' }",
@@ -1766,6 +1790,66 @@ let d = 0;export {d,};
             output: "foo(a)",
             parserOptions: { ecmaVersion: 8 },
             errors: [{ messageId: "unexpected" }]
+        },
+
+        // https://github.com/eslint/eslint/issues/15660
+        {
+            code: unIndent`
+                /*eslint add-named-import:1*/
+                import {
+                    StyleSheet,
+                    View,
+                    TextInput,
+                    ImageBackground,
+                    Image,
+                    TouchableOpacity,
+                    SafeAreaView
+                } from 'react-native';
+            `,
+            output: unIndent`
+                /*eslint add-named-import:1*/
+                import {
+                    StyleSheet,
+                    View,
+                    TextInput,
+                    ImageBackground,
+                    Image,
+                    TouchableOpacity,
+                    SafeAreaView,
+                } from 'react-native';
+            `,
+            options: [{ imports: "always-multiline" }],
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: 2
+        },
+        {
+            code: unIndent`
+                /*eslint add-named-import:1*/
+                import {
+                    StyleSheet,
+                    View,
+                    TextInput,
+                    ImageBackground,
+                    Image,
+                    TouchableOpacity,
+                    SafeAreaView,
+                } from 'react-native';
+            `,
+            output: unIndent`
+                /*eslint add-named-import:1*/
+                import {
+                    StyleSheet,
+                    View,
+                    TextInput,
+                    ImageBackground,
+                    Image,
+                    TouchableOpacity,
+                    SafeAreaView
+                } from 'react-native';
+            `,
+            options: [{ imports: "never" }],
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: 2
         }
     ]
 });
diff --git a/tests/lib/rules/no-constant-condition.js b/tests/lib/rules/no-constant-condition.js
index 9cca27b90..c22b2947a 100644
--- a/tests/lib/rules/no-constant-condition.js
+++ b/tests/lib/rules/no-constant-condition.js
@@ -185,7 +185,17 @@ ruleTester.run("no-constant-condition", rule, {
         "if (`${[a]}`) {}",
         "if (+[a]) {}",
         "if (0 - [a]) {}",
-        "if (1 * [a]) {}"
+        "if (1 * [a]) {}",
+
+        // Boolean function
+        "if (Boolean(a)) {}",
+        "if (Boolean(...args)) {}",
+        "if (foo.Boolean(1)) {}",
+        "function foo(Boolean) { if (Boolean(1)) {} }",
+        "const Boolean = () => {}; if (Boolean(1)) {}",
+        { code: "if (Boolean()) {}", globals: { Boolean: "off" } },
+        "const undefined = 'lol'; if (undefined) {}",
+        { code: "if (undefined) {}", globals: { undefined: "off" } }
     ],
     invalid: [
         { code: "for(;true;);", errors: [{ messageId: "unexpected", type: "Literal" }] },
@@ -396,6 +406,18 @@ ruleTester.run("no-constant-condition", rule, {
         { code: "if(new Number(foo)) {}", errors: [{ messageId: "unexpected" }] },
 
         // Spreading a constant array
-        { code: "if(`${[...['a']]}`) {}", errors: [{ messageId: "unexpected" }] }
+        { code: "if(`${[...['a']]}`) {}", errors: [{ messageId: "unexpected" }] },
+
+        /*
+         * undefined is always falsy (except in old browsers that let you
+         * re-assign, but that's an abscure enough edge case to not worry about)
+         */
+        { code: "if (undefined) {}", errors: [{ messageId: "unexpected" }] },
+
+        // Coercion to boolean via Boolean function
+        { code: "if (Boolean(1)) {}", errors: [{ messageId: "unexpected" }] },
+        { code: "if (Boolean()) {}", errors: [{ messageId: "unexpected" }] },
+        { code: "if (Boolean([a])) {}", errors: [{ messageId: "unexpected" }] },
+        { code: "if (Boolean(1)) { function Boolean() {}}", errors: [{ messageId: "unexpected" }] }
     ]
 });
diff --git a/tests/lib/rules/no-unused-vars.js b/tests/lib/rules/no-unused-vars.js
index 7c5c87102..e7276f6d4 100644
--- a/tests/lib/rules/no-unused-vars.js
+++ b/tests/lib/rules/no-unused-vars.js
@@ -162,6 +162,81 @@ ruleTester.run("no-unused-vars", rule, {
         { code: "function foo(_a) { } foo();", options: [{ args: "all", argsIgnorePattern: "^_" }] },
         { code: "function foo(a, _b) { return a; } foo();", options: [{ args: "after-used", argsIgnorePattern: "^_" }] },
         { code: "var [ firstItemIgnored, secondItem ] = items;\nconsole.log(secondItem);", options: [{ vars: "all", varsIgnorePattern: "[iI]gnored" }], parserOptions: { ecmaVersion: 6 } },
+        {
+            code: "const [ a, _b, c ] = items;\nconsole.log(a+c);",
+            options: [{ destructuredArrayIgnorePattern: "^_" }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "const [ [a, _b, c] ] = items;\nconsole.log(a+c);",
+            options: [{ destructuredArrayIgnorePattern: "^_" }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "const { x: [_a, foo] } = bar;\nconsole.log(foo);",
+            options: [{ destructuredArrayIgnorePattern: "^_" }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "function baz([_b, foo]) { foo; };\nbaz()",
+            options: [{ destructuredArrayIgnorePattern: "^_" }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "function baz({x: [_b, foo]}) {foo};\nbaz()",
+            options: [{ destructuredArrayIgnorePattern: "^_" }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "function baz([{x: [_b, foo]}]) {foo};\nbaz()",
+            options: [{ destructuredArrayIgnorePattern: "^_" }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: `
+            let _a, b;
+            foo.forEach(item => {
+                [_a, b] = item;
+                doSomething(b);
+            });
+            `,
+            options: [{ destructuredArrayIgnorePattern: "^_" }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: `
+            // doesn't report _x
+            let _x, y;
+            _x = 1;
+            [_x, y] = foo;
+            y;
+
+            // doesn't report _a
+            let _a, b;
+            [_a, b] = foo;
+            _a = 1;
+            b;
+            `,
+            options: [{ destructuredArrayIgnorePattern: "^_" }],
+            parserOptions: { ecmaVersion: 2018 }
+        },
+        {
+            code: `
+            // doesn't report _x
+            let _x, y;
+            _x = 1;
+            [_x, y] = foo;
+            y;
+
+            // doesn't report _a
+            let _a, b;
+            _a = 1;
+            ({_a, ...b } = foo);
+            b;
+            `,
+            options: [{ destructuredArrayIgnorePattern: "^_", ignoreRestSiblings: true }],
+            parserOptions: { ecmaVersion: 2018 }
+        },
 
         // for-in loops (see #2342)
         "(function(obj) { var name; for ( name in obj ) return; })({});",
@@ -463,6 +538,131 @@ ruleTester.run("no-unused-vars", rule, {
             }]
         },
 
+        // https://github.com/eslint/eslint/issues/15611
+        {
+            code: `
+            const array = ['a', 'b', 'c'];
+            const [a, _b, c] = array;
+            const newArray = [a, c];
+            `,
+            options: [{ destructuredArrayIgnorePattern: "^_" }],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [
+
+                // should report only `newArray`
+                { ...assignedError("newArray"), line: 4, column: 19 }
+            ]
+        },
+        {
+            code: `
+            const array = ['a', 'b', 'c', 'd', 'e'];
+            const [a, _b, c] = array;
+            `,
+            options: [{ destructuredArrayIgnorePattern: "^_" }],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [
+                {
+                    ...assignedError("a", ". Allowed unused elements of array destructuring patterns must match /^_/u"),
+                    line: 3,
+                    column: 20
+                },
+                {
+                    ...assignedError("c", ". Allowed unused elements of array destructuring patterns must match /^_/u"),
+                    line: 3,
+                    column: 27
+                }
+            ]
+        },
+        {
+            code: `
+            const array = ['a', 'b', 'c'];
+            const [a, _b, c] = array;
+            const fooArray = ['foo'];
+            const barArray = ['bar'];
+            const ignoreArray = ['ignore'];
+            `,
+            options: [{ destructuredArrayIgnorePattern: "^_", varsIgnorePattern: "ignore" }],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [
+                {
+                    ...assignedError("a", ". Allowed unused elements of array destructuring patterns must match /^_/u"),
+                    line: 3,
+                    column: 20
+                },
+                {
+                    ...assignedError("c", ". Allowed unused elements of array destructuring patterns must match /^_/u"),
+                    line: 3,
+                    column: 27
+                },
+                {
+                    ...assignedError("fooArray", ". Allowed unused vars must match /ignore/u"),
+                    line: 4,
+                    column: 19
+                },
+                {
+                    ...assignedError("barArray", ". Allowed unused vars must match /ignore/u"),
+                    line: 5,
+                    column: 19
+                }
+            ]
+        },
+        {
+            code: `
+            const array = [obj];
+            const [{_a, foo}] = array;
+            console.log(foo);
+            `,
+            options: [{ destructuredArrayIgnorePattern: "^_" }],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [
+                {
+                    ...assignedError("_a"),
+                    line: 3,
+                    column: 21
+                }
+            ]
+        },
+        {
+            code: `
+            function foo([{_a, bar}]) {
+                bar;
+            }
+            foo();
+            `,
+            options: [{ destructuredArrayIgnorePattern: "^_" }],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [
+                {
+                    ...definedError("_a"),
+                    line: 2,
+                    column: 28
+                }
+            ]
+        },
+        {
+            code: `
+            let _a, b;
+
+            foo.forEach(item => {
+                [a, b] = item;
+            });
+            `,
+            options: [{ destructuredArrayIgnorePattern: "^_" }],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [
+                {
+                    ...definedError("_a"),
+                    line: 2,
+                    column: 17
+                },
+                {
+                    ...assignedError("b"),
+                    line: 2,
+                    column: 21
+                }
+            ]
+        },
+
         // for-in loops (see #2342)
         {
             code: "(function(obj) { var name; for ( name in obj ) { i(); return; } })({});",
diff --git a/tests/lib/rules/valid-typeof.js b/tests/lib/rules/valid-typeof.js
index cd28088a0..35a52f0a6 100644
--- a/tests/lib/rules/valid-typeof.js
+++ b/tests/lib/rules/valid-typeof.js
@@ -45,6 +45,7 @@ ruleTester.run("valid-typeof", rule, {
         "typeof(foo) == 'string'",
         "typeof(foo) != 'string'",
         "var oddUse = typeof foo + 'thing'",
+        "function f(undefined) { typeof x === undefined }",
         {
             code: "typeof foo === 'number'",
             options: [{ requireStringLiterals: true }]
@@ -136,6 +137,21 @@ ruleTester.run("valid-typeof", rule, {
             options: [{ requireStringLiterals: true }],
             errors: [{ messageId: "invalidValue", type: "Literal" }]
         },
+        {
+            code: "if (typeof bar !== undefined) {}",
+            errors: [
+                {
+                    messageId: "invalidValue",
+                    type: "Identifier",
+                    suggestions: [
+                        {
+                            messageId: "suggestString",
+                            data: { type: "undefined" },
+                            output: 'if (typeof bar !== "undefined") {}'
+                        }
+                    ]
+                }]
+        },
         {
             code: "typeof foo == Object",
             options: [{ requireStringLiterals: true }],
@@ -144,17 +160,50 @@ ruleTester.run("valid-typeof", rule, {
         {
             code: "typeof foo === undefined",
             options: [{ requireStringLiterals: true }],
-            errors: [{ messageId: "notString", type: "Identifier" }]
+            errors: [
+                {
+                    messageId: "notString",
+                    type: "Identifier",
+                    suggestions: [
+                        {
+                            messageId: "suggestString",
+                            data: { type: "undefined" },
+                            output: 'typeof foo === "undefined"'
+                        }
+                    ]
+                }]
         },
         {
             code: "undefined === typeof foo",
             options: [{ requireStringLiterals: true }],
-            errors: [{ messageId: "notString", type: "Identifier" }]
+            errors: [
+                {
+                    messageId: "notString",
+                    type: "Identifier",
+                    suggestions: [
+                        {
+                            messageId: "suggestString",
+                            data: { type: "undefined" },
+                            output: '"undefined" === typeof foo'
+                        }
+                    ]
+                }]
         },
         {
             code: "undefined == typeof foo",
             options: [{ requireStringLiterals: true }],
-            errors: [{ messageId: "notString", type: "Identifier" }]
+            errors: [
+                {
+                    messageId: "notString",
+                    type: "Identifier",
+                    suggestions: [
+                        {
+                            messageId: "suggestString",
+                            data: { type: "undefined" },
+                            output: '"undefined" == typeof foo'
+                        }
+                    ]
+                }]
         },
         {
             code: "typeof foo === `undefined${foo}`",