Repository: eslint/eslint (js)

Issue:
Internal modules are relying on eslint:recommended for default options
Internal modules are reading eslint:recommended to get the default options. For example: [here](https://github.com/eslint/eslint/blob/02f0d272f4d4d6bdc944e0ead9327efdda0cee11/lib/eslint.js#L17), [here](https://github.com/eslint/eslint/blob/5b5c2361d5c35feddece4e866ddcae25b964ee7f/lib/config/config-file.js#L26), and [here](https://github.com/eslint/eslint/blob/02f0d272f4d4d6bdc944e0ead9327efdda0cee11/lib/eslint.js#L1195-L1197).

Conceptually, this doesn't really make sense -- `eslint:recommended` is supposed to be an opt-in config, so it shouldn't be where defaults are stored. It's also a refactoring hazard -- if we change `eslint:recommended`, the defaults for all configs will also get changed.

Pull request description:
Chore: don't pull default options from eslint:recommended (fixes #8374)
**What is the purpose of this pull request? (put an "X" next to item)**

[x] Other, please explain:

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (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, several internal modules used `conf/eslint-recommended.js` as a source for defaults. This made it confusing to work with `conf/eslint-recommended.js`, because it's ostensibly just a config and it shouldn't have any effect when not extending from `eslint:recommended`.

This commit separates the default configuration into its own file and removes some hardcoded defaults from the source code.

The default config is frozen to prevent anything from modifying it by mistake, so this commit depends on https://github.com/eslint/espree/pull/330.

**Is there anything you'd like reviewers to focus on?**

Nothing in particular

Patch:
diff --git a/conf/cli-options.js b/conf/default-cli-options.js
similarity index 92%
rename from conf/cli-options.js
rename to conf/default-cli-options.js
index 291007515..6dfdb5442 100644
--- a/conf/cli-options.js
+++ b/conf/default-cli-options.js
@@ -12,11 +12,9 @@ module.exports = {
     useEslintrc: true,
     envs: [],
     globals: [],
-    rules: {},
     extensions: [".js"],
     ignore: true,
     ignorePath: null,
-    parser: "", // must be empty
     cache: false,
 
     // in order to honor the cacheFile option if specified
diff --git a/conf/default-config-options.js b/conf/default-config-options.js
new file mode 100644
index 000000000..63d28c48b
--- /dev/null
+++ b/conf/default-config-options.js
@@ -0,0 +1,33 @@
+/**
+ * @fileoverview Default config options
+ * @author Teddy Katz
+ */
+
+"use strict";
+
+/**
+ * Freezes an object and all its nested properties
+ * @param {Object} obj The object to deeply freeze
+ * @returns {Object} `obj` after freezing it
+ */
+function deepFreeze(obj) {
+    if (obj === null || typeof obj !== "object") {
+        return obj;
+    }
+
+    Object.keys(obj).map(key => obj[key]).forEach(deepFreeze);
+    return Object.freeze(obj);
+}
+
+module.exports = deepFreeze({
+    env: {},
+    globals: {},
+    rules: {},
+    settings: {},
+    parser: "espree",
+    parserOptions: {
+        ecmaVersion: 5,
+        sourceType: "script",
+        ecmaFeatures: {}
+    }
+});
diff --git a/conf/eslint-recommended.js b/conf/eslint-recommended.js
index 79340ce7d..29cc3de29 100755
--- a/conf/eslint-recommended.js
+++ b/conf/eslint-recommended.js
@@ -10,8 +10,6 @@
 /* eslint-disable sort-keys */
 
 module.exports = {
-    parser: "espree",
-
     rules: {
 
         /* eslint-enable sort-keys */
diff --git a/docs/rules/no-undefined.md b/docs/rules/no-undefined.md
index 19458c6ee..ff8bea720 100644
--- a/docs/rules/no-undefined.md
+++ b/docs/rules/no-undefined.md
@@ -1,6 +1,6 @@
 # Disallow Use of `undefined` Variable (no-undefined)
 
-The `undefined` variable is unique in JavaScript because it is actually a property of the global object. As such, in ECMAScript 3 it was possible to overwrite the value of `undefined`. While ECMAScript 5 disallows overwriting `undefined`, it's still possible to shadow `undefined`, such as:
+The `undefined` variable in JavaScript is actually a property of the global object. As such, in ECMAScript 3 it was possible to overwrite the value of `undefined`. While ECMAScript 5 disallows overwriting `undefined`, it's still possible to shadow `undefined`, such as:
 
 ```js
 function doSomething(data) {
@@ -14,24 +14,15 @@ function doSomething(data) {
 }
 ```
 
-This represents a problem for `undefined` that doesn't exist for `null`, which is a keyword and primitive value that can neither be overwritten nor shadowed.
+Because `undefined` can be overwritten or shadowed, reading `undefined` can give an unexpected value. (This is not the case for `null`, which is a keyword that always produces the same value.) To guard against this, you can avoid all uses of `undefined`, which is what some style guides recommend and what this rule enforces. Those style guides then also recommend:
 
-All uninitialized variables automatically get the value of `undefined`:
-
-```js
-var foo;
-
-console.log(foo === undefined);     // true (assuming no shadowing)
-```
-
-For this reason, it's not necessary to explicitly initialize a variable to `undefined`.
-
-Taking all of this into account, some style guides forbid the use of `undefined`, recommending instead:
-
-* Variables that should be `undefined` are simply left uninitialized.
+* Variables that should be `undefined` are simply left uninitialized. (All uninitialized variables automatically get the value of `undefined` in JavaScript.)
 * Checking if a value is `undefined` should be done with `typeof`.
 * Using the `void` operator to generate the value of `undefined` if necessary.
 
+As an alternative, you can use the [no-global-assign](no-global-assign.md) and [no-shadow-restricted-names](no-shadow-restricted-names.md) rules to prevent `undefined` from being shadowed or assigned a different value. This ensures that `undefined` will always hold its original, expected value.
+
+
 ## Rule Details
 
 This rule aims to eliminate the use of `undefined`, and as such, generates a warning whenever it is used.
@@ -84,3 +75,5 @@ If you want to allow the use of `undefined` in your code, then you can safely tu
 
 * [no-undef-init](no-undef-init.md)
 * [no-void](no-void.md)
+* [no-shadow-restricted-names](no-shadow-restricted-names.md)
+* [no-global-assign](no-global-assign.md)
diff --git a/lib/cli-engine.js b/lib/cli-engine.js
index 6dd85a394..a1378502a 100644
--- a/lib/cli-engine.js
+++ b/lib/cli-engine.js
@@ -19,7 +19,7 @@ const fs = require("fs"),
     path = require("path"),
     rules = require("./rules"),
     eslint = require("./eslint"),
-    defaultOptions = require("../conf/cli-options"),
+    defaultOptions = require("../conf/default-cli-options"),
     IgnoredPaths = require("./ignored-paths"),
     Config = require("./config"),
     Plugins = require("./config/plugins"),
diff --git a/lib/config/config-file.js b/lib/config/config-file.js
index 4965851cf..1cabbbab1 100644
--- a/lib/config/config-file.js
+++ b/lib/config/config-file.js
@@ -23,7 +23,6 @@ const fs = require("fs"),
     stripBom = require("strip-bom"),
     stripComments = require("strip-json-comments"),
     stringify = require("json-stable-stringify"),
-    defaultOptions = require("../../conf/eslint-recommended"),
     requireUncached = require("require-uncached");
 
 const debug = require("debug")("eslint:config-file");
@@ -541,11 +540,6 @@ function load(filePath, applyEnvironments, relativeTo) {
             Plugins.loadAll(config.plugins);
         }
 
-        // remove parser from config if it is the default parser
-        if (config.parser === defaultOptions.parser) {
-            config.parser = null;
-        }
-
         // include full path of parser if present
         if (config.parser) {
             if (isFilePath(config.parser)) {
diff --git a/lib/eslint.js b/lib/eslint.js
index 7671b7ccc..ee5d3d2c1 100755
--- a/lib/eslint.js
+++ b/lib/eslint.js
@@ -14,7 +14,7 @@ const assert = require("assert"),
     eslintScope = require("eslint-scope"),
     levn = require("levn"),
     blankScriptAST = require("../conf/blank-script.json"),
-    DEFAULT_PARSER = require("../conf/eslint-recommended").parser,
+    defaultConfig = require("../conf/default-config-options.js"),
     replacements = require("../conf/replacements.json"),
     CodePathAnalyzer = require("./code-path-analysis/code-path-analyzer"),
     ConfigOps = require("./config/config-ops"),
@@ -453,8 +453,8 @@ function prepareConfig(config) {
     config.globals = config.globals || config.global || {};
     delete config.global;
 
-    const copiedRules = {};
-    let parserOptions = {};
+    const copiedRules = Object.assign({}, defaultConfig.rules);
+    let parserOptions = Object.assign({}, defaultConfig.parserOptions);
 
     if (typeof config.rules === "object") {
         Object.keys(config.rules).forEach(k => {
@@ -484,21 +484,18 @@ function prepareConfig(config) {
 
     const preparedConfig = {
         rules: copiedRules,
-        parser: config.parser || DEFAULT_PARSER,
-        globals: ConfigOps.merge({}, config.globals),
-        env: ConfigOps.merge({}, config.env || {}),
-        settings: ConfigOps.merge({}, config.settings || {}),
+        parser: config.parser || defaultConfig.parser,
+        globals: ConfigOps.merge(defaultConfig.globals, config.globals),
+        env: ConfigOps.merge(defaultConfig.env, config.env || {}),
+        settings: ConfigOps.merge(defaultConfig.settings, config.settings || {}),
         parserOptions: ConfigOps.merge(parserOptions, config.parserOptions || {})
     };
     const isModule = preparedConfig.parserOptions.sourceType === "module";
 
     if (isModule) {
-        if (!preparedConfig.parserOptions.ecmaFeatures) {
-            preparedConfig.parserOptions.ecmaFeatures = {};
-        }
 
         // can't have global return inside of modules
-        preparedConfig.parserOptions.ecmaFeatures.globalReturn = false;
+        preparedConfig.parserOptions.ecmaFeatures = Object.assign({}, preparedConfig.parserOptions.ecmaFeatures, { globalReturn: false });
     }
 
     preparedConfig.parserOptions.ecmaVersion = normalizeEcmaVersion(preparedConfig.parserOptions.ecmaVersion, isModule);
@@ -880,8 +877,8 @@ module.exports = (function() {
             currentConfig = config;
             traverser = new Traverser();
 
-            const ecmaFeatures = currentConfig.parserOptions.ecmaFeatures || {};
-            const ecmaVersion = currentConfig.parserOptions.ecmaVersion || 5;
+            const ecmaFeatures = currentConfig.parserOptions.ecmaFeatures;
+            const ecmaVersion = currentConfig.parserOptions.ecmaVersion;
 
             // gather scope data that may be needed by the rules
             scopeManager = eslintScope.analyze(ast, {
@@ -889,7 +886,7 @@ module.exports = (function() {
                 nodejsScope: ecmaFeatures.globalReturn,
                 impliedStrict: ecmaFeatures.impliedStrict,
                 ecmaVersion,
-                sourceType: currentConfig.parserOptions.sourceType || "script",
+                sourceType: currentConfig.parserOptions.sourceType,
                 fallback: Traverser.getKeys
             });
 
@@ -1183,7 +1180,7 @@ module.exports = (function() {
      * @returns {Object} Object mapping rule IDs to their default configurations
      */
     api.defaults = function() {
-        return require("../conf/eslint-recommended");
+        return defaultConfig;
     };
 
     /**
diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js
index bb4433cc4..44bd6d2c8 100644
--- a/lib/rules/brace-style.js
+++ b/lib/rules/brace-style.js
@@ -62,10 +62,12 @@ module.exports = {
         function removeNewlineBetween(firstToken, secondToken) {
             const textRange = [firstToken.range[1], secondToken.range[0]];
             const textBetween = sourceCode.text.slice(textRange[0], textRange[1]);
-            const NEWLINE_REGEX = astUtils.createGlobalLinebreakMatcher();
 
             // Don't do a fix if there is a comment between the tokens
-            return fixer => fixer.replaceTextRange(textRange, textBetween.trim() ? null : textBetween.replace(NEWLINE_REGEX, ""));
+            if (textBetween.trim()) {
+                return null;
+            }
+            return fixer => fixer.replaceTextRange(textRange, " ");
         }
 
         /**
diff --git a/lib/rules/no-self-compare.js b/lib/rules/no-self-compare.js
index 54f907f59..5beaa181b 100644
--- a/lib/rules/no-self-compare.js
+++ b/lib/rules/no-self-compare.js
@@ -22,15 +22,28 @@ module.exports = {
     },
 
     create(context) {
+        const sourceCode = context.getSourceCode();
+
+        /**
+         * Determines whether two nodes are composed of the same tokens.
+         * @param {ASTNode} nodeA The first node
+         * @param {ASTNode} nodeB The second node
+         * @returns {boolean} true if the nodes have identical token representations
+         */
+        function hasSameTokens(nodeA, nodeB) {
+            const tokensA = sourceCode.getTokens(nodeA);
+            const tokensB = sourceCode.getTokens(nodeB);
+
+            return tokensA.length === tokensB.length &&
+                tokensA.every((token, index) => token.type === tokensB[index].type && token.value === tokensB[index].value);
+        }
 
         return {
 
             BinaryExpression(node) {
-                const operators = ["===", "==", "!==", "!=", ">", "<", ">=", "<="];
+                const operators = new Set(["===", "==", "!==", "!=", ">", "<", ">=", "<="]);
 
-                if (operators.indexOf(node.operator) > -1 &&
-                    (node.left.type === "Identifier" && node.right.type === "Identifier" && node.left.name === node.right.name ||
-                    node.left.type === "Literal" && node.right.type === "Literal" && node.left.value === node.right.value)) {
+                if (operators.has(node.operator) && hasSameTokens(node.left, node.right)) {
                     context.report({ node, message: "Comparing to itself is potentially pointless." });
                 }
             }
diff --git a/lib/util/source-code-util.js b/lib/util/source-code-util.js
index 892c32d22..a16a0d977 100644
--- a/lib/util/source-code-util.js
+++ b/lib/util/source-code-util.js
@@ -12,7 +12,7 @@
 const CLIEngine = require("../cli-engine"),
     eslint = require("../eslint"),
     globUtil = require("./glob-util"),
-    baseDefaultOptions = require("../../conf/cli-options");
+    baseDefaultOptions = require("../../conf/default-cli-options");
 
 const debug = require("debug")("eslint:source-code-util");
 


Test patch:
diff --git a/tests/fixtures/config-file/js/.eslintrc.parser3.js b/tests/fixtures/config-file/js/.eslintrc.parser3.js
index 66463377b..fd5f04ff1 100644
--- a/tests/fixtures/config-file/js/.eslintrc.parser3.js
+++ b/tests/fixtures/config-file/js/.eslintrc.parser3.js
@@ -1,4 +1,4 @@
-var defaultOptions = require("../../../../conf/eslint-recommended");
+var defaultOptions = require("../../../../conf/default-config-options");
 
 module.exports = {
     parser: defaultOptions.parser,
diff --git a/tests/lib/config/autoconfig.js b/tests/lib/config/autoconfig.js
index 7f354f155..5cd193122 100644
--- a/tests/lib/config/autoconfig.js
+++ b/tests/lib/config/autoconfig.js
@@ -12,7 +12,7 @@
 const assert = require("chai").assert,
     autoconfig = require("../../../lib/config/autoconfig"),
     sourceCodeUtil = require("../../../lib/util/source-code-util"),
-    baseDefaultOptions = require("../../../conf/cli-options");
+    baseDefaultOptions = require("../../../conf/default-cli-options");
 
 const defaultOptions = Object.assign({}, baseDefaultOptions, { cwd: process.cwd() });
 
diff --git a/tests/lib/config/config-file.js b/tests/lib/config/config-file.js
index a8a3bfd79..405133a0f 100644
--- a/tests/lib/config/config-file.js
+++ b/tests/lib/config/config-file.js
@@ -492,11 +492,11 @@ describe("ConfigFile", () => {
             });
         });
 
-        it("should not interpret parser module name or path when parser is set to default parser in a JavaScript file", () => {
+        it("should interpret parser module name or path when parser is set to default parser in a JavaScript file", () => {
             const config = ConfigFile.load(getFixturePath("js/.eslintrc.parser3.js"));
 
             assert.deepEqual(config, {
-                parser: null,
+                parser: require.resolve("espree"),
                 parserOptions: {},
                 env: {},
                 globals: {},
diff --git a/tests/lib/rules/brace-style.js b/tests/lib/rules/brace-style.js
index 75c86571e..1dc11ac38 100644
--- a/tests/lib/rules/brace-style.js
+++ b/tests/lib/rules/brace-style.js
@@ -200,7 +200,7 @@ ruleTester.run("brace-style", rule, {
     invalid: [
         {
             code: "if (f) {\nbar;\n}\nelse\nbaz;",
-            output: "if (f) {\nbar;\n}else\nbaz;",
+            output: "if (f) {\nbar;\n} else\nbaz;",
             errors: [{ message: CLOSE_MESSAGE, type: "Punctuator" }]
         },
         {
@@ -216,83 +216,83 @@ ruleTester.run("brace-style", rule, {
         },
         {
             code: "function foo() \n { \n return; }",
-            output: "function foo()  { \n return; \n}",
+            output: "function foo() { \n return; \n}",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }, { message: CLOSE_MESSAGE_SINGLE, type: "Punctuator" }]
         },
         {
             code: "!function foo() \n { \n return; }",
-            output: "!function foo()  { \n return; \n}",
+            output: "!function foo() { \n return; \n}",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }, { message: CLOSE_MESSAGE_SINGLE, type: "Punctuator" }]
         },
         {
             code: "if (foo) \n { \n bar(); }",
-            output: "if (foo)  { \n bar(); \n}",
+            output: "if (foo) { \n bar(); \n}",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }, { message: CLOSE_MESSAGE_SINGLE, type: "Punctuator" }]
         },
         {
             code: "if (foo) \n { \n bar(); }",
-            output: "if (foo)  { \n bar(); \n}",
+            output: "if (foo) { \n bar(); \n}",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }, { message: CLOSE_MESSAGE_SINGLE, type: "Punctuator" }]
         },
         {
             code: "if (a) { \nb();\n } else \n { c(); }",
-            output: "if (a) { \nb();\n } else  {\n c(); \n}",
+            output: "if (a) { \nb();\n } else {\n c(); \n}",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }, { message: BODY_MESSAGE, type: "Punctuator" }, { message: CLOSE_MESSAGE_SINGLE, type: "Punctuator" }]
         },
         {
             code: "while (foo) \n { \n bar(); }",
-            output: "while (foo)  { \n bar(); \n}",
+            output: "while (foo) { \n bar(); \n}",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }, { message: CLOSE_MESSAGE_SINGLE, type: "Punctuator" }]
         },
         {
             code: "for (;;) \n { \n bar(); }",
-            output: "for (;;)  { \n bar(); \n}",
+            output: "for (;;) { \n bar(); \n}",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }, { message: CLOSE_MESSAGE_SINGLE, type: "Punctuator" }]
         },
         {
             code: "with (foo) \n { \n bar(); }",
-            output: "with (foo)  { \n bar(); \n}",
+            output: "with (foo) { \n bar(); \n}",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }, { message: CLOSE_MESSAGE_SINGLE, type: "Punctuator" }]
         },
         {
             code: "switch (foo) \n { \n case \"bar\": break; }",
-            output: "switch (foo)  { \n case \"bar\": break; \n}",
+            output: "switch (foo) { \n case \"bar\": break; \n}",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }, { message: CLOSE_MESSAGE_SINGLE, type: "Punctuator" }]
         },
         {
             code: "switch (foo) \n { }",
-            output: "switch (foo)  { }",
+            output: "switch (foo) { }",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }]
         },
         {
             code: "try \n { \n bar(); \n } catch (e) {}",
-            output: "try  { \n bar(); \n } catch (e) {}",
+            output: "try { \n bar(); \n } catch (e) {}",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }]
         },
         {
             code: "try { \n bar(); \n } catch (e) \n {}",
-            output: "try { \n bar(); \n } catch (e)  {}",
+            output: "try { \n bar(); \n } catch (e) {}",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }]
         },
         {
             code: "do \n { \n bar(); \n} while (true)",
-            output: "do  { \n bar(); \n} while (true)",
+            output: "do { \n bar(); \n} while (true)",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }]
         },
         {
             code: "for (foo in bar) \n { \n baz(); \n }",
-            output: "for (foo in bar)  { \n baz(); \n }",
+            output: "for (foo in bar) { \n baz(); \n }",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }]
         },
         {
             code: "for (foo of bar) \n { \n baz(); \n }",
-            output: "for (foo of bar)  { \n baz(); \n }",
+            output: "for (foo of bar) { \n baz(); \n }",
             parserOptions: { ecmaVersion: 6 },
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }]
         },
         {
             code: "try { \n bar(); \n }\ncatch (e) {\n}",
-            output: "try { \n bar(); \n }catch (e) {\n}",
+            output: "try { \n bar(); \n } catch (e) {\n}",
             errors: [{ message: CLOSE_MESSAGE, type: "Punctuator" }]
         },
         {
@@ -302,7 +302,7 @@ ruleTester.run("brace-style", rule, {
         },
         {
             code: "if (a) { \nb();\n } \n else { \nc();\n }",
-            output: "if (a) { \nb();\n }  else { \nc();\n }",
+            output: "if (a) { \nb();\n } else { \nc();\n }",
             errors: [{ message: CLOSE_MESSAGE, type: "Punctuator" }]
         },
         {
@@ -443,7 +443,7 @@ ruleTester.run("brace-style", rule, {
         },
         {
             code: "if (a) { b(); }\nelse { c(); }",
-            output: "if (a) { b(); }else { c(); }",
+            output: "if (a) { b(); } else { c(); }",
             options: ["1tbs", { allowSingleLine: true }],
             errors: [{ message: CLOSE_MESSAGE, type: "Punctuator" }]
         },
@@ -467,49 +467,49 @@ ruleTester.run("brace-style", rule, {
         },
         {
             code: "switch (foo) \n { \n case \"bar\": break; }",
-            output: "switch (foo)  { \n case \"bar\": break; \n}",
+            output: "switch (foo) { \n case \"bar\": break; \n}",
             options: ["1tbs", { allowSingleLine: true }],
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }, { message: CLOSE_MESSAGE_SINGLE, type: "Punctuator" }]
         },
         {
             code: "switch (foo) \n { }",
-            output: "switch (foo)  { }",
+            output: "switch (foo) { }",
             options: ["1tbs", { allowSingleLine: true }],
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }]
         },
         {
             code: "try {  bar(); }\ncatch (e) { baz();  }",
-            output: "try {  bar(); }catch (e) { baz();  }",
+            output: "try {  bar(); } catch (e) { baz();  }",
             options: ["1tbs", { allowSingleLine: true }],
             errors: [{ message: CLOSE_MESSAGE, type: "Punctuator" }]
         },
         {
             code: "try \n { \n bar(); \n } catch (e) {}",
-            output: "try  { \n bar(); \n } catch (e) {}",
+            output: "try { \n bar(); \n } catch (e) {}",
             options: ["1tbs", { allowSingleLine: true }],
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }]
         },
         {
             code: "try { \n bar(); \n } catch (e) \n {}",
-            output: "try { \n bar(); \n } catch (e)  {}",
+            output: "try { \n bar(); \n } catch (e) {}",
             options: ["1tbs", { allowSingleLine: true }],
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }]
         },
         {
             code: "do \n { \n bar(); \n} while (true)",
-            output: "do  { \n bar(); \n} while (true)",
+            output: "do { \n bar(); \n} while (true)",
             options: ["1tbs", { allowSingleLine: true }],
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }]
         },
         {
             code: "for (foo in bar) \n { \n baz(); \n }",
-            output: "for (foo in bar)  { \n baz(); \n }",
+            output: "for (foo in bar) { \n baz(); \n }",
             options: ["1tbs", { allowSingleLine: true }],
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }]
         },
         {
             code: "try { \n bar(); \n }\ncatch (e) {\n}",
-            output: "try { \n bar(); \n }catch (e) {\n}",
+            output: "try { \n bar(); \n } catch (e) {\n}",
             options: ["1tbs", { allowSingleLine: true }],
             errors: [{ message: CLOSE_MESSAGE, type: "Punctuator" }]
         },
