Repository: eslint/eslint
Pull request #11952 (2019-07-05 16:01:47)

Update: Check computed property keys in no-extra-parens
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[X] Bug fix

<!--
    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.)
-->

**Tell us about your environment**

* **ESLint Version:** 6.0.1
* **Node Version:** 10.16.0
* **npm Version:** 6.9.0

**What parser (default, Babel-ESLint, etc.) are you using?**

default

**Please show your full configuration:**

<details>
<summary>Configuration</summary>

<!-- Paste your configuration below: -->
```js
module.exports = {
  parserOptions: {
    ecmaVersion: 6,
  },
  rules: {
    "no-extra-parens": "error"
  }
};
```

</details>

**What did you do? Please include the actual source code causing the issue.**

```js
const obj = {
  *[(Symbol.iterator)]() {}
}

const { [(a)]:b } = {}
```

**What did you expect to happen?**

2 errors.

**What actually happened? Please include the actual, raw output from ESLint.**

No errors.

<!--
    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)**

Added the `Property` handler to `no-extra-parens`, to check computed property keys.

This affects both `ObjectExpression` properties and `ObjectPattern` properties.

**Is there anything you'd like reviewers to focus on?**

* I guess this is a bug fix that adds more reported warnings, rather than a new behavior.
* The same check could be done within `ObjectExpression` and `ObjectPattern` handlers instead of the `Property` handler. I'm not sure which way would be better. `ObjectExpression` is already checking values.
* `const a = { [b, c]: 1 }` is indeed a syntax error without parens around the sequence expression.
* There will be a separate PR for classes.




Patch:
diff --git a/.gitignore b/.gitignore
index 38b7ecd3b..b550439d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,5 @@ versions.json
 .eslint-release-info.json
 .nyc_output
 /test-results.xml
+.temp-eslintcache
+/tests/fixtures/autofix-integration/temp.js
diff --git a/Makefile.js b/Makefile.js
index 536453bc5..9bb9e12ad 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -774,8 +774,8 @@ target.gensite = function(prereleaseVersion) {
     if (!prereleaseVersion) {
         echo("> Updating the demos (Step 13)");
         target.webpack("production");
-        cp("-f", "build/eslint.js", `${SITE_DIR}js/app/eslint.js`);
-        cp("-f", "build/espree.js", `${SITE_DIR}js/app/espree.js`);
+        cp("-f", "build/eslint.js", `${SITE_DIR}src/js/eslint.js`);
+        cp("-f", "build/espree.js", `${SITE_DIR}src/js/espree.js`);
     } else {
         echo("> Skipped updating the demos (Step 13)");
     }
diff --git a/docs/rules/array-element-newline.md b/docs/rules/array-element-newline.md
index f26c92a82..f67661f60 100644
--- a/docs/rules/array-element-newline.md
+++ b/docs/rules/array-element-newline.md
@@ -61,7 +61,7 @@ var e = [
 
 ### never
 
-Examples of **incorrect** code for this rule with the default `"never"` option:
+Examples of **incorrect** code for this rule with the `"never"` option:
 
 ```js
 /*eslint array-element-newline: ["error", "never"]*/
diff --git a/docs/rules/func-names.md b/docs/rules/func-names.md
index 60a0910e5..cd6994006 100644
--- a/docs/rules/func-names.md
+++ b/docs/rules/func-names.md
@@ -59,7 +59,7 @@ Foo.prototype.bar = function bar() {};
 
 ECMAScript 6 introduced a `name` property on all functions. The value of `name` is determined by evaluating the code around the function to see if a name can be inferred. For example, a function assigned to a variable will automatically have a `name` property equal to the name of the variable. The value of `name` is then used in stack traces for easier debugging.
 
-Examples of **incorrect** code for this rule with the default `"as-needed"` option:
+Examples of **incorrect** code for this rule with the `"as-needed"` option:
 
 ```js
 /*eslint func-names: ["error", "as-needed"]*/
@@ -71,7 +71,7 @@ Foo.prototype.bar = function() {};
 }())
 ```
 
-Examples of **correct** code for this rule with the default `"as-needed"` option:
+Examples of **correct** code for this rule with the `"as-needed"` option:
 
 ```js
 /*eslint func-names: ["error", "as-needed"]*/
diff --git a/docs/rules/function-paren-newline.md b/docs/rules/function-paren-newline.md
index 099021e0e..ab86680dd 100644
--- a/docs/rules/function-paren-newline.md
+++ b/docs/rules/function-paren-newline.md
@@ -200,7 +200,7 @@ foo(
   });
 ```
 
-Examples of **correct** code for this rule with the consistent `"consistent"` option:
+Examples of **correct** code for this rule with the `"consistent"` option:
 
 ```js
 /* eslint function-paren-newline: ["error", "consistent"] */
diff --git a/docs/rules/new-parens.md b/docs/rules/new-parens.md
index 1640d37a2..0b2a49b7e 100644
--- a/docs/rules/new-parens.md
+++ b/docs/rules/new-parens.md
@@ -1,4 +1,4 @@
-# Require parentheses when invoking a constructor with no arguments (new-parens)
+# require parentheses when invoking a constructor with no arguments (new-parens)
 
 JavaScript allows the omission of parentheses when invoking a function via the `new` keyword and the constructor has no arguments. However, some coders believe that omitting the parentheses is inconsistent with the rest of the language and thus makes code less clear.
 
diff --git a/docs/rules/no-duplicate-imports.md b/docs/rules/no-duplicate-imports.md
index 797c626ad..683f31584 100644
--- a/docs/rules/no-duplicate-imports.md
+++ b/docs/rules/no-duplicate-imports.md
@@ -12,7 +12,7 @@ import { find } from 'module';
 
 ## Rule Details
 
-This rules requires that all imports from a single module exists in a single `import` statement.
+This rule requires that all imports from a single module exists in a single `import` statement.
 
 Example of **incorrect** code for this rule:
 
diff --git a/docs/rules/no-global-assign.md b/docs/rules/no-global-assign.md
index c4c1914da..b3718d178 100644
--- a/docs/rules/no-global-assign.md
+++ b/docs/rules/no-global-assign.md
@@ -37,7 +37,7 @@ top = 1
 
 ```js
 /*eslint no-global-assign: "error"*/
-/*globals a:false*/
+/*global a:readonly*/
 
 a = 1
 ```
@@ -61,7 +61,7 @@ onload = function() {}
 
 ```js
 /*eslint no-global-assign: "error"*/
-/*globals a:true*/
+/*global a:writable*/
 
 a = 1
 ```
diff --git a/docs/rules/no-native-reassign.md b/docs/rules/no-native-reassign.md
index fb1fe9f3f..bfb511de1 100644
--- a/docs/rules/no-native-reassign.md
+++ b/docs/rules/no-native-reassign.md
@@ -39,7 +39,7 @@ top = 1
 
 ```js
 /*eslint no-native-reassign: "error"*/
-/*globals a:false*/
+/*global a:readonly*/
 
 a = 1
 ```
@@ -63,7 +63,7 @@ onload = function() {}
 
 ```js
 /*eslint no-native-reassign: "error"*/
-/*globals a:true*/
+/*global a:writable*/
 
 a = 1
 ```
