Repository: eslint/eslint (js)

Issue:
Error message about invalid config file does not mention which config file is invalid
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).

    This template is for bug reports. If you are reporting a bug, please continue on. If you are here for another reason, please see below:

    1. To propose a new rule: http://eslint.org/docs/developer-guide/contributing/new-rules
    2. To request a change: http://eslint.org/docs/developer-guide/contributing/changes
    3. If you have any questions, please stop by our chatroom: https://gitter.im/eslint/eslint

    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
-->

**Tell us about your environment**

* **ESLint Version:** 4.2.0
* **Node Version:** 8.1.4
* **npm Version:** 5.0.3

**What parser (default, Babel-ESLint, etc.) are you using?**

N/A

**Please show your full configuration:**

(see below)

**What did you do? Please include the actual source code causing the issue.**

My config file looks like this:

```yml
# .eslintrc.yml
extends:
  - ./foo.yml
  - ./bar.yml
```

`foo.yml` looks like this:

```yml
# foo.yml
env: {}
```

`bar.yml`, an invalid config file, looks like this:

```yml
# bar.yml
invalidProperty: 5
```

I have an `index.js` file that looks like this:
<!-- Paste the source code below: -->
```js
a;
```

I tried to lint `index.js`: `node_modules/.bin/eslint index.js`

**What did you expect to happen?**

I expected to get an error about the invalid config file, and I expected the error message to mention that the invalid config file is `bar.yml`.

**What actually happened? Please include the actual, raw output from ESLint.**

A config validation error was thrown, and the following output was produced:

```
[object Object]:
	ESLint configuration is invalid:
	- Unexpected top-level property "invalidProperty".

Referenced from: /path/to/.eslintrc.yml
Error: [object Object]:
	ESLint configuration is invalid:
	- Unexpected top-level property "invalidProperty".

Referenced from: /path/to/.eslintrc.yml
    at validateConfigSchema (/path/to/node_modules/eslint/lib/config/config-validator.js:192:15)
    at Object.validate (/path/to/node_modules/eslint/lib/config/config-validator.js:205:5)
    at loadFromDisk (/path/to/node_modules/eslint/lib/config/config-file.js:549:19)
    at load (/path/to/node_modules/eslint/lib/config/config-file.js:592:20)
    at configExtends.reduceRight (/path/to/node_modules/eslint/lib/config/config-file.js:421:36)
    at Array.reduceRight (native)
    at applyExtends (/path/to/node_modules/eslint/lib/config/config-file.js:405:28)
    at loadFromDisk (/path/to/node_modules/eslint/lib/config/config-file.js:556:22)
    at Object.load (/path/to/node_modules/eslint/lib/config/config-file.js:592:20)
    at Config.getLocalConfigHierarchy (/path/to/node_modules/eslint/lib/config.js:228:44)

```

The message does not mention `bar.yml` (although did does mention that the invalid config was referenced from `.eslintrc.yml`). This is probably because we used to assume that all config-loading errors were related to not being able to find the file, so it didn't make sense to repeat the filename before.

Also, the error message is printed twice. That part is probably a separate issue.