@@ -521,7 +521,7 @@ ruleTester.run("brace-style", rule, {
         },
         {
             code: "if (a) { \nb();\n } \n else { \nc();\n }",
-            output: "if (a) { \nb();\n }  else { \nc();\n }",
+            output: "if (a) { \nb();\n } else { \nc();\n }",
             options: ["1tbs", { allowSingleLine: true }],
             errors: [{ message: CLOSE_MESSAGE, type: "Punctuator" }]
         },
@@ -599,12 +599,12 @@ ruleTester.run("brace-style", rule, {
         },
         {
             code: "class Foo\n{\n}",
-            output: "class Foo{\n}",
+            output: "class Foo {\n}",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }]
         },
         {
             code: "(class\n{\n})",
-            output: "(class{\n})",
+            output: "(class {\n})",
             errors: [{ message: OPEN_MESSAGE, type: "Punctuator" }]
         },
         {
@@ -634,6 +634,30 @@ ruleTester.run("brace-style", rule, {
             output: "class\nFoo\n{}",
             options: ["allman"],
             errors: [{ message: OPEN_MESSAGE_ALLMAN, type: "Punctuator" }]
+        },
+
+        // https://github.com/eslint/eslint/issues/7621
+        {
+            code: `
+                if (foo)
+                {
+                    bar
+                }
+                else {
+                    baz
+                }
+            `,
+            output: `
+                if (foo) {
+                    bar
+                } else {
+                    baz
+                }
+            `,
+            errors: [
+                { message: OPEN_MESSAGE, type: "Punctuator" },
+                { message: CLOSE_MESSAGE, type: "Punctuator" }
+            ]
         }
     ]
 });