diff --git a/docs/rules/no-param-reassign.md b/docs/rules/no-param-reassign.md
index 51c81a8c0..f88d211e2 100644
--- a/docs/rules/no-param-reassign.md
+++ b/docs/rules/no-param-reassign.md
@@ -20,6 +20,14 @@ function foo(bar) {
 function foo(bar) {
     bar++;
 }
+
+function foo(bar) {
+    for (bar in baz) {}
+}
+
+function foo(bar) {
+    for (bar of baz) {}
+}
 ```
 
 Examples of **correct** code for this rule:
@@ -54,6 +62,14 @@ function foo(bar) {
 function foo(bar) {
     bar.aaa++;
 }
+
+function foo(bar) {
+    for (bar.aaa in baz) {}
+}
+
+function foo(bar) {
+    for (bar.aaa of baz) {}
+}
 ```
 
 Examples of **incorrect** code for the `{ "props": true }` option:
@@ -72,6 +88,14 @@ function foo(bar) {
 function foo(bar) {
     bar.aaa++;
 }
+
+function foo(bar) {
+    for (bar.aaa in baz) {}
+}
+
+function foo(bar) {
+    for (bar.aaa of baz) {}
+}
 ```
 
 Examples of **correct** code for the `{ "props": true }` option with `"ignorePropertyModificationsFor"` set:
@@ -90,6 +114,14 @@ function foo(bar) {
 function foo(bar) {
     bar.aaa++;
 }
+
+function foo(bar) {
+    for (bar.aaa in baz) {}
+}
+
+function foo(bar) {
+    for (bar.aaa of baz) {}
+}
 ```
 
 
diff --git a/docs/rules/no-return-await.md b/docs/rules/no-return-await.md
index c30fc60a6..02ba9af02 100644
--- a/docs/rules/no-return-await.md
+++ b/docs/rules/no-return-await.md
@@ -6,7 +6,7 @@ Inside an `async function`, `return await` is seldom useful. Since the return va
 
 This rule aims to prevent a likely common performance hazard due to a lack of understanding of the semantics of `async function`.
 
-The following patterns are considered warnings:
+Examples of **incorrect** code for this rule:
 
 ```js
 async function foo() {
@@ -14,7 +14,7 @@ async function foo() {
 }
 ```
 
-The following patterns are not warnings:
+Examples of **correct** code for this rule:
 
 ```js
 async function foo() {
diff --git a/docs/rules/no-self-assign.md b/docs/rules/no-self-assign.md
index b80df6afc..2991504e8 100644
--- a/docs/rules/no-self-assign.md
+++ b/docs/rules/no-self-assign.md
@@ -72,7 +72,7 @@ This rule has the option to check properties as well.
 Examples of **correct** code with the `{ "props": false }` option:
 
 ```js
-/*eslint no-self-assign: ["error", {"props": true}]*/
+/*eslint no-self-assign: ["error", {"props": false}]*/
 
 // self-assignments with properties.
 obj.a = obj.a;
diff --git a/docs/rules/no-template-curly-in-string.md b/docs/rules/no-template-curly-in-string.md
index 52c10df2e..9c03b67a6 100644
--- a/docs/rules/no-template-curly-in-string.md
+++ b/docs/rules/no-template-curly-in-string.md
@@ -5,7 +5,7 @@ ECMAScript 6 allows programmers to create strings containing variable or express
 
 ## Rule Details
 
-This rule aims to warn when a regular string contains what looks like a template literal placeholder. It will warn when it finds a string containing the template literal place holder (`${something}`) that uses either `"` or `'` for the quotes.
+This rule aims to warn when a regular string contains what looks like a template literal placeholder. It will warn when it finds a string containing the template literal placeholder (`${something}`) that uses either `"` or `'` for the quotes.
 
 ## Examples
 
diff --git a/docs/rules/no-undef.md b/docs/rules/no-undef.md
index 49a149fde..f5dc5a280 100644
--- a/docs/rules/no-undef.md
+++ b/docs/rules/no-undef.md
@@ -18,14 +18,14 @@ b = 10;
 Examples of **correct** code for this rule with `global` declaration:
 
 ```js
-/*global someFunction b:true*/
+/*global someFunction b:writable*/
 /*eslint no-undef: "error"*/
 
 var a = someFunction();
 b = 10;
 ```
 
-The `b:true` syntax in `/*global */` indicates that assignment to `b` is correct.
+The `b:writable` syntax in `/*global */` indicates that assignment to `b` is correct.
 
 Examples of **incorrect** code for this rule with `global` declaration:
 
diff --git a/docs/rules/no-underscore-dangle.md b/docs/rules/no-underscore-dangle.md
index 7383815b3..e112c7a1b 100644
--- a/docs/rules/no-underscore-dangle.md
+++ b/docs/rules/no-underscore-dangle.md
@@ -79,7 +79,7 @@ super._bar();
 
 ### enforceInMethodNames
 
-Examples of incorrect code for this rule with the `{ "enforceInMethodNames": true }` option:
+Examples of **incorrect** code for this rule with the `{ "enforceInMethodNames": true }` option:
 
 ```js
 /*eslint no-underscore-dangle: ["error", { "enforceInMethodNames": true }]*/
diff --git a/docs/rules/semi-style.md b/docs/rules/semi-style.md
index f8aa8fcde..7bd916f9d 100644
--- a/docs/rules/semi-style.md
+++ b/docs/rules/semi-style.md
@@ -14,8 +14,8 @@ This rule has an option.
 }
 ```
 
-- `"last"` (Default) ... enforces that semicolons are at the end of statements.
-- `"first"` ... enforces that semicolons are at the beginning of statements. Semicolons of `for` loop heads (`for(a;b;c){}`) should be at the end of lines even if you use this option.
+- `"last"` (Default) enforces that semicolons are at the end of statements.
+- `"first"` enforces that semicolons are at the beginning of statements. Semicolons of `for` loop heads (`for(a;b;c){}`) should be at the end of lines even if you use this option.
 
 Examples of **incorrect** code for this rule with `"last"` option:
 
diff --git a/docs/rules/space-before-blocks.md b/docs/rules/space-before-blocks.md
index 0b188f260..25b81cae2 100644
--- a/docs/rules/space-before-blocks.md
+++ b/docs/rules/space-before-blocks.md
@@ -47,7 +47,7 @@ class Foo{
 }
 ```
 
-Examples of **correct** code for this rule with the "always" option:
+Examples of **correct** code for this rule with the `"always"` option:
 
 ```js
 /*eslint space-before-blocks: "error"*/
@@ -74,7 +74,7 @@ try {} catch(a) {}
 
 ### "never"
 
-Examples of **incorrect** code for this rule with the "never" option:
+Examples of **incorrect** code for this rule with the `"never"` option:
 
 ```js
 /*eslint space-before-blocks: ["error", "never"]*/
@@ -92,7 +92,7 @@ for (;;) {
 try {} catch(a) {}
 ```
 
-Examples of **correct** code for this rule with the "never" option:
+Examples of **correct** code for this rule with the `"never"` option:
 
 ```js
 /*eslint space-before-blocks: ["error", "never"]*/
diff --git a/docs/rules/switch-colon-spacing.md b/docs/rules/switch-colon-spacing.md
index baf081e4c..bd469ea77 100644
--- a/docs/rules/switch-colon-spacing.md
+++ b/docs/rules/switch-colon-spacing.md
@@ -15,10 +15,10 @@ This rule has 2 options that are boolean value.
 }
 ```
 
-- `"after": true` (Default) ... requires one or more spaces after colons.
-- `"after": false` ... disallows spaces after colons.
-- `"before": true` ... requires one or more spaces before colons.
-- `"before": false` (Default) ... disallows before colons.
+- `"after": true` (Default) requires one or more spaces after colons.
+- `"after": false` disallows spaces after colons.
+- `"before": true` requires one or more spaces before colons.
+- `"before": false` (Default) disallows before colons.
 
 
 Examples of **incorrect** code for this rule:
diff --git a/docs/rules/template-tag-spacing.md b/docs/rules/template-tag-spacing.md
index 004624a9f..58cd166f5 100644
--- a/docs/rules/template-tag-spacing.md
+++ b/docs/rules/template-tag-spacing.md
@@ -21,7 +21,7 @@ This rule aims to maintain consistency around the spacing between template tag f
 }
 ```
 
-This rule has one option whose value can be set to "never" or "always"
+This rule has one option whose value can be set to `"never"` or `"always"`
 
 * `"never"` (default) - Disallows spaces between a tag function and its template literal.
 * `"always"` - Requires one or more spaces between a tag function and its template literal.
diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md
index 86e60b176..6132c46bf 100644
--- a/docs/user-guide/configuring.md
+++ b/docs/user-guide/configuring.md
@@ -661,7 +661,7 @@ The complete configuration hierarchy, from highest precedence to lowest preceden
 1. Project-level configuration:
     1. `.eslintrc.*` or `package.json` file in same directory as linted file
     1. Continue searching for `.eslintrc` and `package.json` files in ancestor directories (parent has highest precedence, then grandparent, etc.), up to and including the root directory or until a config with `"root": true` is found.
-1. In the absence of any configuration from (1) through (3), fall back to a personal default configuration in  `~/.eslintrc`.
+1. In the absence of any configuration from (1) through (3), fall back to a personal default configuration in `~/.eslintrc`.
 
 ## Extending Configuration Files
 
diff --git a/docs/user-guide/getting-started.md b/docs/user-guide/getting-started.md
index c35471ea5..45633f618 100644
--- a/docs/user-guide/getting-started.md
+++ b/docs/user-guide/getting-started.md
@@ -13,7 +13,7 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
 
 ## Installation and Usage
 
-Prerequisites: [Node.js](https://nodejs.org/en/) (>=6.14), npm version 3+.
+Prerequisites: [Node.js](https://nodejs.org/en/) (>=8.10), npm version 3+.
 
 You can install ESLint using npm:
 
diff --git a/lib/cli-engine/cli-engine.js b/lib/cli-engine/cli-engine.js
index 81e1b07c8..7f80887f0 100644
--- a/lib/cli-engine/cli-engine.js
+++ b/lib/cli-engine/cli-engine.js
@@ -20,6 +20,7 @@ const path = require("path");
 const defaultOptions = require("../../conf/default-cli-options");
 const pkg = require("../../package.json");
 const ConfigOps = require("../shared/config-ops");
+const naming = require("../shared/naming");
 const ModuleResolver = require("../shared/relative-module-resolver");
 const { Linter } = require("../linter");
 const builtInRules = require("../rules");
@@ -29,7 +30,6 @@ const { FileEnumerator } = require("./file-enumerator");
 const hash = require("./hash");
 const { IgnoredPaths } = require("./ignored-paths");
 const LintResultCache = require("./lint-result-cache");
-const naming = require("./naming");
 
 const debug = require("debug")("eslint:cli-engine");
 const validFixTypes = new Set(["problem", "suggestion", "layout"]);
@@ -734,7 +734,10 @@ class CLIEngine {
             try {
                 fs.unlinkSync(cacheFilePath);
             } catch (error) {
-                if (!error || error.code !== "ENOENT") {
+                const errorCode = error && error.code;
+
+                // Ignore errors when no such file exists or file system is read only (and cache file does not exist)
+                if (errorCode !== "ENOENT" && !(errorCode === "EROFS" && !fs.existsSync(cacheFilePath))) {
                     throw error;
                 }
             }
diff --git a/lib/cli-engine/config-array-factory.js b/lib/cli-engine/config-array-factory.js
index 782bb1d14..95430c358 100644
--- a/lib/cli-engine/config-array-factory.js
+++ b/lib/cli-engine/config-array-factory.js
@@ -38,9 +38,9 @@ const path = require("path");
 const importFresh = require("import-fresh");
 const stripComments = require("strip-json-comments");
 const { validateConfigSchema } = require("../shared/config-validator");
+const naming = require("../shared/naming");
 const ModuleResolver = require("../shared/relative-module-resolver");
 const { ConfigArray, ConfigDependency, OverrideTester } = require("./config-array");
-const naming = require("./naming");
 const debug = require("debug")("eslint:config-array-factory");
 
 //------------------------------------------------------------------------------
diff --git a/lib/init/config-initializer.js b/lib/init/config-initializer.js
index 4d52377d2..17aa96505 100644
--- a/lib/init/config-initializer.js
+++ b/lib/init/config-initializer.js
@@ -18,6 +18,7 @@ const util = require("util"),
     recConfig = require("../../conf/eslint-recommended"),
     ConfigOps = require("../shared/config-ops"),
     log = require("../shared/logging"),
+    naming = require("../shared/naming"),
     ModuleResolver = require("../shared/relative-module-resolver"),
     autoconfig = require("./autoconfig.js"),
     ConfigFile = require("./config-file"),
@@ -97,17 +98,26 @@ function getModulesList(config, installESLint) {
     // Create a list of modules which should be installed based on config
     if (config.plugins) {
         for (const plugin of config.plugins) {
-            modules[`eslint-plugin-${plugin}`] = "latest";
+            const moduleName = naming.normalizePackageName(plugin, "eslint-plugin");
+
+            modules[moduleName] = "latest";
         }
     }
-    if (config.extends && config.extends.indexOf("eslint:") === -1) {
-        const moduleName = `eslint-config-${config.extends}`;
-
-        modules[moduleName] = "latest";
-        Object.assign(
-            modules,
-            getPeerDependencies(`${moduleName}@latest`)
-        );
+    if (config.extends) {
+        const extendList = Array.isArray(config.extends) ? config.extends : [config.extends];
+
+        for (const extend of extendList) {
+            if (extend.startsWith("eslint:") || extend.startsWith("plugin:")) {
+                continue;
+            }
+            const moduleName = naming.normalizePackageName(extend, "eslint-config");
+
+            modules[moduleName] = "latest";
+            Object.assign(
+                modules,
+                getPeerDependencies(`${moduleName}@latest`)
+            );
+        }
     }
 
     if (installESLint === false) {
diff --git a/lib/linter/linter.js b/lib/linter/linter.js
index 632470697..a49d85085 100644
--- a/lib/linter/linter.js
+++ b/lib/linter/linter.js
@@ -262,7 +262,7 @@ function createDisableDirectives(options) {
  */
 function getDirectiveComments(filename, ast, ruleMapper) {
     const configuredRules = {};
-    const enabledGlobals = {};
+    const enabledGlobals = Object.create(null);
     const exportedVariables = {};
     const problems = [];
     const disableDirectives = [];
diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js
index 98fc49654..6c3198b5f 100644
--- a/lib/rules/no-extra-parens.js
+++ b/lib/rules/no-extra-parens.js
@@ -81,6 +81,8 @@ module.exports = {
         const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" });
         const PRECEDENCE_OF_UPDATE_EXPR = precedence({ type: "UpdateExpression" });
 
+        let reportsBuffer;
+
         /**
          * Determines if this rule should be enforced for a node given the current configuration.
          * @param {ASTNode} node - The node to be checked.
@@ -316,19 +318,33 @@ module.exports = {
                 }
             }
 
-            context.report({
-                node,
-                loc: leftParenToken.loc.start,
-                messageId: "unexpected",
-                fix(fixer) {
-                    const parenthesizedSource = sourceCode.text.slice(leftParenToken.range[1], rightParenToken.range[0]);
+            /**
+             * Finishes reporting
+             * @returns {void}
+             * @private
+             */
+            function finishReport() {
+                context.report({
+                    node,
+                    loc: leftParenToken.loc.start,
+                    messageId: "unexpected",
+                    fix(fixer) {
+                        const parenthesizedSource = sourceCode.text.slice(leftParenToken.range[1], rightParenToken.range[0]);
+
+                        return fixer.replaceTextRange([
+                            leftParenToken.range[0],
+                            rightParenToken.range[1]
+                        ], (requiresLeadingSpace(node) ? " " : "") + parenthesizedSource + (requiresTrailingSpace(node) ? " " : ""));
+                    }
+                });
+            }
 
-                    return fixer.replaceTextRange([
-                        leftParenToken.range[0],
-                        rightParenToken.range[1]
-                    ], (requiresLeadingSpace(node) ? " " : "") + parenthesizedSource + (requiresTrailingSpace(node) ? " " : ""));
-                }
-            });
+            if (reportsBuffer) {
+                reportsBuffer.reports.push({ node, finishReport });
+                return;
+            }
+
+            finishReport();
         }
 
         /**
@@ -498,6 +514,126 @@ module.exports = {
             }
         }
 
+        /**
+         * Finds the path from the given node to the specified ancestor.
+         * @param {ASTNode} node First node in the path.
+         * @param {ASTNode} ancestor Last node in the path.
+         * @returns {ASTNode[]} Path, including both nodes.
+         * @throws {Error} If the given node does not have the specified ancestor.
+         */
+        function pathToAncestor(node, ancestor) {
+            const path = [node];
+            let currentNode = node;
+
+            while (currentNode !== ancestor) {
+
+                currentNode = currentNode.parent;
+
+                /* istanbul ignore if */
+                if (currentNode === null) {
+                    throw new Error("Nodes are not in the ancestor-descendant relationship.");
+                }
+
+                path.push(currentNode);
+            }
+
+            return path;
+        }
+
+        /**
+         * Finds the path from the given node to the specified descendant.
+         * @param {ASTNode} node First node in the path.
+         * @param {ASTNode} descendant Last node in the path.
+         * @returns {ASTNode[]} Path, including both nodes.
+         * @throws {Error} If the given node does not have the specified descendant.
+         */
+        function pathToDescendant(node, descendant) {
+            return pathToAncestor(descendant, node).reverse();
+        }
+
+        /**
+         * Checks whether the syntax of the given ancestor of an 'in' expression inside a for-loop initializer
+         * is preventing the 'in' keyword from being interpreted as a part of an ill-formed for-in loop.
+         *
+         * @param {ASTNode} node Ancestor of an 'in' expression.
+         * @param {ASTNode} child Child of the node, ancestor of the same 'in' expression or the 'in' expression itself.
+         * @returns {boolean} True if the keyword 'in' would be interpreted as the 'in' operator, without any parenthesis.
+         */
+        function isSafelyEnclosingInExpression(node, child) {
+            switch (node.type) {
+                case "ArrayExpression":
+                case "ArrayPattern":
+                case "BlockStatement":
+                case "ObjectExpression":
+                case "ObjectPattern":
+                case "TemplateLiteral":
+                    return true;
+                case "ArrowFunctionExpression":
+                case "FunctionExpression":
+                    return node.params.includes(child);
+                case "CallExpression":
+                case "NewExpression":
+                    return node.arguments.includes(child);
+                case "MemberExpression":
+                    return node.computed && node.property === child;
+                case "ConditionalExpression":
+                    return node.consequent === child;
+                default:
+                    return false;
+            }
+        }
+
+        /**
+         * Starts a new reports buffering. Warnings will be stored in a buffer instead of being reported immediately.
+         * An additional logic that requires multiple nodes (e.g. a whole subtree) may dismiss some of the stored warnings.
+         *
+         * @returns {void}
+         */
+        function startNewReportsBuffering() {
+            reportsBuffer = {
+                upper: reportsBuffer,
+                inExpressionNodes: [],
+                reports: []
+            };
+        }
+
+        /**
+         * Ends the current reports buffering.
+         * @returns {void}
+         */
+        function endCurrentReportsBuffering() {
+            const { upper, inExpressionNodes, reports } = reportsBuffer;
+
+            if (upper) {
+                upper.inExpressionNodes.push(...inExpressionNodes);
+                upper.reports.push(...reports);
+            } else {
+
+                // flush remaining reports
+                reports.forEach(({ finishReport }) => finishReport());
+            }
+
+            reportsBuffer = upper;
+        }
+
+        /**
+         * Checks whether the given node is in the current reports buffer.
+         * @param {ASTNode} node Node to check.
+         * @returns {boolean} True if the node is in the current buffer, false otherwise.
+         */
+        function isInCurrentReportsBuffer(node) {
+            return reportsBuffer.reports.some(r => r.node === node);
+        }
+
+        /**
+         * Removes the given node from the current reports buffer.
+         * @param {ASTNode} node Node to remove.
+         * @returns {void}
+         */
+        function removeFromCurrentReportsBuffer(node) {
+            reportsBuffer.reports = reportsBuffer.reports.filter(r => r.node !== node);
+        }
+
         return {
             ArrayExpression(node) {
                 node.elements
@@ -540,7 +676,14 @@ module.exports = {
                 }
             },
 
-            BinaryExpression: checkBinaryLogical,
+            BinaryExpression(node) {
+                if (reportsBuffer && node.operator === "in") {
+                    reportsBuffer.inExpressionNodes.push(node);
+                }
+
+                checkBinaryLogical(node);
+            },
+
             CallExpression: checkCallNew,
 
             ConditionalExpression(node) {
@@ -602,10 +745,6 @@ module.exports = {
             },
 
             ForStatement(node) {
-                if (node.init && hasExcessParens(node.init)) {
-                    report(node.init);
-                }
-
                 if (node.test && hasExcessParens(node.test) && !isCondAssignException(node)) {
                     report(node.test);
                 }
@@ -613,6 +752,81 @@ module.exports = {
                 if (node.update && hasExcessParens(node.update)) {
                     report(node.update);
                 }
+
+                if (node.init) {
+                    startNewReportsBuffering();
+
+                    if (hasExcessParens(node.init)) {
+                        report(node.init);
+                    }
+                }
+            },
+
+            "ForStatement > *.init:exit"(node) {
+
+                /*
+                 * Removing parentheses around `in` expressions might change semantics and cause errors.
+                 *
+                 * For example, this valid for loop:
+                 *      for (let a = (b in c); ;);
+                 * after removing parentheses would be treated as an invalid for-in loop:
+                 *      for (let a = b in c; ;);
+                 */
+
+                if (reportsBuffer.reports.length) {
+                    reportsBuffer.inExpressionNodes.forEach(inExpressionNode => {
+                        const path = pathToDescendant(node, inExpressionNode);
+                        let nodeToExclude;
+
+                        for (let i = 0; i < path.length; i++) {
+                            const pathNode = path[i];
+
+                            if (i < path.length - 1) {
+                                const nextPathNode = path[i + 1];
+
+                                if (isSafelyEnclosingInExpression(pathNode, nextPathNode)) {
+
+                                    // The 'in' expression in safely enclosed by the syntax of its ancestor nodes (e.g. by '{}' or '[]').
+                                    return;
+                                }
+                            }
+
+                            if (isParenthesised(pathNode)) {
+                                if (isInCurrentReportsBuffer(pathNode)) {
+
+                                    // This node was supposed to be reported, but parentheses might be necessary.
+
+                                    if (isParenthesisedTwice(pathNode)) {
+
+                                        /*
+                                         * This node is parenthesised twice, it certainly has at least one pair of `extra` parentheses.
+                                         * If the --fix option is on, the current fixing iteration will remove only one pair of parentheses.
+                                         * The remaining pair is safely enclosing the 'in' expression.
+                                         */
+                                        return;
+                                    }
+
+                                    // Exclude the outermost node only.
+                                    if (!nodeToExclude) {
+                                        nodeToExclude = pathNode;
+                                    }
+
+                                    // Don't break the loop here, there might be some safe nodes or parentheses that will stay inside.
+
+                                } else {
+
+                                    // This node will stay parenthesised, the 'in' expression in safely enclosed by '()'.
+                                    return;
+                                }
+                            }
+                        }
+
+                        // Exclude the node from the list (i.e. treat parentheses as necessary)
+                        removeFromCurrentReportsBuffer(nodeToExclude);
+                    });
+                }
+
+                endCurrentReportsBuffering();
             },
 
             IfStatement(node) {
@@ -664,6 +878,16 @@ module.exports = {
                     }).forEach(property => report(property.value));
             },
 
+            Property(node) {
+                if (node.computed) {
+                    const { key } = node;
+
+                    if (key && hasExcessParens(key) && precedence(key) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
+                        report(key);
+                    }
+                }
+            },
+
             ReturnStatement(node) {
                 const returnToken = sourceCode.getFirstToken(node);
 
diff --git a/lib/rules/no-param-reassign.js b/lib/rules/no-param-reassign.js
index 880ff93cb..9b8c828d2 100644
--- a/lib/rules/no-param-reassign.js
+++ b/lib/rules/no-param-reassign.js
@@ -67,7 +67,8 @@ module.exports = {
             let node = reference.identifier;
             let parent = node.parent;
 
-            while (parent && !stopNodePattern.test(parent.type)) {
+            while (parent && (!stopNodePattern.test(parent.type) ||
+                    parent.type === "ForInStatement" || parent.type === "ForOfStatement")) {
                 switch (parent.type) {
 
                     // e.g. foo.a = 0;
@@ -85,6 +86,16 @@ module.exports = {
                         }
                         break;
 
+                    // e.g. for (foo.a in b) {}
+                    case "ForInStatement":
+                    case "ForOfStatement":
+                        if (parent.left === node) {
+                            return true;
+                        }
+
+                        // this is a stop node for parent.right and parent.body
+                        return false;
+
                     // EXCLUDES: e.g. cache.get(foo.a).b = 0;
                     case "CallExpression":
                         if (parent.callee !== node) {
diff --git a/lib/rules/prefer-const.js b/lib/rules/prefer-const.js
index 68c07da4e..854da310e 100644
--- a/lib/rules/prefer-const.js
+++ b/lib/rules/prefer-const.js
@@ -420,8 +420,9 @@ module.exports = {
 
                 let shouldFix = varDeclParent &&
 
-                    // Don't do a fix unless the variable is initialized (or it's in a for-in or for-of loop)
-                    (varDeclParent.parent.type === "ForInStatement" || varDeclParent.parent.type === "ForOfStatement" || varDeclParent.declarations[0].init) &&
+                    // Don't do a fix unless all variables in the declarations are initialized (or it's in a for-in or for-of loop)
+                    (varDeclParent.parent.type === "ForInStatement" || varDeclParent.parent.type === "ForOfStatement" ||
+                        varDeclParent.declarations.every(declaration => declaration.init)) &&
 
                     /*
                      * If options.destructuring is "all", then this warning will not occur unless
@@ -450,7 +451,12 @@ module.exports = {
                         node,
                         messageId: "useConst",
                         data: node,
-                        fix: shouldFix ? fixer => fixer.replaceText(sourceCode.getFirstToken(varDeclParent), "const") : null
+                        fix: shouldFix
+                            ? fixer => fixer.replaceText(
+                                sourceCode.getFirstToken(varDeclParent, t => t.value === varDeclParent.kind),
+                                "const"
+                            )
+                            : null
                     });
                 });
             }
diff --git a/lib/cli-engine/naming.js b/lib/shared/naming.js
similarity index 100%
rename from lib/cli-engine/naming.js
rename to lib/shared/naming.js
diff --git a/package.json b/package.json
index 068b4e185..46cb03866 100644
--- a/package.json
+++ b/package.json
@@ -67,7 +67,7 @@
     "js-yaml": "^3.13.1",
     "json-stable-stringify-without-jsonify": "^1.0.1",
     "levn": "^0.3.0",
-    "lodash": "^4.17.11",
+    "lodash": "^4.17.14",
     "minimatch": "^3.0.4",
     "mkdirp": "^0.5.1",
     "natural-compare": "^1.4.0",


Test patch:
diff --git a/tests/lib/init/config-initializer.js b/tests/lib/init/config-initializer.js
index 68cd06e1d..4ee6ff6b7 100644
--- a/tests/lib/init/config-initializer.js
+++ b/tests/lib/init/config-initializer.js
@@ -306,6 +306,17 @@ describe("configInitializer", () => {
                     });
                 });
             });
+
+            it("should support the standard style guide with Vue.js", () => {
+                const config = {
+                    plugins: ["vue"],
+                    extends: ["plugin:vue/essential", "standard"]
+                };
+                const modules = init.getModulesList(config);
+
+                assert.include(modules, "eslint-plugin-vue@latest");
+                assert.include(modules, "eslint-config-standard@latest");
+            });
         });
 
         describe("auto", () => {
diff --git a/tests/lib/linter/linter.js b/tests/lib/linter/linter.js
index da052b339..3cfef3bbe 100644
--- a/tests/lib/linter/linter.js
+++ b/tests/lib/linter/linter.js
@@ -1208,6 +1208,16 @@ describe("Linter", () => {
         });
     });
 
+    describe("when evaluating code containing a /*global */ block with specific variables", () => {
+        const code = "/* global toString hasOwnProperty valueOf: true */";
+
+        it("should not throw an error if comment block has global variables which are Object.prototype contains", () => {
+            const config = { rules: { checker: "error" } };
+
+            linter.verify(code, config);
+        });
+    });
+
     describe("when evaluating code containing /*eslint-env */ block", () => {
         it("variables should be available in global scope", () => {
             const code = `/*${ESLINT_ENV} node*/ function f() {} /*${ESLINT_ENV} browser, foo*/`;
diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js
index 69d814e7d..7be7d6e9e 100644
--- a/tests/lib/rules/no-extra-parens.js
+++ b/tests/lib/rules/no-extra-parens.js
@@ -148,7 +148,18 @@ ruleTester.run("no-extra-parens", rule, {
         "var a = (b, c);",
         "[]",
         "[a, b]",
+        "!{a}",
         "!{a: 0, b: 1}",
+        "!{[a]:0}",
+        "!{[(a, b)]:0}",
+        "!{a, ...b}",
+        "const {a} = {}",
+        "const {a:b} = {}",
+        "const {a:b=1} = {}",
+        "const {[a]:b} = {}",
+        "const {[a]:b=1} = {}",
+        "const {[(a, b)]:c} = {}",
+        "const {a, ...b} = {}",
 
         // ExpressionStatement restricted productions
         "({});",
@@ -466,7 +477,36 @@ ruleTester.run("no-extra-parens", rule, {
         "for ((let) in foo);",
         "for ((let[foo]) in bar);",
         "for ((let)[foo] in bar);",
-        "for ((let[foo].bar) in baz);"
+        "for ((let[foo].bar) in baz);",
+
+        // https://github.com/eslint/eslint/issues/11706 (also in invalid[])
+        "for (let a = (b in c); ;);",
+        "for (let a = (b && c in d); ;);",
+        "for (let a = (b in c && d); ;);",
+        "for (let a = (b => b in c); ;);",
+        "for (let a = b => (b in c); ;);",
+        "for (let a = (b in c in d); ;);",
+        "for (let a = (b in c), d = (e in f); ;);",
+        "for (let a = (b => c => b in c); ;);",
+        "for (let a = (b && c && d in e); ;);",
+        "for (let a = b && (c in d); ;);",
+        "for (let a = (b in c) && (d in e); ;);",
+        "for ((a in b); ;);",
+        "for (a = (b in c); ;);",
+        "for ((a in b && c in d && e in f); ;);",
+        "for (let a = [] && (b in c); ;);",
+        "for (let a = (b in [c]); ;);",
+        "for (let a = b => (c in d); ;);",
+        "for (let a = (b in c) ? d : e; ;);",
+        "for (let a = (b in c ? d : e); ;);",
+        "for (let a = b ? c : (d in e); ;);",
+        "for (let a = (b in c), d = () => { for ((e in f);;); for ((g in h);;); }; ;); for((i in j); ;);",
+
+        // https://github.com/eslint/eslint/issues/11706 regression tests (also in invalid[])
+        "for (let a = b; a; a); a; a;",
+        "for (a; a; a); a; a;",
+        "for (; a; a); a; a;",
+        "for (let a = (b && c) === d; ;);"
     ],
 
     invalid: [
@@ -485,6 +525,34 @@ ruleTester.run("no-extra-parens", rule, {
         invalid("do; while((0))", "do; while(0)", "Literal"),
         invalid("for(a in (0));", "for(a in 0);", "Literal"),
         invalid("for(a of (0));", "for(a of 0);", "Literal", 1),
+        invalid("const foo = {[(a)]:1}", "const foo = {[a]:1}", "Identifier", 1),
+        invalid("const foo = {[(a=b)]:1}", "const foo = {[a=b]:1}", "AssignmentExpression", 1),
+        invalid("const foo = {*[(Symbol.iterator)]() {}}", "const foo = {*[Symbol.iterator]() {}}", "MemberExpression", 1),
+        invalid("const foo = { get [(a)]() {}}", "const foo = { get [a]() {}}", "Identifier", 1),
+        invalid("const foo = {[(a+b)]:c, d}", "const foo = {[a+b]:c, d}", "BinaryExpression", 1),
+        invalid("const foo = {a, [(b+c)]:d, e}", "const foo = {a, [b+c]:d, e}", "BinaryExpression", 1),
+        invalid("const foo = {[(a+b)]:c, d:e}", "const foo = {[a+b]:c, d:e}", "BinaryExpression", 1),
+        invalid("const foo = {a:b, [(c+d)]:e, f:g}", "const foo = {a:b, [c+d]:e, f:g}", "BinaryExpression", 1),
+        invalid("const foo = {[(a+b)]:c, [d]:e}", "const foo = {[a+b]:c, [d]:e}", "BinaryExpression", 1),
+        invalid("const foo = {[a]:b, [(c+d)]:e, [f]:g}", "const foo = {[a]:b, [c+d]:e, [f]:g}", "BinaryExpression", 1),
+        invalid("const foo = {[(a+b)]:c, [(d,e)]:f}", "const foo = {[a+b]:c, [(d,e)]:f}", "BinaryExpression", 1),
+        invalid("const foo = {[(a,b)]:c, [(d+e)]:f, [(g,h)]:e}", "const foo = {[(a,b)]:c, [d+e]:f, [(g,h)]:e}", "BinaryExpression", 1),
+        invalid("const foo = {a, b:c, [(d+e)]:f, [(g,h)]:i, [j]:k}", "const foo = {a, b:c, [d+e]:f, [(g,h)]:i, [j]:k}", "BinaryExpression", 1),
+        invalid("const foo = {[a+(b*c)]:d}", "const foo = {[a+b*c]:d}", "BinaryExpression", 1),
+        invalid("const foo = {[(a, (b+c))]:d}", "const foo = {[(a, b+c)]:d}", "BinaryExpression", 1),
+        invalid("const {[(a)]:b} = {}", "const {[a]:b} = {}", "Identifier", 1),
+        invalid("const {[(a=b)]:c=1} = {}", "const {[a=b]:c=1} = {}", "AssignmentExpression", 1),
+        invalid("const {[(a+b)]:c, d} = {}", "const {[a+b]:c, d} = {}", "BinaryExpression", 1),
+        invalid("const {a, [(b+c)]:d, e} = {}", "const {a, [b+c]:d, e} = {}", "BinaryExpression", 1),
+        invalid("const {[(a+b)]:c, d:e} = {}", "const {[a+b]:c, d:e} = {}", "BinaryExpression", 1),
+        invalid("const {a:b, [(c+d)]:e, f:g} = {}", "const {a:b, [c+d]:e, f:g} = {}", "BinaryExpression", 1),
+        invalid("const {[(a+b)]:c, [d]:e} = {}", "const {[a+b]:c, [d]:e} = {}", "BinaryExpression", 1),
+        invalid("const {[a]:b, [(c+d)]:e, [f]:g} = {}", "const {[a]:b, [c+d]:e, [f]:g} = {}", "BinaryExpression", 1),
+        invalid("const {[(a+b)]:c, [(d,e)]:f} = {}", "const {[a+b]:c, [(d,e)]:f} = {}", "BinaryExpression", 1),
+        invalid("const {[(a,b)]:c, [(d+e)]:f, [(g,h)]:e} = {}", "const {[(a,b)]:c, [d+e]:f, [(g,h)]:e} = {}", "BinaryExpression", 1),
+        invalid("const {a, b:c, [(d+e)]:f, [(g,h)]:i, [j]:k} = {}", "const {a, b:c, [d+e]:f, [(g,h)]:i, [j]:k} = {}", "BinaryExpression", 1),
+        invalid("const {[a+(b*c)]:d} = {}", "const {[a+b*c]:d} = {}", "BinaryExpression", 1),
+        invalid("const {[(a, (b+c))]:d} = {}", "const {[(a, b+c)]:d} = {}", "BinaryExpression", 1),
         invalid(
             "var foo = (function*() { if ((yield foo())) { return; } }())",
             "var foo = (function*() { if (yield foo()) { return; } }())",
@@ -1105,6 +1173,624 @@ ruleTester.run("no-extra-parens", rule, {
             "(let)",
             "Identifier",
             1
-        )
+        ),
+
+        // https://github.com/eslint/eslint/issues/11706 (also in valid[])
+        {
+            code: "for ((a = (b in c)); ;);",
+            output: "for ((a = b in c); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = ((b in c) && (d in e)); ;);",
+            output: "for (let a = (b in c && d in e); ;);",
+            errors: Array(2).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (let a = ((b in c) in d); ;);",
+            output: "for (let a = (b in c in d); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = (b && (c in d)), e = (f in g); ;);",
+            output: "for (let a = (b && c in d), e = (f in g); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = (b + c), d = (e in f); ;);",
+            output: "for (let a = b + c, d = (e in f); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = [(b in c)]; ;);",
+            output: "for (let a = [b in c]; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = [b, (c in d)]; ;);",
+            output: "for (let a = [b, c in d]; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = ([b in c]); ;);",
+            output: "for (let a = [b in c]; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = ([b, c in d]); ;);",
+            output: "for (let a = [b, c in d]; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for ((a = [b in c]); ;);",
+            output: "for (a = [b in c]; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = [b && (c in d)]; ;);",
+            output: "for (let a = [b && c in d]; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = [(b && c in d)]; ;);",
+            output: "for (let a = [b && c in d]; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = ([b && c in d]); ;);",
+            output: "for (let a = [b && c in d]; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for ((a = [b && c in d]); ;);",
+            output: "for (a = [b && c in d]; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for ([(a in b)]; ;);",
+            output: "for ([a in b]; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (([a in b]); ;);",
+            output: "for ([a in b]; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = [(b in c)], d = (e in f); ;);",
+            output: "for (let a = [b in c], d = (e in f); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let [a = b && (c in d)] = []; ;);",
+            output: "for (let [a = b && c in d] = []; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = () => { (b in c) }; ;);",
+            output: "for (let a = () => { b in c }; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = () => { a && (b in c) }; ;);",
+            output: "for (let a = () => { a && b in c }; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = function () { (b in c) }; ;);",
+            output: "for (let a = function () { b in c }; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = { a: (b in c) }; ;);",
+            output: "for (let a = { a: b in c }; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = { a: b && (c in d) }; ;);",
+            output: "for (let a = { a: b && c in d }; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let { a = b && (c in d) } = {}; ;);",
+            output: "for (let { a = b && c in d } = {}; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let { a: { b = c && (d in e) } } = {}; ;);",
+            output: "for (let { a: { b = c && d in e } } = {}; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = `${a && (b in c)}`; ;);",
+            output: "for (let a = `${a && b in c}`; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = (b = c && (d in e)) => {}; ;);",
+            output: "for (let a = (b = c && d in e) => {}; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = (b, c = d && (e in f)) => {}; ;);",
+            output: "for (let a = (b, c = d && e in f) => {}; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = function (b = c && (d in e)) {}; ;);",
+            output: "for (let a = function (b = c && d in e) {}; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = function (b, c = d && (e in f)) {}; ;);",
+            output: "for (let a = function (b, c = d && e in f) {}; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = b((c in d)); ;);",
+            output: "for (let a = b(c in d); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = b(c, (d in e)); ;);",
+            output: "for (let a = b(c, d in e); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = b(c && (d in e)); ;);",
+            output: "for (let a = b(c && d in e); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = b(c, d && (e in f)); ;);",
+            output: "for (let a = b(c, d && e in f); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = new b((c in d)); ;);",
+            output: "for (let a = new b(c in d); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = new b(c, (d in e)); ;);",
+            output: "for (let a = new b(c, d in e); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = new b(c && (d in e)); ;);",
+            output: "for (let a = new b(c && d in e); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = new b(c, d && (e in f)); ;);",
+            output: "for (let a = new b(c, d && e in f); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = b[(c in d)]; ;);",
+            output: "for (let a = b[c in d]; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = b[c && (d in e)]; ;);",
+            output: "for (let a = b[c && d in e]; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = b ? (c in d) : e; ;);",
+            output: "for (let a = b ? c in d : e; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = b ? c && (d in e) : f; ;);",
+            output: "for (let a = b ? c && d in e : f; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (a ? b && (c in d) : e; ;);",
+            output: "for (a ? b && c in d : e; ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = ((b in c)); ;);",
+            output: "for (let a = (b in c); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (((a in b)); ;);",
+            output: "for ((a in b); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (((a && b in c && d)); ;);",
+            output: "for ((a && b in c && d); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = (!(b in c)); ;);",
+            output: "for (let a = !(b in c); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = (!(b && c in d)); ;);",
+            output: "for (let a = !(b && c in d); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = !((b in c) && (d in e)); ;);",
+            output: "for (let a = !(b in c && d in e); ;);",
+            errors: Array(2).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (let a = (x && (b in c)), d = () => { for ((e in f); ;); for ((g in h); ;); }; ;); for((i in j); ;);",
+            output: "for (let a = (x && b in c), d = () => { for ((e in f); ;); for ((g in h); ;); }; ;); for((i in j); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = (b in c), d = () => { for ((x && (e in f)); ;); for ((g in h); ;); }; ;); for((i in j); ;);",
+            output: "for (let a = (b in c), d = () => { for ((x && e in f); ;); for ((g in h); ;); }; ;); for((i in j); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = (b in c), d = () => { for ((e in f); ;); for ((x && (g in h)); ;); }; ;); for((i in j); ;);",
+            output: "for (let a = (b in c), d = () => { for ((e in f); ;); for ((x && g in h); ;); }; ;); for((i in j); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = (b in c), d = () => { for ((e in f); ;); for ((g in h); ;); }; ;); for((x && (i in j)); ;);",
+            output: "for (let a = (b in c), d = () => { for ((e in f); ;); for ((g in h); ;); }; ;); for((x && i in j); ;);",
+            errors: [
+                {
+                    messageId: "unexpected"
+                }
+            ]
+        },
+        {
+            code: "for (let a = (x && (b in c)), d = () => { for ((e in f); ;); for ((y && (g in h)); ;); }; ;); for((i in j); ;);",
+            output: "for (let a = (x && b in c), d = () => { for ((e in f); ;); for ((y && g in h); ;); }; ;); for((i in j); ;);",
+            errors: Array(2).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (let a = (x && (b in c)), d = () => { for ((y && (e in f)); ;); for ((z && (g in h)); ;); }; ;); for((w && (i in j)); ;);",
+            output: "for (let a = (x && b in c), d = () => { for ((y && e in f); ;); for ((z && g in h); ;); }; ;); for((w && i in j); ;);",
+            errors: Array(4).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+
+        // TODO: Add '`in` inside a for-loop' first level tests for AssignmentPattern (param, obj, ary) and TemplateLiteral when these become supported.
+
+        // https://github.com/eslint/eslint/issues/11706 regression tests (also in valid[])
+        {
+            code: "for (let a = (b); a > (b); a = (b)) a = (b); a = (b);",
+            output: "for (let a = b; a > b; a = b) a = b; a = b;",
+            errors: Array(5).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for ((a = b); (a > b); (a = b)) (a = b); (a = b);",
+            output: "for (a = b; a > b; a = b) a = b; a = b;",
+            errors: Array(5).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (let a = b; a > (b); a = (b)) a = (b); a = (b);",
+            output: "for (let a = b; a > b; a = b) a = b; a = b;",
+            errors: Array(4).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (let a = b; (a > b); (a = b)) (a = b); (a = b);",
+            output: "for (let a = b; a > b; a = b) a = b; a = b;",
+            errors: Array(4).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (; a > (b); a = (b)) a = (b); a = (b);",
+            output: "for (; a > b; a = b) a = b; a = b;",
+            errors: Array(4).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (; (a > b); (a = b)) (a = b); (a = b);",
+            output: "for (; a > b; a = b) a = b; a = b;",
+            errors: Array(4).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (let a = (b); a = (b in c); a = (b in c)) a = (b in c); a = (b in c);",
+            output: "for (let a = b; a = b in c; a = b in c) a = b in c; a = b in c;",
+            errors: Array(5).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (let a = (b); (a in b); (a in b)) (a in b); (a in b);",
+            output: "for (let a = b; a in b; a in b) a in b; a in b;",
+            errors: Array(5).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (let a = b; a = (b in c); a = (b in c)) a = (b in c); a = (b in c);",
+            output: "for (let a = b; a = b in c; a = b in c) a = b in c; a = b in c;",
+            errors: Array(4).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (let a = b; (a in b); (a in b)) (a in b); (a in b);",
+            output: "for (let a = b; a in b; a in b) a in b; a in b;",
+            errors: Array(4).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (; a = (b in c); a = (b in c)) a = (b in c); a = (b in c);",
+            output: "for (; a = b in c; a = b in c) a = b in c; a = b in c;",
+            errors: Array(4).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (; (a in b); (a in b)) (a in b); (a in b);",
+            output: "for (; a in b; a in b) a in b; a in b;",
+            errors: Array(4).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        },
+        {
+            code: "for (let a = (b + c), d = () => { for ((e + f); ;); for ((g + h); ;); }; ;); for((i + j); ;);",
+            output: "for (let a = b + c, d = () => { for (e + f; ;); for (g + h; ;); }; ;); for(i + j; ;);",
+            errors: Array(4).fill(
+                {
+                    messageId: "unexpected"
+                }
+            )
+        }
     ]
 });
diff --git a/tests/lib/rules/no-param-reassign.js b/tests/lib/rules/no-param-reassign.js
index dfff7f26a..fec1e4168 100644
--- a/tests/lib/rules/no-param-reassign.js
+++ b/tests/lib/rules/no-param-reassign.js
@@ -21,7 +21,11 @@ ruleTester.run("no-param-reassign", rule, {
 
     valid: [
         "function foo(a) { var b = a; }",
+        "function foo(a) { for (b in a); }",
+        { code: "function foo(a) { for (b of a); }", parserOptions: { ecmaVersion: 6 } },
         "function foo(a) { a.prop = 'value'; }",
+        "function foo(a) { for (a.prop in obj); }",
+        { code: "function foo(a) { for (a.prop of arr); }", parserOptions: { ecmaVersion: 6 } },
         "function foo(a) { (function() { var a = 12; a++; })(); }",
         "function foo() { someGlobal = 13; }",
         { code: "function foo() { someGlobal = 13; }", globals: { someGlobal: false } },
@@ -37,6 +41,8 @@ ruleTester.run("no-param-reassign", rule, {
         { code: "function foo(a) { a.b = 0; }", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }] },
         { code: "function foo(a) { ++a.b; }", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }] },
         { code: "function foo(a) { delete a.b; }", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }] },
+        { code: "function foo(a) { for (a.b in obj); }", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }] },
+        { code: "function foo(a) { for (a.b of arr); }", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }], parserOptions: { ecmaVersion: 6 } },
         { code: "function foo(a, z) { a.b = 0; x.y = 0; }", options: [{ props: true, ignorePropertyModificationsFor: ["a", "x"] }] },
         { code: "function foo(a) { a.b.c = 0;}", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }] },
         {
@@ -53,6 +59,33 @@ ruleTester.run("no-param-reassign", rule, {
             code: "function foo(a) { ({...a.b} = obj); }",
             options: [{ props: false }],
             parserOptions: { ecmaVersion: 2018 }
+        },
+        {
+            code: "function foo(a) { for (obj[a.b] in obj); }",
+            options: [{ props: true }]
+        },
+        {
+            code: "function foo(a) { for (obj[a.b] of arr); }",
+            options: [{ props: true }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "function foo(a) { for (bar in a.b); }",
+            options: [{ props: true }]
+        },
+        {
+            code: "function foo(a) { for (bar of a.b); }",
+            options: [{ props: true }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "function foo(a) { for (bar in baz) a.b; }",
+            options: [{ props: true }]
+        },
+        {
+            code: "function foo(a) { for (bar of baz) a.b; }",
+            options: [{ props: true }],
+            parserOptions: { ecmaVersion: 6 }
         }
     ],
 
@@ -68,6 +101,8 @@ ruleTester.run("no-param-reassign", rule, {
         { code: "function foo([, {bar}]) { bar = 13; }", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Assignment to function parameter 'bar'." }] },
         { code: "function foo(bar) { ({bar} = {}); }", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Assignment to function parameter 'bar'." }] },
         { code: "function foo(bar) { ({x: [, bar = 0]} = {}); }", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Assignment to function parameter 'bar'." }] },
+        { code: "function foo(bar) { for (bar in baz); }", errors: [{ message: "Assignment to function parameter 'bar'." }] },
+        { code: "function foo(bar) { for (bar of baz); }", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Assignment to function parameter 'bar'." }] },
 
         {
             code: "function foo(bar) { bar.a = 0; }",
@@ -89,6 +124,17 @@ ruleTester.run("no-param-reassign", rule, {
             options: [{ props: true }],
             errors: [{ message: "Assignment to property of function parameter 'bar'." }]
         },
+        {
+            code: "function foo(bar) { for (bar.a in {}); }",
+            options: [{ props: true }],
+            errors: [{ message: "Assignment to property of function parameter 'bar'." }]
+        },
+        {
+            code: "function foo(bar) { for (bar.a of []); }",
+            options: [{ props: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{ message: "Assignment to property of function parameter 'bar'." }]
+        },
         {
             code: "function foo(bar) { (bar ? bar : [])[0] = 1; }",
             options: [{ props: true }],
@@ -139,6 +185,18 @@ ruleTester.run("no-param-reassign", rule, {
             options: [{ props: true }],
             parserOptions: { ecmaVersion: 2018 },
             errors: [{ message: "Assignment to property of function parameter 'a'." }]
+        },
+        {
+            code: "function foo(a) { for ({bar: a.b} in {}); }",
+            options: [{ props: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{ message: "Assignment to property of function parameter 'a'." }]
+        },
+        {
+            code: "function foo(a) { for ([a.b] of []); }",
+            options: [{ props: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{ message: "Assignment to property of function parameter 'a'." }]
         }
     ]
 });
diff --git a/tests/lib/rules/prefer-const.js b/tests/lib/rules/prefer-const.js
index d8c018bdf..d92398533 100644
--- a/tests/lib/rules/prefer-const.js
+++ b/tests/lib/rules/prefer-const.js
@@ -508,7 +508,25 @@ ruleTester.run("prefer-const", rule, {
                 { message: "'a' is never reassigned. Use 'const' instead.", type: "Identifier" },
                 { message: "'b' is never reassigned. Use 'const' instead.", type: "Identifier" }
             ]
-        }
+        },
 
+        // https://github.com/eslint/eslint/issues/11699
+        {
+            code: "let {a, b} = c, d;",
+            output: null,
+            errors: [
+                { messageId: "useConst", data: { name: "a" }, type: "Identifier" },
+                { messageId: "useConst", data: { name: "b" }, type: "Identifier" }
+            ]
+        },
+        {
+            code: "let {a, b, c} = {}, e, f;",
+            output: null,
+            errors: [
+                { messageId: "useConst", data: { name: "a" }, type: "Identifier" },
+                { messageId: "useConst", data: { name: "b" }, type: "Identifier" },
+                { messageId: "useConst", data: { name: "c" }, type: "Identifier" }
+            ]
+        }
     ]
 });
diff --git a/tests/lib/cli-engine/naming.js b/tests/lib/shared/naming.js
similarity index 97%
rename from tests/lib/cli-engine/naming.js
rename to tests/lib/shared/naming.js
index 3bc917baf..f6239dab1 100644
--- a/tests/lib/cli-engine/naming.js
+++ b/tests/lib/shared/naming.js
@@ -9,7 +9,7 @@
 
 const assert = require("chai").assert,
     leche = require("leche"),
-    naming = require("../../../lib/cli-engine/naming");
+    naming = require("../../../lib/shared/naming");
 
 //------------------------------------------------------------------------------
 // Tests