Pull request description:
Fix: include name of invalid config in validation messages (fixes #8963)

<!--
    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 (https://github.com/eslint/eslint/issues/8963)

<!--
    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 (http://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)**

Previously, if an ESLint configuration was invalid, the error message would not contain the filename of the offending config. Instead, the error message contained `[object Object]`, which was probably a bug. This commit updates the error message to clearly indicate which config file contains the error.

**Is there anything you'd like reviewers to focus on?**

Nothing in particular


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3866a56b3..5121911a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,32 @@
+v4.3.0 - July 21, 2017
+
+* 91dccdf Update: support more options in prefer-destructuring (#8796) (Victor Hom)
+* 3bebcfd Update: Support generator yields in no constant condition (#8762) (Victor Hom)
+* 96df8c9 Fix: Handle fixing objects containing comments (fixes #8484) (#8944) (Brian Schemp)
+* e39d41d Docs: Make `peerDependencies` package.json snippet valid JSON (#8971) (Sam Adams)
+* a5fd101 Fix: duplicated error message if a crash occurs (fixes #8964) (#8965) (Teddy Katz)
+* f8d122c Docs: trailing commas not allowed in json (#8969) (Scott Fletcher)
+* d09288a Chore: Use `output: null` to assert that a test case is not autofixed. (#8960) (薛定谔的猫)
+* e639358 Update: add question to confirm downgrade (fixes #8870) (#8911) (Toru Nagashima)
+* 601039d Docs: fix badge in eslint-config-eslint readme (#8954) (Teddy Katz)
+* 3c231fa Update: add enforceInMethodNames to no-underscore-dangle (fixes #7065) (#7234) (Gabriele Petronella)
+* 128591f Update: prefer-numeric-literals warns Number.parseInt (fixes #8913) (#8929) (Kevin Partington)
+* 846f8b1 Docs: Clarified that core PRs require issue in maintainer guide (#8927) (Kevin Partington)
+* 55bc35d Fix: Avoid shell mangling during eslint --init (#8936) (Anders Kaseorg)
+* 10c3d78 Chore: fix misleading `indent` test (#8925) (Teddy Katz)
+* fb8005d Update: no-restricted-globals custom error messages (fixes #8315) (#8932) (Kevin Partington)
+* a747b6f Chore: make minor improvements to `indent` internals (#8947) (Teddy Katz)
+* 1ea3723 Update: fix indentation of parenthesized MemberExpressions (fixes #8924) (#8928) (Teddy Katz)
+* 9abc6f7 Update: fix BinaryExpression indentation edge case (fixes #8914) (#8930) (Teddy Katz)
+* 0e90453 Docs: Fixing broken cyclomatic complexity link (fixes #8396) (#8937) (Chris Bargren)
+* a8a8350 Chore: improve performance of `indent` rule (#8905) (Teddy Katz)
+* 764b2a9 Chore: update header info in `indent` (#8926) (Teddy Katz)
+* 597c217 Fix: confusing error if plugins from config is not an array (#8888) (Calvin Freitas)
+* 3c1dd6d Docs: add description of no-sync `allowAtRootLevel` option (fixes #8902) (#8906) (Teddy Katz)
+* 933a9cf Chore: add a fuzzer to detect bugs in core rules (#8422) (Teddy Katz)
+* 45f8cd9 Docs: fix verifyAndFix result property name (#8903) (Tino Vyatkin)
+* 1a89e1c Docs: Fix always-multiline example in multiline-ternary docs (#8904) (Nathan Woltman)
+
 v4.2.0 - July 8, 2017
 
 * e0f0101 Update: fix indentation of nested function parameters (fixes #8892) (#8900) (Teddy Katz)
diff --git a/docs/developer-guide/shareable-configs.md b/docs/developer-guide/shareable-configs.md
index 23d6a3fef..58f36e904 100644
--- a/docs/developer-guide/shareable-configs.md
+++ b/docs/developer-guide/shareable-configs.md
@@ -33,7 +33,7 @@ Once your shareable config is ready, you can [publish to npm](https://docs.npmjs
 You should declare your dependency on eslint in `package.json` using the [peerDependencies](https://docs.npmjs.com/files/package.json#peerdependencies) field. The recommended way to declare a dependency for future proof compatibility is with the ">=" range syntax, using the lowest required eslint version. For example:
 
 ```
-peerDependencies: {
+"peerDependencies": {
     "eslint": ">= 3"
 }
 ```
diff --git a/docs/rules/prefer-destructuring.md b/docs/rules/prefer-destructuring.md
index 2925225d3..46a49393d 100644
--- a/docs/rules/prefer-destructuring.md
+++ b/docs/rules/prefer-destructuring.md
@@ -4,6 +4,18 @@ With JavaScript ES6, a new syntax was added for creating variables from an array
 
 ## Rule Details
 
+### Options
+
+This rule takes two sets of configuration objects. The first object parameter determines what types of destructuring the rule applies to.
+
+The two properties, `array` and `object`, can be used to turn on or off the destructuring requirement for each of those types independently. By default, both are true.
+
+Alternatively, you can use separate configurations for different assignment types. It accepts 2 other keys instead of `array` and `object`.
+
+One key is `VariableDeclarator` and the other is `AssignmentExpression`, which can be used to control the destructuring requirement for each of those types independently. Each property accepts an object that accepts two properties, `array` and `object`, which can be used to control the destructuring requirement for each of `array` and `object` independently for variable declarations and assignment expressions.  By default, `array` and `object` are set to true for both `VariableDeclarator` and `AssignmentExpression`.
+
+The rule has a second object with a single key, `enforceForRenamedProperties`, which determines whether the `object` destructuring applies to renamed variables.
+
 Examples of **incorrect** code for this rule:
 
 ```javascript
@@ -27,14 +39,6 @@ var { foo } = object;
 var foo = object.bar;
 ```
 
-### Options
-
-This rule takes two sets of configuration objects; the first controls the types that the rule is applied to, and the second controls the way those objects are evaluated.
-
-The first has two properties, `array` and `object`, which can be used to turn on or off the destructuring requirement for each of those types independently.  By default, both are `true`.
-
-The second has a single property, `enforceForRenamedProperties`, that controls whether or not the `object` destructuring rules are applied in cases where the variable requires the property being access to be renamed.
-
 Examples of **incorrect** code when `enforceForRenamedProperties` is enabled:
 
 ```javascript
@@ -47,7 +51,7 @@ Examples of **correct** code when `enforceForRenamedProperties` is enabled:
 var { bar: foo } = object;
 ```
 
-An example configuration, with the defaults filled in, looks like this:
+An example configuration, with the defaults `array` and `object` filled in, looks like this:
 
 ```json
 {
@@ -62,6 +66,77 @@ An example configuration, with the defaults filled in, looks like this:
 }
 ```
 
+The two properties, `array` and `object`, which can be used to turn on or off the destructuring requirement for each of those types independently. By default, both are true.
+
+For example, the following configuration enforces only object destructuring, but not array destructuring:
+
+```json
+{
+  "rules": {
+    "prefer-destructuring": ["error", {"object": true, "array": false}]
+  }
+}
+```
+
+An example configuration, with the defaults `VariableDeclarator` and `AssignmentExpression` filled in, looks like this:
+
+```json
+{
+  "rules": {
+    "prefer-destructuring": ["error", {
+      "VariableDeclarator": {
+        "array": false,
+        "object": true
+      },
+      "AssignmentExpression": {
+        "array": true,
+        "object": true
+      }
+    }, {
+      "enforceForRenamedProperties": false
+    }]
+  }
+}
+```
+
+The two properties, `VariableDeclarator` and `AssignmentExpression`, which can be used to turn on or off the destructuring requirement for `array` and `object`. By default, all values are true.
+
+For example, the following configuration enforces object destructuring in variable declarations and enforces array destructuring in assignment expressions.
+
+```json
+{
+  "rules": {
+    "prefer-destructuring": ["error", {
+      "VariableDeclarator": {
+        "array": false,
+        "object": true
+      },
+      "AssignmentExpression": {
+        "array": true,
+        "object": false
+      }
+    }, {
+      "enforceForRenamedProperties": false
+    }]
+  }
+}
+
+```
+
+Examples of **correct** code when object destructuring in `VariableDeclarator` is enforced:
+
+```javascript
+/* eslint prefer-destructuring: ["error", {VariableDeclarator: {object: true}}] */
+var {bar: foo} = object;
+```
+
+Examples of **correct** code when array destructuring in `AssignmentExpression` is enforced:
+
+```javascript
+/* eslint prefer-destructuring: ["error", {AssignmentExpression: {array: true}}] */
+[bar] = array;
+```
+
 ## When Not To Use It
 
 If you want to be able to access array indices or object properties directly, you can either configure the rule to your tastes or disable the rule entirely.
diff --git a/lib/config/config-file.js b/lib/config/config-file.js
index 3c790cf3b..091a24f34 100644
--- a/lib/config/config-file.js
+++ b/lib/config/config-file.js
@@ -546,7 +546,7 @@ function loadFromDisk(resolvedPath, configContext) {
         }
 
         // validate the configuration before continuing
-        validator.validate(config, resolvedPath, configContext.linterContext.rules, configContext.linterContext.environments);
+        validator.validate(config, resolvedPath.configFullName, configContext.linterContext.rules, configContext.linterContext.environments);
 
         /*
          * If an `extends` property is defined, it represents a configuration file to use as
diff --git a/lib/config/config-validator.js b/lib/config/config-validator.js
index fb5a96564..bff010a63 100644
--- a/lib/config/config-validator.js
+++ b/lib/config/config-validator.js
@@ -189,7 +189,7 @@ function validateConfigSchema(config, source) {
     validateSchema = validateSchema || ajv.compile(configSchema);
 
     if (!validateSchema(config)) {
-        throw new Error(`${source}:\n\tESLint configuration is invalid:\n${formatErrors(validateSchema.errors)}`);
+        throw new Error(`ESLint configuration in ${source} is invalid:\n${formatErrors(validateSchema.errors)}`);
     }
 }
 
diff --git a/lib/rules/no-constant-condition.js b/lib/rules/no-constant-condition.js
index 7178d5dbe..31e5b372c 100644
--- a/lib/rules/no-constant-condition.js
+++ b/lib/rules/no-constant-condition.js
@@ -33,7 +33,10 @@ module.exports = {
 
     create(context) {
         const options = context.options[0] || {},
-            checkLoops = options.checkLoops !== false;
+            checkLoops = options.checkLoops !== false,
+            loopSetStack = [];
+
+        let loopsInCurrentScope = new Set();
 
         //--------------------------------------------------------------------------
         // Helpers
@@ -114,18 +117,64 @@ module.exports = {
             return false;
         }
 
+        /**
+         * Tracks when the given node contains a constant condition.
+         * @param {ASTNode} node The AST node to check.
+         * @returns {void}
+         * @private
+         */
+        function trackConstantConditionLoop(node) {
+            if (node.test && isConstant(node.test, true)) {
+                loopsInCurrentScope.add(node);
+            }
+        }
+
+        /**
+         * Reports when the set contains the given constant condition node
+         * @param {ASTNode} node The AST node to check.
+         * @returns {void}
+         * @private
+         */
+        function checkConstantConditionLoopInSet(node) {
+            if (loopsInCurrentScope.has(node)) {
+                loopsInCurrentScope.delete(node);
+                context.report({ node, message: "Unexpected constant condition." });
+            }
+        }
+
         /**
          * Reports when the given node contains a constant condition.
          * @param {ASTNode} node The AST node to check.
          * @returns {void}
          * @private
          */
-        function checkConstantCondition(node) {
+        function reportIfConstant(node) {
             if (node.test && isConstant(node.test, true)) {
                 context.report({ node, message: "Unexpected constant condition." });
             }
         }
 
+        /**
+         * Stores current set of constant loops in loopSetStack temporarily
+         * and uses a new set to track constant loops
+         * @returns {void}
+         * @private
+         */
+        function enterFunction() {
+            loopSetStack.push(loopsInCurrentScope);
+            loopsInCurrentScope = new Set();
+        }
+
+        /**
+         * Reports when the set still contains stored constant conditions
+         * @param {ASTNode} node The AST node to check.
+         * @returns {void}
+         * @private
+         */
+        function exitFunction() {
+            loopsInCurrentScope = loopSetStack.pop();
+        }
+
         /**
          * Checks node when checkLoops option is enabled
          * @param {ASTNode} node The AST node to check.
@@ -134,7 +183,7 @@ module.exports = {
          */
         function checkLoop(node) {
             if (checkLoops) {
-                checkConstantCondition(node);
+                trackConstantConditionLoop(node);
             }
         }
 
@@ -143,11 +192,18 @@ module.exports = {
         //--------------------------------------------------------------------------
 
         return {
-            ConditionalExpression: checkConstantCondition,
-            IfStatement: checkConstantCondition,
+            ConditionalExpression: reportIfConstant,
+            IfStatement: reportIfConstant,
             WhileStatement: checkLoop,
+            "WhileStatement:exit": checkConstantConditionLoopInSet,
             DoWhileStatement: checkLoop,
-            ForStatement: checkLoop
+            "DoWhileStatement:exit": checkConstantConditionLoopInSet,
+            ForStatement: checkLoop,
+            "ForStatement > .test": node => checkLoop(node.parent),
+            "ForStatement:exit": checkConstantConditionLoopInSet,
+            FunctionDeclaration: enterFunction,
+            "FunctionDeclaration:exit": exitFunction,
+            YieldExpression: () => loopsInCurrentScope.clear()
         };
 
     }
diff --git a/lib/rules/object-curly-newline.js b/lib/rules/object-curly-newline.js
index b78cb9cfc..42f277873 100644
--- a/lib/rules/object-curly-newline.js
+++ b/lib/rules/object-curly-newline.js
@@ -143,6 +143,8 @@ module.exports = {
                     first.loc.start.line !== last.loc.end.line
                 )
             );
+            const hasCommentsFirstToken = astUtils.isCommentToken(first);
+            const hasCommentsLastToken = astUtils.isCommentToken(last);
 
             /*
              * Use tokens or comments to check multiline or not.
@@ -162,6 +164,10 @@ module.exports = {
                         node,
                         loc: openBrace.loc.start,
                         fix(fixer) {
+                            if (hasCommentsFirstToken) {
+                                return null;
+                            }
+
                             return fixer.insertTextAfter(openBrace, "\n");
                         }
                     });
@@ -172,6 +178,10 @@ module.exports = {
                         node,
                         loc: closeBrace.loc.start,
                         fix(fixer) {
+                            if (hasCommentsLastToken) {
+                                return null;
+                            }
+
                             return fixer.insertTextBefore(closeBrace, "\n");
                         }
                     });
@@ -190,6 +200,10 @@ module.exports = {
                         node,
                         loc: openBrace.loc.start,
                         fix(fixer) {
+                            if (hasCommentsFirstToken) {
+                                return null;
+                            }
+
                             return fixer.removeRange([
                                 openBrace.range[1],
                                 first.range[0]
@@ -206,6 +220,10 @@ module.exports = {
                         node,
                         loc: closeBrace.loc.start,
                         fix(fixer) {
+                            if (hasCommentsLastToken) {
+                                return null;
+                            }
+
                             return fixer.removeRange([
                                 last.range[1],
                                 closeBrace.range[0]
diff --git a/lib/rules/prefer-destructuring.js b/lib/rules/prefer-destructuring.js
index 7f472b423..ebf4e713b 100644
--- a/lib/rules/prefer-destructuring.js
+++ b/lib/rules/prefer-destructuring.js
@@ -15,19 +15,55 @@ module.exports = {
             category: "ECMAScript 6",
             recommended: false
         },
-
         schema: [
             {
-                type: "object",
-                properties: {
-                    array: {
-                        type: "boolean"
+
+                // old support {array: Boolean, object: Boolean}
+                // new support {VariableDeclarator: {}, AssignmentExpression: {}}
+                oneOf: [
+                    {
+                        type: "object",
+                        properties: {
+                            VariableDeclarator: {
+                                type: "object",
+                                properties: {
+                                    array: {
+                                        type: "boolean"
+                                    },
+                                    object: {
+                                        type: "boolean"
+                                    }
+                                },
+                                additionalProperties: false
+                            },
+                            AssignmentExpression: {
+                                type: "object",
+                                properties: {
+                                    array: {
+                                        type: "boolean"
+                                    },
+                                    object: {
+                                        type: "boolean"
+                                    }
+                                },
+                                additionalProperties: false
+                            }
+                        },
+                        additionalProperties: false
                     },
-                    object: {
-                        type: "boolean"
+                    {
+                        type: "object",
+                        properties: {
+                            array: {
+                                type: "boolean"
+                            },
+                            object: {
+                                type: "boolean"
+                            }
+                        },
+                        additionalProperties: false
                     }
-                },
-                additionalProperties: false
+                ]
             },
             {
                 type: "object",
@@ -42,26 +78,17 @@ module.exports = {
     },
     create(context) {
 
-        let checkArrays = true;
-        let checkObjects = true;
-        let enforceForRenamedProperties = false;
         const enabledTypes = context.options[0];
-        const additionalOptions = context.options[1];
+        const enforceForRenamedProperties = context.options[1] && context.options[1].enforceForRenamedProperties;
+        let normalizedOptions = {
+            VariableDeclarator: { array: true, object: true },
+            AssignmentExpression: { array: true, object: true }
+        };
 
         if (enabledTypes) {
-            if (typeof enabledTypes.array !== "undefined") {
-                checkArrays = enabledTypes.array;
-            }
-
-            if (typeof enabledTypes.object !== "undefined") {
-                checkObjects = enabledTypes.object;
-            }
-        }
-
-        if (additionalOptions) {
-            if (typeof additionalOptions.enforceForRenamedProperties !== "undefined") {
-                enforceForRenamedProperties = additionalOptions.enforceForRenamedProperties;
-            }
+            normalizedOptions = typeof enabledTypes.array !== "undefined" || typeof enabledTypes.object !== "undefined"
+                ? { VariableDeclarator: enabledTypes, AssignmentExpression: enabledTypes }
+                : enabledTypes;
         }
 
         //--------------------------------------------------------------------------
@@ -69,7 +96,18 @@ module.exports = {
         //--------------------------------------------------------------------------
 
         /**
-         * Determines if the given node node is accessing an array index
+         * @param {string} nodeType "AssignmentExpression" or "VariableDeclarator"
+         * @param {string} destructuringType "array" or "object"
+         * @returns {boolean} `true` if the destructuring type should be checked for the given node
+         */
+        function shouldCheck(nodeType, destructuringType) {
+            return normalizedOptions &&
+                normalizedOptions[nodeType] &&
+                normalizedOptions[nodeType][destructuringType];
+        }
+
+        /**
+         * Determines if the given node is accessing an array index
          *
          * This is used to differentiate array index access from object property
          * access.
@@ -110,22 +148,22 @@ module.exports = {
             }
 
             if (isArrayIndexAccess(rightNode)) {
-                if (checkArrays) {
+                if (shouldCheck(reportNode.type, "array")) {
                     report(reportNode, "array");
                 }
                 return;
             }
 
-            if (checkObjects && enforceForRenamedProperties) {
+            if (shouldCheck(reportNode.type, "object") && enforceForRenamedProperties) {
                 report(reportNode, "object");
                 return;
             }
 
-            if (checkObjects) {
+            if (shouldCheck(reportNode.type, "object")) {
                 const property = rightNode.property;
 
-                if ((property.type === "Literal" && leftNode.name === property.value) ||
-                    (property.type === "Identifier" && leftNode.name === property.name)) {
+                if ((property.type === "Literal" && leftNode.name === property.value) || (property.type === "Identifier" &&
+                    leftNode.name === property.name)) {
                     report(reportNode, "object");
                 }
             }
diff --git a/package.json b/package.json
index ca6e3cd91..984592b1f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint",
-  "version": "4.2.0",
+  "version": "4.3.0",
   "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
   "description": "An AST-based pattern checker for JavaScript.",
   "bin": {


Test patch:
diff --git a/lib/testers/rule-tester.js b/lib/testers/rule-tester.js
index fc6781fdc..41d2a3109 100644
--- a/lib/testers/rule-tester.js
+++ b/lib/testers/rule-tester.js
@@ -9,7 +9,7 @@
 /*
  * This is a wrapper around mocha to allow for DRY unittests for eslint
  * Format:
- * RuleTester.add("{ruleName}", {
+ * RuleTester.run("{ruleName}", {
  *      valid: [
  *          "{code}",
  *          { code: "{code}", options: {options}, globals: {globals}, parser: "{parser}", settings: {settings} }
diff --git a/tests/fixtures/config-file/invalid/invalid-top-level-property.yml b/tests/fixtures/config-file/invalid/invalid-top-level-property.yml
new file mode 100644
index 000000000..7ea8ce744
--- /dev/null
+++ b/tests/fixtures/config-file/invalid/invalid-top-level-property.yml
@@ -0,0 +1,1 @@
+invalidProperty: 3
diff --git a/tests/lib/config/config-file.js b/tests/lib/config/config-file.js
index 23a206b05..210a9de35 100644
--- a/tests/lib/config/config-file.js
+++ b/tests/lib/config/config-file.js
@@ -993,6 +993,18 @@ describe("ConfigFile", () => {
                 });
             });
         });
+
+        it("throws an error including the config file name if the config file is invalid", () => {
+            const configFilePath = getFixturePath("invalid/invalid-top-level-property.yml");
+
+            try {
+                ConfigFile.load(configFilePath, configContext);
+            } catch (err) {
+                assert.include(err.message, `ESLint configuration in ${configFilePath} is invalid`);
+                return;
+            }
+            assert.fail();
+        });
     });
 
     describe("resolve()", () => {
diff --git a/tests/lib/config/config-validator.js b/tests/lib/config/config-validator.js
index 5e42823b2..6fc24f0ad 100644
--- a/tests/lib/config/config-validator.js
+++ b/tests/lib/config/config-validator.js
@@ -370,31 +370,31 @@ describe("Validator", () => {
             it("should throw if override does not specify files", () => {
                 const fn = validator.validate.bind(null, { overrides: [{ rules: {} }] }, "tests", linter.rules, linter.environments);
 
-                assert.throws(fn, "tests:\n\tESLint configuration is invalid:\n\t- \"overrides[0]\" should have required property 'files'. Value: {\"rules\":{}}.\n");
+                assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- \"overrides[0]\" should have required property 'files'. Value: {\"rules\":{}}.\n");
             });
 
             it("should throw if override has an empty files array", () => {
                 const fn = validator.validate.bind(null, { overrides: [{ files: [] }] }, "tests", linter.rules, linter.environments);
 
-                assert.throws(fn, "tests:\n\tESLint configuration is invalid:\n\t- Property \"overrides[0].files\" is the wrong type (expected string but got `[]`).\n\t- \"overrides[0].files\" should NOT have less than 1 items. Value: [].\n\t- \"overrides[0].files\" should match exactly one schema in oneOf. Value: [].\n");
+                assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Property \"overrides[0].files\" is the wrong type (expected string but got `[]`).\n\t- \"overrides[0].files\" should NOT have less than 1 items. Value: [].\n\t- \"overrides[0].files\" should match exactly one schema in oneOf. Value: [].\n");
             });
 
             it("should throw if override has nested overrides", () => {
                 const fn = validator.validate.bind(null, { overrides: [{ files: "*", overrides: [{ files: "*", rules: {} }] }] }, "tests", linter.rules, linter.environments);
 
-                assert.throws(fn, "tests:\n\tESLint configuration is invalid:\n\t- Unexpected top-level property \"overrides[0].overrides\".\n");
+                assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].overrides\".\n");
             });
 
             it("should throw if override extends", () => {
                 const fn = validator.validate.bind(null, { overrides: [{ files: "*", extends: "eslint-recommended" }] }, "tests", linter.rules, linter.environments);
 
-                assert.throws(fn, "tests:\n\tESLint configuration is invalid:\n\t- Unexpected top-level property \"overrides[0].extends\".\n");
+                assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].extends\".\n");
             });
 
             it("should throw if override tries to set root", () => {
                 const fn = validator.validate.bind(null, { overrides: [{ files: "*", root: "true" }] }, "tests", linter.rules, linter.environments);
 
-                assert.throws(fn, "tests:\n\tESLint configuration is invalid:\n\t- Unexpected top-level property \"overrides[0].root\".\n");
+                assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].root\".\n");
             });
         });
 
diff --git a/tests/lib/rules/no-constant-condition.js b/tests/lib/rules/no-constant-condition.js
index 244ab63ec..41f3d4bc3 100644
--- a/tests/lib/rules/no-constant-condition.js
+++ b/tests/lib/rules/no-constant-condition.js
@@ -16,7 +16,7 @@ const rule = require("../../../lib/rules/no-constant-condition"),
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester();
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
 
 ruleTester.run("no-constant-condition", rule, {
     valid: [
@@ -58,7 +58,17 @@ ruleTester.run("no-constant-condition", rule, {
         // { checkLoops: false }
         { code: "while(true);", options: [{ checkLoops: false }] },
         { code: "for(;true;);", options: [{ checkLoops: false }] },
-        { code: "do{}while(true)", options: [{ checkLoops: false }] }
+        { code: "do{}while(true)", options: [{ checkLoops: false }] },
+
+        { code: "function* foo(){while(true){yield 'foo';}}" },
+        { code: "function* foo(){for(;true;){yield 'foo';}}" },
+        { code: "function* foo(){do{yield 'foo';}while(true)}" },
+        { code: "function* foo(){while (true) { while(true) {yield;}}}" },
+        { code: "function* foo() {for (; yield; ) {}}" },
+        { code: "function* foo() {for (; ; yield) {}}" },
+        { code: "function* foo() {while (true) {function* foo() {yield;}yield;}}" },
+        { code: "function* foo() { for (let x = yield; x < 10; x++) {yield;}yield;}" },
+        { code: "function* foo() { for (let x = yield; ; x++) { yield; }}" }
     ],
     invalid: [
         { code: "for(;true;);", errors: [{ message: "Unexpected constant condition.", type: "ForStatement" }] },
@@ -78,7 +88,8 @@ ruleTester.run("no-constant-condition", rule, {
         { code: "while(~!0);", errors: [{ message: "Unexpected constant condition.", type: "WhileStatement" }] },
         { code: "while(x = 1);", errors: [{ message: "Unexpected constant condition.", type: "WhileStatement" }] },
         { code: "while(function(){});", errors: [{ message: "Unexpected constant condition.", type: "WhileStatement" }] },
-        { code: "while(() => {});", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Unexpected constant condition.", type: "WhileStatement" }] },
+        { code: "while(true);", errors: [{ message: "Unexpected constant condition.", type: "WhileStatement" }] },
+        { code: "while(() => {});", errors: [{ message: "Unexpected constant condition.", type: "WhileStatement" }] },
 
         // #5228 , typeof conditions
         { code: "if(typeof x){}", errors: [{ message: "Unexpected constant condition.", type: "IfStatement" }] },
@@ -103,6 +114,47 @@ ruleTester.run("no-constant-condition", rule, {
         { code: "if(abc==='str' || true){}", errors: [{ message: "Unexpected constant condition.", type: "IfStatement" }] },
         { code: "if(abc==='str' || true || def ==='str'){}", errors: [{ message: "Unexpected constant condition.", type: "IfStatement" }] },
         { code: "if(false || true){}", errors: [{ message: "Unexpected constant condition.", type: "IfStatement" }] },
-        { code: "if(typeof abc==='str' || true){}", errors: [{ message: "Unexpected constant condition.", type: "IfStatement" }] }
+        { code: "if(typeof abc==='str' || true){}", errors: [{ message: "Unexpected constant condition.", type: "IfStatement" }] },
+
+        {
+            code: "function* foo(){while(true){} yield 'foo';}",
+            errors: [{ message: "Unexpected constant condition.", type: "WhileStatement" }]
+        },
+        {
+            code: "function* foo(){while(true){if (true) {yield 'foo';}}}",
+            errors: [{ message: "Unexpected constant condition.", type: "IfStatement" }]
+        },
+        {
+            code: "function* foo(){while(true){yield 'foo';} while(true) {}}",
+            errors: [{ message: "Unexpected constant condition.", type: "WhileStatement" }]
+        },
+        {
+            code: "var a = function* foo(){while(true){} yield 'foo';}",
+            errors: [{ message: "Unexpected constant condition.", type: "WhileStatement" }]
+        },
+        {
+            code: "while (true) { function* foo() {yield;}}",
+            errors: [{ message: "Unexpected constant condition.", type: "WhileStatement" }]
+        },
+        {
+            code: "function* foo(){if (true) {yield 'foo';}}",
+            errors: [{ message: "Unexpected constant condition.", type: "IfStatement" }]
+        },
+        {
+            code: "function* foo() {for (let foo = yield; true;) {}}",
+            errors: [{ message: "Unexpected constant condition.", type: "ForStatement" }]
+        },
+        {
+            code: "function* foo() {for (foo = yield; true;) {}}",
+            errors: [{ message: "Unexpected constant condition.", type: "ForStatement" }]
+        },
+        {
+            code: "function foo() {while (true) {function* bar() {while (true) {yield;}}}}",
+            errors: [{ message: "Unexpected constant condition.", type: "WhileStatement" }]
+        },
+        {
+            code: "function* foo() { for (let foo = 1 + 2 + 3 + (yield); true; baz) {}}",
+            errors: [{ message: "Unexpected constant condition.", type: "ForStatement" }]
+        }
     ]
 });
diff --git a/tests/lib/rules/object-curly-newline.js b/tests/lib/rules/object-curly-newline.js
index 05376e0d4..ce8f87165 100644
--- a/tests/lib/rules/object-curly-newline.js
+++ b/tests/lib/rules/object-curly-newline.js
@@ -584,6 +584,31 @@ ruleTester.run("object-curly-newline", rule, {
                 { line: 2, column: 1, message: "Unexpected line break before this closing brace." }
             ]
         },
+        {
+            code: [
+                "var a = {",
+                " /* comment */ ",
+                "};"
+            ].join("\n"),
+            output: null,
+            options: [{ multiline: true }],
+            errors: [
+                { line: 1, column: 9, message: "Unexpected line break after this opening brace." },
+                { line: 3, column: 1, message: "Unexpected line break before this closing brace." }
+            ]
+        },
+        {
+            code: [
+                "var a = { // comment",
+                "};"
+            ].join("\n"),
+            output: null,
+            options: [{ multiline: true }],
+            errors: [
+                { line: 1, column: 9, message: "Unexpected line break after this opening brace." },
+                { line: 2, column: 1, message: "Unexpected line break before this closing brace." }
+            ]
+        },
         {
             code: [
                 "var b = {",
@@ -599,6 +624,22 @@ ruleTester.run("object-curly-newline", rule, {
                 { line: 3, column: 1, message: "Unexpected line break before this closing brace." }
             ]
         },
+        {
+            code: [
+                "var b = {",
+                "   a: 1 // comment",
+                "};"
+            ].join("\n"),
+            output: [
+                "var b = {a: 1 // comment",
+                "};"
+            ].join("\n"),
+            options: [{ multiline: true }],
+            errors: [
+                { line: 1, column: 9, message: "Unexpected line break after this opening brace." },
+                { line: 3, column: 1, message: "Unexpected line break before this closing brace." }
+            ]
+        },
         {
             code: [
                 "var c = {",
@@ -614,6 +655,22 @@ ruleTester.run("object-curly-newline", rule, {
                 { line: 3, column: 1, message: "Unexpected line break before this closing brace." }
             ]
         },
+        {
+            code: [
+                "var c = {",
+                "    a: 1, b: 2 // comment",
+                "};"
+            ].join("\n"),
+            output: [
+                "var c = {a: 1, b: 2 // comment",
+                "};"
+            ].join("\n"),
+            options: [{ multiline: true }],
+            errors: [
+                { line: 1, column: 9, message: "Unexpected line break after this opening brace." },
+                { line: 3, column: 1, message: "Unexpected line break before this closing brace." }
+            ]
+        },
         {
             code: [
                 "var d = {a: 1,",
@@ -631,6 +688,23 @@ ruleTester.run("object-curly-newline", rule, {
                 { line: 2, column: 9, message: "Expected a line break before this closing brace." }
             ]
         },
+        {
+            code: [
+                "var d = {a: 1, // comment",
+                "    b: 2};"
+            ].join("\n"),
+            output: [
+                "var d = {",
+                "a: 1, // comment",
+                "    b: 2",
+                "};"
+            ].join("\n"),
+            options: [{ multiline: true }],
+            errors: [
+                { line: 1, column: 9, message: "Expected a line break after this opening brace." },
+                { line: 2, column: 9, message: "Expected a line break before this closing brace." }
+            ]
+        },
         {
             code: [
                 "var e = {a: function foo() {",
@@ -650,6 +724,71 @@ ruleTester.run("object-curly-newline", rule, {
                 { line: 3, column: 2, message: "Expected a line break before this closing brace." }
             ]
         },
+        {
+            code: [
+                "var e = {a: function foo() { // comment",
+                "    dosomething();",
+                "}};"
+            ].join("\n"),
+            output: [
+                "var e = {",
+                "a: function foo() { // comment",
+                "    dosomething();",
+                "}",
+                "};"
+            ].join("\n"),
+            options: [{ multiline: true }],
+            errors: [
+                { line: 1, column: 9, message: "Expected a line break after this opening brace." },
+                { line: 3, column: 2, message: "Expected a line break before this closing brace." }
+            ]
+        },
+        {
+            code: [
+                "var e = {a: 1, /* comment */",
+                "    b: 2, // another comment",
+                "};"
+            ].join("\n"),
+            output: [
+                "var e = {",
+                "a: 1, /* comment */",
+                "    b: 2, // another comment",
+                "};"
+            ].join("\n"),
+            options: [{ multiline: true }],
+            errors: [
+                { line: 1, column: 9, message: "Expected a line break after this opening brace." }
+            ]
+        },
+        {
+            code: [
+                "var f = { /* comment */ a:",
+                "2",
+                "};"
+            ].join("\n"),
+            output: null,
+            options: [{ multiline: true }],
+            errors: [
+                { line: 1, column: 9, message: "Expected a line break after this opening brace." }
+            ]
+        },
+        {
+            code: [
+                "var f = {",
+                "/* comment */",
+                "a: 1};"
+            ].join("\n"),
+            output: [
+                "var f = {",
+                "/* comment */",
+                "a: 1",
+                "};"
+            ].join("\n"),
+            options: [{ multiline: true }],
+            errors: [
+                { line: 3, column: 5, message: "Expected a line break before this closing brace." }
+            ]
+        },
 
         // "minProperties" ----------------------------------------------------------
         {
diff --git a/tests/lib/rules/prefer-destructuring.js b/tests/lib/rules/prefer-destructuring.js
index bf33b8a6e..4ac2ba3e8 100644
--- a/tests/lib/rules/prefer-destructuring.js
+++ b/tests/lib/rules/prefer-destructuring.js
@@ -31,47 +31,75 @@ ruleTester.run("prefer-destructuring", rule, {
             // Ensure that this doesn't break variable declarating without assignment
             code: "var foo;"
         },
+        {
+
+            // Ensure that the default behavior does not require desturcturing when renaming
+            code: "var foo = object.bar;",
+            options: [{ VariableDeclarator: { object: true } }]
+        },
         {
 
             // Ensure that the default behavior does not require desturcturing when renaming
             code: "var foo = object.bar;",
             options: [{ object: true }]
         },
+        {
+            code: "var foo = object.bar;",
+            options: [{ VariableDeclarator: { object: true } }, { enforceForRenamedProperties: false }]
+        },
         {
             code: "var foo = object.bar;",
             options: [{ object: true }, { enforceForRenamedProperties: false }]
         },
         {
             code: "var foo = object['bar'];",
-            options: [{ object: true }, { enforceForRenamedProperties: false }]
+            options: [{ VariableDeclarator: { object: true } }, { enforceForRenamedProperties: false }]
         },
         {
             code: "var foo = object[bar];",
             options: [{ object: true }, { enforceForRenamedProperties: false }]
         },
+        {
+            code: "var { bar: foo } = object;",
+            options: [{ VariableDeclarator: { object: true } }, { enforceForRenamedProperties: true }]
+        },
         {
             code: "var { bar: foo } = object;",
             options: [{ object: true }, { enforceForRenamedProperties: true }]
         },
+        {
+            code: "var { [bar]: foo } = object;",
+            options: [{ VariableDeclarator: { object: true } }, { enforceForRenamedProperties: true }]
+        },
         {
             code: "var { [bar]: foo } = object;",
             options: [{ object: true }, { enforceForRenamedProperties: true }]
         },
+        {
+            code: "var foo = array[0];",
+            options: [{ VariableDeclarator: { array: false } }]
+        },
         {
             code: "var foo = array[0];",
             options: [{ array: false }]
         },
         {
             code: "var foo = object.foo;",
-            options: [{ object: false }]
+            options: [{ VariableDeclarator: { object: false } }]
         },
         {
             code: "var foo = object['foo'];",
-            options: [{ object: false }]
+            options: [{ VariableDeclarator: { object: false } }]
         },
         {
             code: "({ foo } = object);"
         },
+        {
+
+            // Fix #8654
+            code: "var foo = array[0];",
+            options: [{ VariableDeclarator: { array: false } }, { enforceForRenamedProperties: true }]
+        },
         {
 
             // Fix #8654
@@ -80,7 +108,39 @@ ruleTester.run("prefer-destructuring", rule, {
         },
         "[foo] = array;",
         "foo += array[0]",
-        "foo += bar.foo"
+        "foo += bar.foo",
+        {
+            code: "foo = object.foo;",
+            options: [{ AssignmentExpression: { object: false } }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "foo = object.foo;",
+            options: [{ AssignmentExpression: { object: false } }, { enforceForRenamedProperties: false }]
+        },
+        {
+            code: "foo = array[0];",
+            options: [{ AssignmentExpression: { array: false } }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "foo = array[0];",
+            options: [{ AssignmentExpression: { array: false } }, { enforceForRenamedProperties: false }]
+        },
+        {
+            code: "foo = array[0];",
+            options: [{ VariableDeclarator: { array: true }, AssignmentExpression: { array: false } }, { enforceForRenamedProperties: false }]
+        },
+        {
+            code: "var foo = array[0];",
+            options: [{ VariableDeclarator: { array: false }, AssignmentExpression: { array: true } }, { enforceForRenamedProperties: false }]
+        },
+        {
+            code: "foo = object.foo;",
+            options: [{ VariableDeclarator: { object: true }, AssignmentExpression: { object: false } }]
+        },
+        {
+            code: "var foo = object.foo;",
+            options: [{ VariableDeclarator: { object: false }, AssignmentExpression: { object: true } }]
+        }
     ],
 
     invalid: [
@@ -105,6 +165,14 @@ ruleTester.run("prefer-destructuring", rule, {
                 type: "VariableDeclarator"
             }]
         },
+        {
+            code: "var foobar = object.bar;",
+            options: [{ VariableDeclarator: { object: true } }, { enforceForRenamedProperties: true }],
+            errors: [{
+                message: "Use object destructuring.",
+                type: "VariableDeclarator"
+            }]
+        },
         {
             code: "var foobar = object.bar;",
             options: [{ object: true }, { enforceForRenamedProperties: true }],
@@ -113,6 +181,14 @@ ruleTester.run("prefer-destructuring", rule, {
                 type: "VariableDeclarator"
             }]
         },
+        {
+            code: "var foo = object[bar];",
+            options: [{ VariableDeclarator: { object: true } }, { enforceForRenamedProperties: true }],
+            errors: [{
+                message: "Use object destructuring.",
+                type: "VariableDeclarator"
+            }]
+        },
         {
             code: "var foo = object[bar];",
             options: [{ object: true }, { enforceForRenamedProperties: true }],
@@ -122,21 +198,90 @@ ruleTester.run("prefer-destructuring", rule, {
             }]
         },
         {
-            code: "var foo = array['foo'];",
+            code: "var foo = object['foo'];",
             errors: [{
                 message: "Use object destructuring.",
                 type: "VariableDeclarator"
             }]
         },
         {
-            code: "foo = array.foo;",
+            code: "foo = object.foo;",
             errors: [{
                 message: "Use object destructuring.",
                 type: "AssignmentExpression"
             }]
         },
         {
-            code: "foo = array['foo'];",
+            code: "foo = object['foo'];",
+            errors: [{
+                message: "Use object destructuring.",
+                type: "AssignmentExpression"
+            }]
+        },
+        {
+            code: "var foo = array[0];",
+            options: [{ VariableDeclarator: { array: true } }, { enforceForRenamedProperties: true }],
+            errors: [{
+                message: "Use array destructuring.",
+                type: "VariableDeclarator"
+            }]
+        },
+        {
+            code: "foo = array[0];",
+            options: [{ AssignmentExpression: { array: true } }],
+            errors: [{
+                message: "Use array destructuring.",
+                type: "AssignmentExpression"
+            }]
+        },
+        {
+            code: "var foo = array[0];",
+            options: [
+                {
+                    VariableDeclarator: { array: true },
+                    AssignmentExpression: { array: false }
+                },
+                { enforceForRenamedProperties: true }
+            ],
+            errors: [{
+                message: "Use array destructuring.",
+                type: "VariableDeclarator"
+            }]
+        },
+        {
+            code: "var foo = array[0];",
+            options: [
+                {
+                    VariableDeclarator: { array: true },
+                    AssignmentExpression: { array: false }
+                }
+            ],
+            errors: [{
+                message: "Use array destructuring.",
+                type: "VariableDeclarator"
+            }]
+        },
+        {
+            code: "foo = array[0];",
+            options: [
+                {
+                    VariableDeclarator: { array: false },
+                    AssignmentExpression: { array: true }
+                }
+            ],
+            errors: [{
+                message: "Use array destructuring.",
+                type: "AssignmentExpression"
+            }]
+        },
+        {
+            code: "foo = object.foo;",
+            options: [
+                {
+                    VariableDeclarator: { array: true, object: false },
+                    AssignmentExpression: { object: true }
+                }
+            ],
             errors: [{
                 message: "Use object destructuring.",
                 type: "AssignmentExpression"
diff --git a/tests/lib/testers/rule-tester.js b/tests/lib/testers/rule-tester.js
index 86bf40cde..84e6febae 100644
--- a/tests/lib/testers/rule-tester.js
+++ b/tests/lib/testers/rule-tester.js
@@ -648,7 +648,7 @@ describe("RuleTester", () => {
                 ],
                 invalid: []
             });
-        }, /ESLint configuration is invalid./);
+        }, /ESLint configuration in rule-tester is invalid./);
     });
 
     it("throw an error when an invalid config value is included", () => {