Repository: eslint/eslint (js)

Issue:
ESLint CLI gives no information about exceptions thrown on plugin load
**What version of ESLint are you using?**
2.13.1 (with eslint-plugin-react 6.0.0)

**What parser (default, Babel-ESLint, etc.) are you using?**
Default (this shouldn't matter)

**Please show your full configuration:**

``` json
{
    "plugins": ["react"]
}
```

**What did you do? Please include the actual source code causing the issue.**

``` shell
$ eslint --debug someFile.js
```

**What did you expect to happen?**
Plugin loads successfully, or if it fails to load, the exception thrown should show up in `eslint --debug`.

**What actually happened? Please include the actual, raw output from ESLint.**

``` text
eslint:cli Running on files +0ms
eslint:ignored-paths Looking for ignore file in /path/to/root +88ms
eslint:ignored-paths Could not find ignore file in cwd +3ms
eslint:glob-util Creating list of files to process. +0ms
eslint:cli-engine Processing /path/to/js/file +23ms
eslint:cli-engine Linting /path/to/js/file +2ms
eslint:config Constructing config for /path/to/js/file +1ms
eslint:config Using .eslintrc and package.json files +0ms
eslint:config Loading /.eslintrc +3ms
eslint:config-file Loading config file: /.eslintrc +1ms
eslint:plugins Failed to load plugin eslint-plugin-react. Proceeding without it. +212ms
```

With no indication of why the plugin failed to load. In this case, it wasn't due to the plugin not being available, but rather the `require()` failed due to something wrong with the plugin's main file.


Pull request description:
Fix: don't classify plugins that throw errors as "missing" (fixes #6874)
**What is the purpose of this pull request? (put an "X" next to item)**

[x] Bug fix (https://github.com/eslint/eslint/issues/6874)

**What changes did you make? (Give an overview)**

Previously, if a plugin threw an error on load, ESLint would display the "missing plugin" error message. This was confusing for plugin developers, because it would be difficult to distinguish between an installation error and a broken plugin.

This fix avoids the confusion by treating the errors as fatal and crashing with a stack trace if a plugin throws when it loads, as described in https://github.com/eslint/eslint/issues/7668#issuecomment-263148418.

**Is there anything you'd like reviewers to focus on?**

Nothing in particular

Patch:
diff --git a/lib/config/config-rule.js b/lib/config/config-rule.js
index bd7aa13b9..a8a073caa 100644
--- a/lib/config/config-rule.js
+++ b/lib/config/config-rule.js
@@ -223,7 +223,7 @@ class RuleConfigSet {
     /**
     * Add rule configurations from a schema object
     * @param  {Object} obj Schema item with type === "object"
-    * @returns {void}
+    * @returns {boolean} true if at least one schema for the object could be generated, false otherwise
     */
     addObject(obj) {
         const objectConfigSet = {
@@ -258,7 +258,10 @@ class RuleConfigSet {
 
         if (objectConfigSet.objectConfigs.length > 0) {
             this.ruleConfigs = this.ruleConfigs.concat(combineArrays(this.ruleConfigs, objectConfigSet.objectConfigs));
+            return true;
         }
+
+        return false;
     }
 }
 
@@ -271,20 +274,21 @@ function generateConfigsFromSchema(schema) {
     const configSet = new RuleConfigSet();
 
     if (Array.isArray(schema)) {
-        schema.forEach(opt => {
+        for (const opt of schema) {
             if (opt.enum) {
                 configSet.addEnums(opt.enum);
-            }
-
-            if (opt.type && opt.type === "object") {
-                configSet.addObject(opt);
-            }
+            } else if (opt.type && opt.type === "object") {
+                if (!configSet.addObject(opt)) {
+                    break;
+                }
 
-            if (opt.oneOf) {
+            // TODO (IanVS): support oneOf
+            } else {
 
-                // TODO (IanVS): not yet implemented
+                // If we don't know how to fill in this option, don't fill in any of the following options.
+                break;
             }
-        });
+        }
     }
     configSet.addErrorSeverity();
     return configSet.ruleConfigs;
diff --git a/lib/config/plugins.js b/lib/config/plugins.js
index 5073d564a..e28a77929 100644
--- a/lib/config/plugins.js
+++ b/lib/config/plugins.js
@@ -127,14 +127,25 @@ module.exports = {
         if (!plugins[shortName]) {
             try {
                 plugin = require(longName);
-            } catch (err) {
-                debug(`Failed to load plugin ${longName}.`);
-                err.message = `Failed to load plugin ${pluginName}: ${err.message}`;
-                err.messageTemplate = "plugin-missing";
-                err.messageData = {
-                    pluginName: longName
-                };
-                throw err;
+            } catch (pluginLoadErr) {
+                try {
+
+                    // Check whether the plugin exists
+                    require.resolve(longName);
+                } catch (missingPluginErr) {
+
+                    // If the plugin can't be resolved, display the missing plugin error (usually a config or install error)
+                    debug(`Failed to load plugin ${longName}.`);
+                    missingPluginErr.message = `Failed to load plugin ${pluginName}: ${missingPluginErr.message}`;
+                    missingPluginErr.messageTemplate = "plugin-missing";
+                    missingPluginErr.messageData = {
+                        pluginName: longName
+                    };
+                    throw missingPluginErr;
+                }
+
+                // Otherwise, the plugin exists and is throwing on module load for some reason, so print the stack trace.
+                throw pluginLoadErr;
             }
 
             this.define(pluginName, plugin);
diff --git a/lib/eslint.js b/lib/eslint.js
index 3c23a7495..a9066c4c8 100755
--- a/lib/eslint.js
+++ b/lib/eslint.js
@@ -778,17 +778,18 @@ module.exports = (function() {
         // search and apply "eslint-env *".
         const envInFile = findEslintEnv(text || textOrSourceCode.text);
 
+        config = Object.assign({}, config);
+
         if (envInFile) {
-            if (!config || !config.env) {
-                config = Object.assign({}, config || {}, { env: envInFile });
-            } else {
-                config = Object.assign({}, config);
+            if (config.env) {
                 config.env = Object.assign({}, config.env, envInFile);
+            } else {
+                config.env = envInFile;
             }
         }
 
         // process initial config to make it safe to extend
-        config = prepareConfig(config || {});
+        config = prepareConfig(config);
 
         // only do this for text
         if (text !== null) {
diff --git a/lib/rules/comma-style.js b/lib/rules/comma-style.js
index 4892ae505..fcaecc662 100644
--- a/lib/rules/comma-style.js
+++ b/lib/rules/comma-style.js
@@ -205,7 +205,11 @@ module.exports = {
                                 currentItemToken, reportItem);
                     }
 
-                    previousItemToken = item ? sourceCode.getLastToken(item) : previousItemToken;
+                    if (item) {
+                        const tokenAfterItem = sourceCode.getTokenAfter(item, astUtils.isNotClosingParenToken);
+
+                        previousItemToken = tokenAfterItem ? sourceCode.getTokenBefore(tokenAfterItem) : sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1];
+                    }
                 });
 
                 /*
diff --git a/lib/rules/dot-notation.js b/lib/rules/dot-notation.js
index d55b098b2..abb9b4b48 100644
--- a/lib/rules/dot-notation.js
+++ b/lib/rules/dot-notation.js
@@ -4,6 +4,12 @@
  */
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
@@ -64,20 +70,20 @@ module.exports = {
                                 propertyValue: JSON.stringify(node.property.value)
                             },
                             fix(fixer) {
-                                const leftBracket = sourceCode.getTokenBefore(node.property);
-                                const rightBracket = sourceCode.getTokenAfter(node.property);
-                                const textBeforeProperty = sourceCode.text.slice(leftBracket.range[1], node.property.range[0]);
-                                const textAfterProperty = sourceCode.text.slice(node.property.range[1], rightBracket.range[0]);
+                                const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken);
+                                const rightBracket = sourceCode.getLastToken(node);
 
-                                if (textBeforeProperty.trim() || textAfterProperty.trim()) {
+                                if (sourceCode.getFirstTokenBetween(leftBracket, rightBracket, { includeComments: true, filter: astUtils.isCommentToken })) {
 
                                     // Don't perform any fixes if there are comments inside the brackets.
                                     return null;
                                 }
 
+                                const textBeforeDot = astUtils.isDecimalInteger(node.object) ? " " : "";
+
                                 return fixer.replaceTextRange(
                                     [leftBracket.range[0], rightBracket.range[1]],
-                                    `.${node.property.value}`
+                                    `${textBeforeDot}.${node.property.value}`
                                 );
                             }
                         });
diff --git a/lib/rules/no-else-return.js b/lib/rules/no-else-return.js
index 6ba0b2426..68ab4c760 100644
--- a/lib/rules/no-else-return.js
+++ b/lib/rules/no-else-return.js
@@ -9,6 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
+const astUtils = require("../ast-utils");
 const FixTracker = require("../util/fix-tracker");
 
 //------------------------------------------------------------------------------
@@ -199,9 +200,11 @@ module.exports = {
             let consequents,
                 alternate;
 
-            // Only "top-level" if statements are checked, meaning the first `if`
-            // in a `if-else-if-...` chain.
-            if (parent.type === "IfStatement" && parent.alternate === node) {
+            /*
+             * Fixing this would require splitting one statement into two, so no error should
+             * be reported if this node is in a position where only one statement is allowed.
+             */
+            if (!astUtils.STATEMENT_LIST_PARENTS.has(parent.type)) {
                 return;
             }
 
diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js
index eed0d401d..42a86484b 100644
--- a/lib/rules/no-extra-parens.js
+++ b/lib/rules/no-extra-parens.js
@@ -318,7 +318,7 @@ module.exports = {
             }
 
             // If the parens are preceded by a keyword (e.g. `typeof(0)`), a space should be inserted (`typeof 0`)
-            const precededByKeyword = tokenBeforeLeftParen.type === "Keyword";
+            const precededByIdentiferPart = esUtils.code.isIdentifierPartES6(tokenBeforeLeftParen.value.slice(-1).charCodeAt(0));
 
             // However, a space should not be inserted unless the first character of the token is an identifier part
             // e.g. `typeof([])` should be fixed to `typeof[]`
@@ -331,7 +331,7 @@ module.exports = {
             const startsWithUnaryPlus = firstToken.type === "Punctuator" && firstToken.value === "+";
             const startsWithUnaryMinus = firstToken.type === "Punctuator" && firstToken.value === "-";
 
-            return (precededByKeyword && startsWithIdentifierPart) ||
+            return (precededByIdentiferPart && startsWithIdentifierPart) ||
                 (precededByUnaryPlus && startsWithUnaryPlus) ||
                 (precededByUnaryMinus && startsWithUnaryMinus);
         }
@@ -345,6 +345,11 @@ module.exports = {
         function report(node) {
             const leftParenToken = sourceCode.getTokenBefore(node);
             const rightParenToken = sourceCode.getTokenAfter(node);
+            const tokenBeforeLeftParen = sourceCode.getTokenBefore(leftParenToken);
+
+            if (tokenBeforeLeftParen && astUtils.isArrowToken(tokenBeforeLeftParen) && astUtils.isOpeningBraceToken(sourceCode.getFirstToken(node))) {
+                return;
+            }
 
             context.report({
                 node,
@@ -481,7 +486,7 @@ module.exports = {
                 }
 
                 if (node.body.type !== "BlockStatement") {
-                    if (sourceCode.getFirstToken(node.body).value !== "{" && hasExcessParens(node.body) && precedence(node.body) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
+                    if (hasExcessParens(node.body) && precedence(node.body) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
                         report(node.body);
                         return;
                     }
diff --git a/lib/rules/no-implicit-coercion.js b/lib/rules/no-implicit-coercion.js
index 8e8db9879..387b3dae4 100644
--- a/lib/rules/no-implicit-coercion.js
+++ b/lib/rules/no-implicit-coercion.js
@@ -6,6 +6,7 @@
 "use strict";
 
 const astUtils = require("../ast-utils");
+const esUtils = require("esutils");
 
 //------------------------------------------------------------------------------
 // Helpers
@@ -197,19 +198,31 @@ module.exports = {
         */
         function report(node, recommendation, shouldFix) {
             shouldFix = typeof shouldFix === "undefined" ? true : shouldFix;
-            const reportObj = {
+
+            context.report({
                 node,
                 message: "use `{{recommendation}}` instead.",
                 data: {
                     recommendation
+                },
+                fix(fixer) {
+                    if (!shouldFix) {
+                        return null;
+                    }
+
+                    const tokenBefore = sourceCode.getTokenBefore(node);
+
+                    if (
+                        tokenBefore &&
+                        tokenBefore.range[1] === node.range[0] &&
+                        esUtils.code.isIdentifierPartES6(tokenBefore.value.slice(-1).charCodeAt(0)) &&
+                        esUtils.code.isIdentifierPartES6(recommendation.charCodeAt(0))
+                    ) {
+                        return fixer.replaceText(node, ` ${recommendation}`);
+                    }
+                    return fixer.replaceText(node, recommendation);
                 }
-            };
-
-            if (shouldFix) {
-                reportObj.fix = fixer => fixer.replaceText(node, recommendation);
-            }
-
-            context.report(reportObj);
+            });
         }
 
         return {
diff --git a/lib/rules/no-useless-computed-key.js b/lib/rules/no-useless-computed-key.js
index 573fe21c0..fd5ec2c92 100644
--- a/lib/rules/no-useless-computed-key.js
+++ b/lib/rules/no-useless-computed-key.js
@@ -9,6 +9,7 @@
 //------------------------------------------------------------------------------
 
 const astUtils = require("../ast-utils");
+const esUtils = require("esutils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
@@ -55,7 +56,17 @@ module.exports = {
                                 // If there are comments between the brackets and the property name, don't do a fix.
                                 return null;
                             }
-                            return fixer.replaceTextRange([leftSquareBracket.range[0], rightSquareBracket.range[1]], key.raw);
+
+                            const tokenBeforeLeftBracket = sourceCode.getTokenBefore(leftSquareBracket);
+
+                            // Insert a space before the key to avoid changing identifiers, e.g. ({ get[2]() {} }) to ({ get2() {} })
+                            const needsSpaceBeforeKey = tokenBeforeLeftBracket.range[1] === leftSquareBracket.range[0] &&
+                                esUtils.code.isIdentifierPartES6(tokenBeforeLeftBracket.value.slice(-1).charCodeAt(0)) &&
+                                esUtils.code.isIdentifierPartES6(key.raw.charCodeAt(0));
+
+                            const replacementKey = (needsSpaceBeforeKey ? " " : "") + key.raw;
+
+                            return fixer.replaceTextRange([leftSquareBracket.range[0], rightSquareBracket.range[1]], replacementKey);
                         }
                     });
                 }
diff --git a/lib/rules/object-shorthand.js b/lib/rules/object-shorthand.js
index 1d32d1722..1d3d9dae1 100644
--- a/lib/rules/object-shorthand.js
+++ b/lib/rules/object-shorthand.js
@@ -230,7 +230,10 @@ module.exports = {
                 const functionToken = sourceCode.getTokens(node.value).find(token => token.type === "Keyword" && token.value === "function");
                 const tokenBeforeParams = node.value.generator ? sourceCode.getTokenAfter(functionToken) : functionToken;
 
-                return fixer.replaceTextRange([firstKeyToken.range[0], tokenBeforeParams.range[1]], keyPrefix + keyText);
+                return fixer.replaceTextRange(
+                    [firstKeyToken.range[0], node.range[1]],
+                    keyPrefix + keyText + sourceCode.text.slice(tokenBeforeParams.range[1], node.value.range[1])
+                );
             }
             const arrowToken = sourceCode.getTokens(node.value).find(token => token.value === "=>");
             const tokenBeforeArrow = sourceCode.getTokenBefore(arrowToken);
@@ -238,7 +241,10 @@ module.exports = {
             const oldParamText = sourceCode.text.slice(sourceCode.getFirstToken(node.value, node.value.async ? 1 : 0).range[0], tokenBeforeArrow.range[1]);
             const newParamText = hasParensAroundParameters ? oldParamText : `(${oldParamText})`;
 
-            return fixer.replaceTextRange([firstKeyToken.range[0], arrowToken.range[1]], keyPrefix + keyText + newParamText);
+            return fixer.replaceTextRange(
+                [firstKeyToken.range[0], node.range[1]],
+                keyPrefix + keyText + newParamText + sourceCode.text.slice(arrowToken.range[1], node.value.range[1])
+            );
 
         }
 
diff --git a/lib/rules/quotes.js b/lib/rules/quotes.js
index 1085ea33b..b1117b85f 100644
--- a/lib/rules/quotes.js
+++ b/lib/rules/quotes.js
@@ -209,6 +209,7 @@ module.exports = {
 
                 // LiteralPropertyName.
                 case "Property":
+                case "MethodDefinition":
                     return parent.key === node && !parent.computed;
 
                 // ModuleSpecifier.


Test patch:
diff --git a/tests/fixtures/config-rule/schemas.js b/tests/fixtures/config-rule/schemas.js
index 7eb77fdae..d74bcce1d 100644
--- a/tests/fixtures/config-rule/schemas.js
+++ b/tests/fixtures/config-rule/schemas.js
@@ -69,6 +69,22 @@ module.exports = {
         "additionalProperties": false
     }],
 
+    mixedObjectWithNothingEnum: [{
+        "type": "object",
+        "properties": {},
+        "additionalProperties": false
+    },
+    {
+        "enum": ["always", "never"]
+    }],
+
+    mixedStringEnum: [{
+        "type": "string"
+    },
+    {
+        "enum": ["always", "never"]
+    }],
+
     oneOf: [{
         "oneOf": [
             {
diff --git a/tests/lib/config/config-rule.js b/tests/lib/config/config-rule.js
index a801e91c8..f41e3bfc6 100644
--- a/tests/lib/config/config-rule.js
+++ b/tests/lib/config/config-rule.js
@@ -230,7 +230,7 @@ describe("ConfigRule", () => {
             });
         });
 
-        describe("for a schema with an enum and an object with no usable properties", () => {
+        describe("for a schema with an enum followed by an object with no usable properties", () => {
             before(() => {
                 actualConfigs = ConfigRule.generateConfigsFromSchema(schema.mixedEnumObjectWithNothing);
             });
@@ -242,6 +242,30 @@ describe("ConfigRule", () => {
             });
         });
 
+        describe("for a schema with an enum preceded by an object with no usable properties", () => {
+            before(() => {
+                actualConfigs = ConfigRule.generateConfigsFromSchema(schema.mixedObjectWithNothingEnum);
+            });
+
+            it("should not create a config for the enum", () => {
+                const expectedConfigs = [2];
+
+                assert.sameDeepMembers(actualConfigs, expectedConfigs);
+            });
+        });
+
+        describe("for a schema with an enum preceded by a string", () => {
+            before(() => {
+                actualConfigs = ConfigRule.generateConfigsFromSchema(schema.mixedStringEnum);
+            });
+
+            it("should not create a config for the enum", () => {
+                const expectedConfigs = [2];
+
+                assert.sameDeepMembers(actualConfigs, expectedConfigs);
+            });
+        });
+
         describe("for a schema with oneOf", () => {
 
             before(() => {
diff --git a/tests/lib/config/plugins.js b/tests/lib/config/plugins.js
index 06b2bcd38..61b759797 100644
--- a/tests/lib/config/plugins.js
+++ b/tests/lib/config/plugins.js
@@ -43,7 +43,12 @@ describe("Plugins", () => {
                 "eslint-plugin-example": plugin,
                 "@scope/eslint-plugin-example": scopedPlugin,
                 "./environments": Environments,
-                "../rules": Rules
+                "../rules": Rules,
+                "eslint-plugin-throws-on-load": {
+                    get rules() {
+                        throw new Error("error thrown while loading this module");
+                    }
+                }
             });
         });
 
@@ -106,6 +111,21 @@ describe("Plugins", () => {
             }, /Failed to load plugin/);
         });
 
+        it("should rethrow an error that a plugin throws on load", () => {
+            try {
+                StubbedPlugins.load("throws-on-load");
+            } catch (err) {
+                assert.strictEqual(
+                    err.message,
+                    "error thrown while loading this module",
+                    "should rethrow the same error that was thrown on plugin load"
+                );
+
+                return;
+            }
+            assert.fail(null, null, "should throw an error if a plugin fails to load");
+        });
+
         it("should load a scoped plugin when referenced by short name", () => {
             StubbedPlugins.load("@scope/example");
             assert.equal(StubbedPlugins.get("@scope/example"), scopedPlugin);
diff --git a/tests/lib/eslint.js b/tests/lib/eslint.js
index 406302a69..9a1456cb1 100644
--- a/tests/lib/eslint.js
+++ b/tests/lib/eslint.js
@@ -3425,6 +3425,16 @@ describe("eslint", () => {
 
             assert(result.length === 0);
         });
+
+
+        it("should not modify config object passed as argument", () => {
+            const config = {};
+
+            Object.freeze(config);
+            assert.doesNotThrow(() => {
+                eslint.verify("var foo", config);
+            });
+        });
     });
 
     describe("Variables and references", () => {
diff --git a/tests/lib/rules/comma-style.js b/tests/lib/rules/comma-style.js
index 8dcbbc883..98c765c6b 100644
--- a/tests/lib/rules/comma-style.js
+++ b/tests/lib/rules/comma-style.js
@@ -569,6 +569,11 @@ ruleTester.run("comma-style", rule, {
                 message: BAD_LN_BRK_MSG,
                 type: "Identifier"
             }]
+        },
+        {
+            code: "[(foo),\n,\nbar]",
+            output: "[(foo),,\nbar]",
+            errors: [{ message: BAD_LN_BRK_MSG }]
         }
     ]
 });
diff --git a/tests/lib/rules/dot-notation.js b/tests/lib/rules/dot-notation.js
index 83b212efd..0c28c4393 100644
--- a/tests/lib/rules/dot-notation.js
+++ b/tests/lib/rules/dot-notation.js
@@ -153,6 +153,26 @@ ruleTester.run("dot-notation", rule, {
             options: [{ allowKeywords: false }],
             errors: [{ message: ".while is a syntax error." }],
             output: null // Not fixed due to comment
+        },
+        {
+            code: "foo[('bar')]",
+            output: "foo.bar",
+            errors: [{ message: "[\"bar\"] is better written in dot notation." }]
+        },
+        {
+            code: "foo[(null)]",
+            output: "foo.null",
+            errors: [{ message: "[null] is better written in dot notation." }]
+        },
+        {
+            code: "(foo)['bar']",
+            output: "(foo).bar",
+            errors: [{ message: "[\"bar\"] is better written in dot notation." }]
+        },
+        {
+            code: "1['toString']",
+            output: "1 .toString",
+            errors: [{ message: "[\"toString\"] is better written in dot notation." }]
         }
     ]
 });
diff --git a/tests/lib/rules/no-else-return.js b/tests/lib/rules/no-else-return.js
index e99b83d6c..6aea5c867 100644
--- a/tests/lib/rules/no-else-return.js
+++ b/tests/lib/rules/no-else-return.js
@@ -27,7 +27,22 @@ ruleTester.run("no-else-return", rule, {
         "function foo() { if (true) notAReturn(); else return y; }",
         "function foo() {if (x) { notAReturn(); } else if (y) { return true; } else { notAReturn(); } }",
         "function foo() {if (x) { return true; } else if (y) { notAReturn() } else { notAReturn(); } }",
-        "if (0) { if (0) {} else {} } else {}"
+        "if (0) { if (0) {} else {} } else {}",
+        `
+            function foo() {
+                if (foo)
+                    if (bar) return;
+                    else baz;
+                else qux;
+            }
+        `,
+        `
+            function foo() {
+                while (foo)
+                    if (bar) return;
+                    else baz;
+            }
+        `
     ],
     invalid: [
         {
diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js
index 349c14994..967d2df05 100644
--- a/tests/lib/rules/no-extra-parens.js
+++ b/tests/lib/rules/no-extra-parens.js
@@ -410,6 +410,22 @@ ruleTester.run("no-extra-parens", rule, {
         {
             code: "const A = class extends (B=C) {}",
             parserOptions: { ecmaVersion: 2015 }
+        },
+        {
+            code: "() => ({ foo: 1 })",
+            parserOptions: { ecmaVersion: 2015 }
+        },
+        {
+            code: "() => ({ foo: 1 }).foo",
+            parserOptions: { ecmaVersion: 2015 }
+        },
+        {
+            code: "() => ({ foo: 1 }.foo().bar).baz.qux()",
+            parserOptions: { ecmaVersion: 2015 }
+        },
+        {
+            code: "() => ({ foo: 1 }.foo().bar + baz)",
+            parserOptions: { ecmaVersion: 2015 }
         }
     ],
 
@@ -952,6 +968,25 @@ ruleTester.run("no-extra-parens", rule, {
             "AssignmentExpression",
             1,
             { parserOptions: { ecmaVersion: 2015 } }
-        )
+        ),
+        invalid(
+            "for (foo of(bar));",
+            "for (foo of bar);",
+            "Identifier",
+            1,
+            { parserOptions: { ecmaVersion: 2015 } }
+        ),
+        {
+            code: "() => (({ foo: 1 }).foo)",
+            output: "() => ({ foo: 1 }).foo",
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [
+
+                // 2 errors are reported, but fixing one gets rid of the other
+                { message: "Gratuitous parentheses around expression.", type: "MemberExpression" },
+                { message: "Gratuitous parentheses around expression.", type: "ObjectExpression" }
+            ]
+
+        }
     ]
 });
diff --git a/tests/lib/rules/no-implicit-coercion.js b/tests/lib/rules/no-implicit-coercion.js
index 3b1f8e89c..5ea5f5fa4 100644
--- a/tests/lib/rules/no-implicit-coercion.js
+++ b/tests/lib/rules/no-implicit-coercion.js
@@ -221,6 +221,16 @@ ruleTester.run("no-implicit-coercion", rule, {
             parserOptions: { ecmaVersion: 6 },
             errors: [{ message: "use `String(foo)` instead.", type: "BinaryExpression" }],
             output: "var a = String(foo)"
+        },
+        {
+            code: "typeof+foo",
+            output: "typeof Number(foo)",
+            errors: [{ message: "use `Number(foo)` instead.", type: "UnaryExpression" }]
+        },
+        {
+            code: "typeof +foo",
+            output: "typeof Number(foo)",
+            errors: [{ message: "use `Number(foo)` instead.", type: "UnaryExpression" }]
         }
     ]
 });
diff --git a/tests/lib/rules/no-useless-computed-key.js b/tests/lib/rules/no-useless-computed-key.js
index 61c535075..c825579fc 100644
--- a/tests/lib/rules/no-useless-computed-key.js
+++ b/tests/lib/rules/no-useless-computed-key.js
@@ -87,6 +87,81 @@ ruleTester.run("no-useless-computed-key", rule, {
             errors: [{
                 message: "Unnecessarily computed property ['x'] found.", type: "Property"
             }]
+        }, {
+            code: "({ get[.2]() {} })",
+            output: "({ get.2() {} })",
+            errors: [{
+                message: "Unnecessarily computed property [.2] found.", type: "Property"
+            }]
+        }, {
+            code: "({ set[.2](value) {} })",
+            output: "({ set.2(value) {} })",
+            errors: [{
+                message: "Unnecessarily computed property [.2] found.", type: "Property"
+            }]
+        }, {
+            code: "({ async[.2]() {} })",
+            output: "({ async.2() {} })",
+            parserOptions: { ecmaVersion: 8 },
+            errors: [{
+                message: "Unnecessarily computed property [.2] found.", type: "Property"
+            }]
+        }, {
+            code: "({ [2]() {} })",
+            output: "({ 2() {} })",
+            errors: [{
+                message: "Unnecessarily computed property [2] found.", type: "Property"
+            }]
+        }, {
+            code: "({ get [2]() {} })",
+            output: "({ get 2() {} })",
+            errors: [{
+                message: "Unnecessarily computed property [2] found.", type: "Property"
+            }]
+        }, {
+            code: "({ set [2](value) {} })",
+            output: "({ set 2(value) {} })",
+            errors: [{
+                message: "Unnecessarily computed property [2] found.", type: "Property"
+            }]
+        }, {
+            code: "({ async [2]() {} })",
+            output: "({ async 2() {} })",
+            parserOptions: { ecmaVersion: 8 },
+            errors: [{
+                message: "Unnecessarily computed property [2] found.", type: "Property"
+            }]
+        }, {
+            code: "({ get[2]() {} })",
+            output: "({ get 2() {} })",
+            errors: [{
+                message: "Unnecessarily computed property [2] found.", type: "Property"
+            }]
+        }, {
+            code: "({ set[2](value) {} })",
+            output: "({ set 2(value) {} })",
+            errors: [{
+                message: "Unnecessarily computed property [2] found.", type: "Property"
+            }]
+        }, {
+            code: "({ async[2]() {} })",
+            output: "({ async 2() {} })",
+            parserOptions: { ecmaVersion: 8 },
+            errors: [{
+                message: "Unnecessarily computed property [2] found.", type: "Property"
+            }]
+        }, {
+            code: "({ get['foo']() {} })",
+            output: "({ get'foo'() {} })",
+            errors: [{
+                message: "Unnecessarily computed property ['foo'] found.", type: "Property"
+            }]
+        }, {
+            code: "({ *[2]() {} })",
+            output: "({ *2() {} })",
+            errors: [{
+                message: "Unnecessarily computed property [2] found.", type: "Property"
+            }]
         }
     ]
 });
diff --git a/tests/lib/rules/object-shorthand.js b/tests/lib/rules/object-shorthand.js
index 8ef707d52..d961849ae 100644
--- a/tests/lib/rules/object-shorthand.js
+++ b/tests/lib/rules/object-shorthand.js
@@ -973,6 +973,17 @@ ruleTester.run("object-shorthand", rule, {
             `,
             options: ["always", { avoidExplicitReturnArrows: true }],
             errors: [METHOD_ERROR]
+        },
+        {
+            code: "({ a: (function(){ return foo; }) })",
+            output: "({ a(){ return foo; } })",
+            errors: [METHOD_ERROR]
+        },
+        {
+            code: "({ a: (() => { return foo; }) })",
+            output: "({ a() { return foo; } })",
+            options: ["always", { avoidExplicitReturnArrows: true }],
+            errors: [METHOD_ERROR]
         }
     ]
 });
diff --git a/tests/lib/rules/quotes.js b/tests/lib/rules/quotes.js
index 98ccd043e..3201e594c 100644
--- a/tests/lib/rules/quotes.js
+++ b/tests/lib/rules/quotes.js
@@ -68,8 +68,10 @@ ruleTester.run("quotes", rule, {
         { code: "import a from \"a\"; import b from 'b';", options: ["backtick"], parserOptions: { sourceType: "module" } },
         { code: "export * from \"a\"; export * from 'b';", options: ["backtick"], parserOptions: { sourceType: "module" } },
 
-        // `backtick` should not warn property names (not computed).
-        { code: "var obj = {\"key0\": 0, 'key1': 1};", options: ["backtick"], parserOptions: { ecmaVersion: 6 } }
+        // `backtick` should not warn property/method names (not computed).
+        { code: "var obj = {\"key0\": 0, 'key1': 1};", options: ["backtick"], parserOptions: { ecmaVersion: 6 } },
+        { code: "class Foo { 'bar'(){} }", options: ["backtick"], parserOptions: { ecmaVersion: 6 } },
+        { code: "class Foo { static ''(){} }", options: ["backtick"], parserOptions: { ecmaVersion: 6 } }
     ],
     invalid: [
         {
@@ -205,7 +207,7 @@ ruleTester.run("quotes", rule, {
             errors: [{ message: "Strings must use backtick.", type: "Literal" }]
         },
 
-        // `backtick` should not warn computed property names.
+        // `backtick` should warn computed property names.
         {
             code: "var obj = {[\"key0\"]: 0, ['key1']: 1};",
             output: "var obj = {[`key0`]: 0, [`key1`]: 1};",
@@ -216,6 +218,16 @@ ruleTester.run("quotes", rule, {
                 { message: "Strings must use backtick.", type: "Literal" }
             ]
         },
+        {
+            code: "class Foo { ['a'](){} static ['b'](){} }",
+            output: "class Foo { [`a`](){} static [`b`](){} }",
+            options: ["backtick"],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                { message: "Strings must use backtick.", type: "Literal" },
+                { message: "Strings must use backtick.", type: "Literal" }
+            ]
+        },
 
         // https://github.com/eslint/eslint/issues/7084
         {