diff --git a/tests/lib/rules/no-self-compare.js b/tests/lib/rules/no-self-compare.js
index 6b19bdbfb..b937a9214 100644
--- a/tests/lib/rules/no-self-compare.js
+++ b/tests/lib/rules/no-self-compare.js
@@ -21,10 +21,9 @@ const ruleTester = new RuleTester();
 ruleTester.run("no-self-compare", rule, {
     valid: [
         "if (x === y) { }",
-        "if (f() === f()) { }",
-        "if (a[1] === a[1]) { }",
         "if (1 === 2) { }",
-        "y=x*x"
+        "y=x*x",
+        "foo.bar.baz === foo.bar.qux"
     ],
     invalid: [
         { code: "if (x === x) { }", errors: [{ message: "Comparing to itself is potentially pointless.", type: "BinaryExpression" }] },
@@ -39,6 +38,7 @@ ruleTester.run("no-self-compare", rule, {
         { code: "x > x", errors: [{ message: "Comparing to itself is potentially pointless.", type: "BinaryExpression" }] },
         { code: "x < x", errors: [{ message: "Comparing to itself is potentially pointless.", type: "BinaryExpression" }] },
         { code: "x >= x", errors: [{ message: "Comparing to itself is potentially pointless.", type: "BinaryExpression" }] },
-        { code: "x <= x", errors: [{ message: "Comparing to itself is potentially pointless.", type: "BinaryExpression" }] }
+        { code: "x <= x", errors: [{ message: "Comparing to itself is potentially pointless.", type: "BinaryExpression" }] },
+        { code: "foo.bar().baz.qux >= foo.bar ().baz .qux", errors: [{ message: "Comparing to itself is potentially pointless.", type: "BinaryExpression" }] }
     ]
 });