Repository: eslint/eslint
Pull request #11628 (2019-04-18 01:15:16)

Docs: add missing items to migration guide
<!--
    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] Documentation update

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

**What changes did you make? (Give an overview)**

This updates the migration guide to add the missing items mentioned in https://github.com/eslint/eslint/pull/11515#issue-261757235.

It does not add https://github.com/eslint/eslint/issues/9505 yet because there is some debate about whether that feature will still make it into the release.

**Is there anything you'd like reviewers to focus on?**





Patch:
diff --git a/.markdownlint.yml b/.markdownlint.yml
new file mode 100644
index 000000000..7bbf13588
--- /dev/null
+++ b/.markdownlint.yml
@@ -0,0 +1,20 @@
+default: true
+
+# Exclusions for deliberate/widespread violations
+MD001: false     # Header levels should only increment by one level at a time
+MD002: false     # First header should be a h1 header
+MD007:           # Unordered list indentation
+    indent: 4
+MD012: false     # Multiple consecutive blank lines
+MD013: false     # Line length
+MD014: false     # Dollar signs used before commands without showing output
+MD019: false     # Multiple spaces after hash on atx style header
+MD021: false     # Multiple spaces inside hashes on closed atx style header
+MD024: false     # Multiple headers with the same content
+MD026: false     # Trailing punctuation in header
+MD029: false     # Ordered list item prefix
+MD030: false     # Spaces after list markers
+MD033: false     # Allow inline HTML
+MD034: false     # Bare URL used
+MD040: false     # Fenced code blocks should have a language specified
+MD041: false     # First line in file should be a top level header
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 03cd84ead..40aa2a8a4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,7 +3,7 @@ node_js:
     - "8"
     - "10"
     - "11"
-
+    - "12"
 branches:
     only:
     - master
diff --git a/Makefile.js b/Makefile.js
index 08e102564..2aea2619d 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -385,29 +385,7 @@ function getFirstVersionOfDeletion(filePath) {
  * @private
  */
 function lintMarkdown(files) {
-    const config = {
-            default: true,
-
-            // Exclusions for deliberate/widespread violations
-            MD001: false, // Header levels should only increment by one level at a time
-            MD002: false, // First header should be a h1 header
-            MD007: { // Unordered list indentation
-                indent: 4
-            },
-            MD012: false, // Multiple consecutive blank lines
-            MD013: false, // Line length
-            MD014: false, // Dollar signs used before commands without showing output
-            MD019: false, // Multiple spaces after hash on atx style header
-            MD021: false, // Multiple spaces inside hashes on closed atx style header
-            MD024: false, // Multiple headers with the same content
-            MD026: false, // Trailing punctuation in header
-            MD029: false, // Ordered list item prefix
-            MD030: false, // Spaces after list markers
-            MD033: false, // Allow inline HTML
-            MD034: false, // Bare URL used
-            MD040: false, // Fenced code blocks should have a language specified
-            MD041: false // First line in file should be a top level header
-        },
+    const config = yaml.safeLoad(fs.readFileSync(path.join(__dirname, "./.markdownlint.yml"), "utf8")),
         result = markdownlint.sync({
             files,
             config,
diff --git a/README.md b/README.md
index 9b406e451..1cd33338d 100644
--- a/README.md
+++ b/README.md
@@ -245,9 +245,9 @@ The following companies, organizations, and individuals support ESLint's ongoing
 <!-- NOTE: This section is autogenerated. Do not manually edit.-->
 <!--sponsorsstart-->
 <h3>Gold Sponsors</h3>
-<p><a href="https://www.gitkraken.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fgitkraken.com&height=96" alt="GitKraken" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F098e3bd0-4d57-11e8-9324-0f6cc1f92bf1.png&height=96" alt="Airbnb" height="96"></a> <a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fres.cloudinary.com%2Fopencollective%2Fimage%2Fupload%2Fv1508519428%2FS9gk78AS_400x400_fulq2l.jpg&height=96" alt="Facebook Open Source" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2Fbbdb9cc0-3b5d-11e9-9537-ad85092287b8.png&height=96" alt="Badoo" height="96"></a> <a href="http://salesforce.com"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fsalesforce.com&height=96" alt="Salesforce" height="96"></a></p><h3>Silver Sponsors</h3>
+<p><a href="https://www.gitkraken.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fgitkraken.com&height=96" alt="GitKraken" height="96"></a> <a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fres.cloudinary.com%2Fopencollective%2Fimage%2Fupload%2Fv1508519428%2FS9gk78AS_400x400_fulq2l.jpg&height=96" alt="Facebook Open Source" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2Fbbdb9cc0-3b5d-11e9-9537-ad85092287b8.png&height=96" alt="Badoo" height="96"></a> <a href="http://salesforce.com"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fsalesforce.com&height=96" alt="Salesforce" height="96"></a></p><h3>Silver Sponsors</h3>
 <p><a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F68ed8b70-ebf2-11e6-9958-cb7e79408c56.png&height=96" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
-<p><a href="http://faithlife.com/ref/about"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Ffaithlife.com&height=96" alt="Faithlife" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fjsheroes.io&height=96" alt="JSHeroes " height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Ffiresticktricks.com&height=96" alt="Fire Stick Tricks" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F431b60c0-6062-11e9-b486-e1bb2d3f0d94.png&height=96" alt="Marfeel" height="32"></a> <a href="https://usave.co.uk/utilities/broadband/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fusave.co.uk&height=96" alt="Usave" height="32"></a></p>
+<p><a href="http://faithlife.com/ref/about"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Ffaithlife.com&height=96" alt="Faithlife" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fjsheroes.io&height=96" alt="JSHeroes " height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Ffiresticktricks.com&height=96" alt="Fire Stick Tricks" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F431b60c0-6062-11e9-b486-e1bb2d3f0d94.png&height=96" alt="Marfeel" height="32"></a> <a href="https://usave.co.uk/utilities/broadband/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fusave.co.uk&height=96" alt="Usave" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F31835450-5a98-11e9-adb1-e17fccd4c65e.png&height=96" alt="TekHattan" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fthemeisle.com&height=96" alt="ThemeIsle" height="32"></a></p>
 <!--sponsorsend-->
 
 ## Technology Sponsors
diff --git a/docs/developer-guide/working-with-rules.md b/docs/developer-guide/working-with-rules.md
index d189d53ad..c49863501 100644
--- a/docs/developer-guide/working-with-rules.md
+++ b/docs/developer-guide/working-with-rules.md
@@ -169,6 +169,13 @@ This method returns the scope which has the following types:
 **※2** Only if the `for` statement defines the iteration variable as a block-scoped variable (E.g., `for (let i = 0;;) {}`).<br>
 **※3** The scope of the closest ancestor node which has own scope. If the closest ancestor node has multiple scopes then it chooses the innermost scope (E.g., the `Program` node has a `global` scope and a `module` scope if `Program#sourceType` is `"module"`. The innermost scope is the `module` scope.).
 
+The returned value is a [`Scope` object](scope-manager-interface.md) defined by the `eslint-scope` package. The `Variable` objects of global variables have some additional properties.
+
+* `variable.writeable` (`boolean | undefined`) ... If `true`, this global variable can be assigned arbitrary value. If `false`, this global variable is read-only.
+* `variable.eslintExplicitGlobal` (`boolean | undefined`) ... If `true`, this global variable was defined by a `/* globals */` directive comment in the source code file.
+* `variable.eslintExplicitGlobalComments` (`Comment[] | undefined`) ... The array of `/* globals */` directive comments which defined this global variable in the source code file. This property is `undefined` if there are no `/* globals */` directive comments.
+* `variable.eslintImplicitGlobalSetting` (`"readonly" | "writable" | undefined`) ... The configured value in config files. This can be different from `variable.writeable` if there are `/* globals */` directive comments.
+
 ### context.report()
 
 The main method you'll use is `context.report()`, which publishes a warning or error (depending on the configuration being used). This method accepts a single argument, which is an object containing the following properties:
diff --git a/docs/rules/no-caller.md b/docs/rules/no-caller.md
index ff408688a..f5688df38 100644
--- a/docs/rules/no-caller.md
+++ b/docs/rules/no-caller.md
@@ -10,7 +10,7 @@ function foo() {
 
 ## Rule Details
 
-This rule is aimed at discouraging the use of deprecated and sub-optimal code, but disallowing the use of `arguments.caller` and `arguments.callee`. As such, it will warn when `arguments.caller` and `arguments.callee` are used.
+This rule is aimed at discouraging the use of deprecated and sub-optimal code by disallowing the use of `arguments.caller` and `arguments.callee`. As such, it will warn when `arguments.caller` and `arguments.callee` are used.
 
 Examples of **incorrect** code for this rule:
 
diff --git a/docs/rules/no-redeclare.md b/docs/rules/no-redeclare.md
index d0d71e5de..c3b8422cf 100644
--- a/docs/rules/no-redeclare.md
+++ b/docs/rules/no-redeclare.md
@@ -27,7 +27,7 @@ a = 10;
 
 ## Options
 
-This rule takes one optional argument, an object with a boolean property `"builtinGlobals"`. It defaults to `false`.
+This rule takes one optional argument, an object with a boolean property `"builtinGlobals"`. It defaults to `true`.
 If set to `true`, this rule also checks redeclaration of built-in globals, such as `Object`, `Array`, `Number`...
 
 ### builtinGlobals
diff --git a/docs/user-guide/migrating-to-6.0.0.md b/docs/user-guide/migrating-to-6.0.0.md
index cace85e78..775d19e7e 100644
--- a/docs/user-guide/migrating-to-6.0.0.md
+++ b/docs/user-guide/migrating-to-6.0.0.md
@@ -14,11 +14,15 @@ The lists below are ordered roughly by the number of users each change is expect
 1. [The `comma-dangle` rule is now more strict by default](#comma-dangle-updates)
 1. [The `no-confusing-arrow` rule is now more lenient by default](#no-confusing-arrow-updates)
 1. [Overrides in a config file can now match dotfiles](#overrides-dotfiles)
+1. [Overrides in an extended config file can now be overridden by a parent config file](#overrides-precedence)
+1. [Configuration values for globals are now validated](#globals-validation)
 1. [The deprecated `experimentalObjectRestSpread` option has been removed](#experimental-object-rest-spread)
+1. [User-provided regular expressions in rule options are parsed with the unicode flag](#unicode-regexes)
 
 ### Breaking changes for plugin/custom rule developers
 
 1. [Plugin authors may need to update installation instructions](#plugin-documentation)
+1. [`RuleTester` now validates against invalid `default` keywords in rule schemas](#rule-tester-defaults)
 1. [The `eslintExplicitGlobalComment` scope analysis property has been removed](#eslintExplicitGlobalComment)
 
 ### Breaking changes for integration developers
@@ -103,7 +107,7 @@ As a rule of thumb: With ESLint v6, plugins should always be installed locally,
 
 * The `ecmaVersion` parser option is set to something other than a number, such as the string `"2015"`. (Previously, a non-number option would simply be ignored.)
 * The `sourceType: "module"` parser option is set while `ecmaVersion` is set to `5` or left unspecified. (Previously, setting `sourceType: "module"` would implicitly cause `ecmaVersion` to be set to a minimum of 2015, which could be surprising.)
-* Setting `sourceType` to anything other than `"script"` or `"module"` now causes an error to be thrown.
+* The `sourceType` is set to anything other than `"script"` or `"module"`.
 
 **To address:** If your config sets `ecmaVersion` to something other than a number, you can restore the previous behavior by removing `ecmaVersion`. (However, you may want to double-check that your config is actually working as expected.) If your config sets `parserOptions: { sourceType: "module" }` without also setting `parserOptions.ecmaVersion`, you should add `parserOptions: { ecmaVersion: 2015 }` to restore the previous behavior.
 
@@ -179,6 +183,59 @@ Due to a bug, the glob patterns in a `files` list in an `overrides` section of a
 
 **Related issue(s):** [eslint/eslint#11201](https://github.com/eslint/eslint/issues/11201)
 
+## <a name="overrides-precedence"></a> Overrides in an extended config file can now be overridden by a parent config file
+
+**Note:** This update is planned, but has not been implemented in the latest alpha release yet.
+
+Due to a bug, it was previously the case that an `overrides` block in a shareable config had precedence over the top level of a parent config. For example, with the following config setup, the `semi` rule would end up enabled even though it was explicitly disabled in the end user's config:
+
+```js
+// .eslintrc.js
+module.exports = {
+  extends: ["foo"],
+  rules: {
+    semi: "off"
+  }
+};
+```
+
+```js
+// eslint-config-foo/index.js
+module.exports = {
+  overrides: {
+    files: ["*.js"],
+    rules: {
+      semi: "error"
+    }
+  }
+};
+```
+
+In ESLint v6.0.0, a parent config always has precedence over extended configs, even with `overrides` blocks.
+
+**To address:** We expect the impact of this issue to be very low because most shareable configs don't use `overrides` blocks. However, if you use a shareable config with `overrides` blocks, you might encounter a change in behavior due to something that is explicitly specified in your config but was inactive until now. If you would rather inherit the behavior from the shareable config, simply remove the corresponding entry from your own config. (In the example above, the previous behavior could be restored by removing `semi: "off"` from `.eslintrc.js`.)
+
+**Related issue(s):** [eslint/eslint#11510](https://github.com/eslint/eslint/issues/11510)
+
+## <a name="globals-validation"></a> Configuration values for globals are now validated
+
+Previously, when configuring a set of global variables with an object, it was possible to use anything as the values of the object. An unknown value would be treated the same as `"writable"`.
+
+```js
+// .eslintrc.js
+module.exports = {
+  globals: {
+    foo: "readonly",
+    bar: "writable",
+    baz: "hello!" // ???
+  }
+};
+```
+
+With this change, any unknown values in a `globals` object result in a config validation error.
+
+**To address:** If you see config validation errors related to globals after updating, ensure that all values configured for globals are either `readonly`, `writable`, or `off`. (ESLint also accepts some alternate spellings and variants for compatibility.)
+
 ## <a name="experimental-object-rest-spread"></a> The depreacted `experimentalObjectRestSpread` option has been removed
 
 Previously, when using the default parser, a config could use the `experimentalObjectRestSpread` option to enable parsing support for object rest/spread properties:
@@ -209,14 +266,30 @@ If you're not sure which config file needs to be updated, it may be useful to ru
 
 **Related issue(s):** [eslint/eslint#9990](https://github.com/eslint/eslint/issues/9990)
 
+## <a name="unicode-regexes"></a> User-provided regular expressions in rule options are parsed with the unicode flag
+
+Rules like [`max-len`](/docs/rules/max-len) accept a string option which is interpreted as a regular expression. In ESLint v6.0.0, these regular expressions are interpreted with the [unicode flag](https://mathiasbynens.be/notes/es6-unicode-regex), which should exhibit more reasonable behavior when matching characters like astral symbols. Unicode regexes also validate escape sequences more strictly than non-unicode regexes.
+
+**To address:** If you get rule option validation errors after upgrading, ensure that any regular expressions in your rule options have no invalid escape sequences.
+
+**Related issue(s):** [eslint/eslint#11423](https://github.com/eslint/eslint/issues/11423)
+
 ---
 
 ## <a name="plugin-documentation"></a> Plugin authors may need to update installation instructions
 
-If you maintain a plugin and provide installation instructions, you should ensure that the installation instructions are up to date with the [user-facing changes to how plugins are loaded](#package-loading-simplification). In particular, if your plugin was generated with the [`generator-eslint`](https://github.com/eslint/generator-eslint) package, it likely contains outdated instructions for how to use the plugin with global ESLint installations.)
+If you maintain a plugin and provide installation instructions, you should ensure that the installation instructions are up to date with the [user-facing changes to how plugins are loaded](#package-loading-simplification). In particular, if your plugin was generated with the [`generator-eslint`](https://github.com/eslint/generator-eslint) package, it likely contains outdated instructions for how to use the plugin with global ESLint installations.
 
 **Related issue(s):** [eslint/rfcs#7](https://github.com/eslint/rfcs/pull/7)
 
+## <a name="rule-tester-defaults"></a> `RuleTester` now validates against invalid `default` keywords in rule schemas
+
+In some cases, rule schemas can use the `default` keyword to automatically specify default values for rule options. However, the `default` keyword is only effective in certain schema locations, and is ignored elsewhere, which creates a risk of bugs if a rule incorrectly expects a default value to be provided as a rule option. In ESLint v6.0.0, `RuleTester` will raise an error if a rule has an invalid `default` keyword in its schema.
+
+**To address:** If `RuleTester` starts reporting an error about an invalid default, you can remove the `default` property at the indicated location in your rule schema, and the rule will behave the same way. (If this happens, you might also want to verify that the rule behaves correctly when no option value is provided in that location.)
+
+**Related issue(s):** [eslint/eslint#11473](https://github.com/eslint/eslint/issues/11473)
+
 ## <a name="eslintExplicitGlobalComment"></a> The `eslintExplicitGlobalComment` scope analysis property has been removed
 
 **Note:** This update is planned, but has not been implemented in the latest alpha release yet.
@@ -225,7 +298,7 @@ Previously, ESLint would add an `eslintExplicitGlobalComment` property to `Varia
 
 **To address:** If you maintain a rule that uses the `eslintExplicitGlobalComment` property, update it to use the `eslintExplicitGlobalComments` property as a list instead.
 
-**Related issue(s):**: [eslint/rfcs#17](https://github.com/eslint/rfcs/pull/17)
+**Related issue(s):** [eslint/rfcs#17](https://github.com/eslint/rfcs/pull/17)
 
 ---
 
diff --git a/lib/config/config-ops.js b/lib/config/config-ops.js
index 48a8302d9..beca8ca2c 100644
--- a/lib/config/config-ops.js
+++ b/lib/config/config-ops.js
@@ -385,14 +385,14 @@ module.exports = {
             case "true":
             case "writeable":
             case "writable":
-                return "writeable";
+                return "writable";
 
             case null:
             case false:
             case "false":
             case "readable":
             case "readonly":
-                return "readable";
+                return "readonly";
 
             default:
                 throw new Error(`'${configuredValue}' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')`);
diff --git a/lib/linter.js b/lib/linter.js
index bd3d8b235..f2297529b 100644
--- a/lib/linter.js
+++ b/lib/linter.js
@@ -70,39 +70,41 @@ const commentParser = new ConfigCommentParser();
  * @param {{exportedVariables: Object, enabledGlobals: Object}} commentDirectives Directives from comment configuration
  * @returns {void}
  */
-function addDeclaredGlobals(globalScope, configGlobals, commentDirectives) {
-    const mergedGlobalsInfo = Object.assign(
-        {},
+function addDeclaredGlobals(globalScope, configGlobals, { exportedVariables, enabledGlobals }) {
+
+    // Define configured global variables.
+    for (const id of new Set([...Object.keys(configGlobals), ...Object.keys(enabledGlobals)])) {
 
         /*
          * `ConfigOps.normalizeConfigGlobal` will throw an error if a configured global value is invalid. However, these errors would
          * typically be caught when validating a config anyway (validity for inline global comments is checked separately).
          */
-        lodash.mapValues(configGlobals, value => ({ sourceComment: null, value: ConfigOps.normalizeConfigGlobal(value) })),
-        lodash.mapValues(commentDirectives.enabledGlobals, ({ comment, value }) => ({ sourceComment: comment, value }))
-    );
+        const configValue = configGlobals[id] === void 0 ? void 0 : ConfigOps.normalizeConfigGlobal(configGlobals[id]);
+        const commentValue = enabledGlobals[id] && enabledGlobals[id].value;
+        const value = commentValue || configValue;
+        const sourceComments = enabledGlobals[id] && enabledGlobals[id].comments;
 
-    Object.keys(mergedGlobalsInfo)
-        .filter(name => mergedGlobalsInfo[name].value !== "off")
-        .forEach(name => {
-            let variable = globalScope.set.get(name);
-
-            if (!variable) {
-                variable = new eslintScope.Variable(name, globalScope);
-                if (mergedGlobalsInfo[name].sourceComment === null) {
-                    variable.eslintExplicitGlobal = false;
-                } else {
-                    variable.eslintExplicitGlobal = true;
-                    variable.eslintExplicitGlobalComment = mergedGlobalsInfo[name].sourceComment;
-                }
-                globalScope.variables.push(variable);
-                globalScope.set.set(name, variable);
-            }
-            variable.writeable = (mergedGlobalsInfo[name].value === "writeable");
-        });
+        if (value === "off") {
+            continue;
+        }
+
+        let variable = globalScope.set.get(id);
+
+        if (!variable) {
+            variable = new eslintScope.Variable(id, globalScope);
+
+            globalScope.variables.push(variable);
+            globalScope.set.set(id, variable);
+        }
+
+        variable.eslintImplicitGlobalSetting = configValue;
+        variable.eslintExplicitGlobal = sourceComments !== void 0;
+        variable.eslintExplicitGlobalComments = sourceComments;
+        variable.writeable = (value === "writable");
+    }
 
     // mark all exported variables as such
-    Object.keys(commentDirectives.exportedVariables).forEach(name => {
+    Object.keys(exportedVariables).forEach(name => {
         const variable = globalScope.set.get(name);
 
         if (variable) {
@@ -157,7 +159,7 @@ function createDisableDirectives(type, loc, value) {
  * @param {string} filename The file being checked.
  * @param {ASTNode} ast The top node of the AST.
  * @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
- * @returns {{configuredRules: Object, enabledGlobals: Object, exportedVariables: Object, problems: Problem[], disableDirectives: DisableDirective[]}}
+ * @returns {{configuredRules: Object, enabledGlobals: {value:string,comment:Token}[], exportedVariables: Object, problems: Problem[], disableDirectives: DisableDirective[]}}
  * A collection of the directive comments that were found, along with any problems that occurred when parsing
  */
 function getDirectiveComments(filename, ast, ruleMapper) {
@@ -201,11 +203,8 @@ function getDirectiveComments(filename, ast, ruleMapper) {
                     break;
 
                 case "globals":
-                case "global": {
-                    const updatedGlobals = commentParser.parseStringConfig(directiveValue, comment);
-
-                    Object.keys(updatedGlobals).forEach(globalName => {
-                        const { value } = updatedGlobals[globalName];
+                case "global":
+                    for (const [id, { value }] of Object.entries(commentParser.parseStringConfig(directiveValue, comment))) {
                         let normalizedValue;
 
                         try {
@@ -221,13 +220,21 @@ function getDirectiveComments(filename, ast, ruleMapper) {
                                 endColumn: comment.loc.end.column + 1,
                                 nodeType: null
                             });
-                            return;
+                            continue;
                         }
 
-                        enabledGlobals[globalName] = { comment, value: normalizedValue };
-                    });
+                        if (enabledGlobals[id]) {
+                            enabledGlobals[id].comments.push(comment);
+                            enabledGlobals[id].value = normalizedValue;
+                        } else {
+                            enabledGlobals[id] = {
+                                comments: [comment],
+                                value: normalizedValue
+                            };
+                        }
+                    }
                     break;
-                }
+
                 case "eslint-disable":
                     disableDirectives.push(...createDisableDirectives("disable", comment.loc.start, directiveValue));
                     break;
diff --git a/lib/rules/no-async-promise-executor.js b/lib/rules/no-async-promise-executor.js
index af15509ed..553311e58 100644
--- a/lib/rules/no-async-promise-executor.js
+++ b/lib/rules/no-async-promise-executor.js
@@ -15,7 +15,7 @@ module.exports = {
         docs: {
             description: "disallow using an async function as a Promise executor",
             category: "Possible Errors",
-            recommended: false,
+            recommended: true,
             url: "https://eslint.org/docs/rules/no-async-promise-executor"
         },
 
diff --git a/lib/rules/no-console.js b/lib/rules/no-console.js
index d3a2e3447..5967483d6 100644
--- a/lib/rules/no-console.js
+++ b/lib/rules/no-console.js
@@ -22,7 +22,7 @@ module.exports = {
         docs: {
             description: "disallow the use of `console`",
             category: "Possible Errors",
-            recommended: true,
+            recommended: false,
             url: "https://eslint.org/docs/rules/no-console"
         },
 
diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js
index 4fa650ed5..287aa2060 100644
--- a/lib/rules/no-misleading-character-class.js
+++ b/lib/rules/no-misleading-character-class.js
@@ -106,7 +106,7 @@ module.exports = {
         docs: {
             description: "disallow characters which are made with multiple code points in character class syntax",
             category: "Possible Errors",
-            recommended: false,
+            recommended: true,
             url: "https://eslint.org/docs/rules/no-misleading-character-class"
         },
 
diff --git a/lib/rules/no-prototype-builtins.js b/lib/rules/no-prototype-builtins.js
index 171395306..87a760156 100644
--- a/lib/rules/no-prototype-builtins.js
+++ b/lib/rules/no-prototype-builtins.js
@@ -15,7 +15,7 @@ module.exports = {
         docs: {
             description: "disallow calling some `Object.prototype` methods directly on objects",
             category: "Possible Errors",
-            recommended: false,
+            recommended: true,
             url: "https://eslint.org/docs/rules/no-prototype-builtins"
         },
 
diff --git a/lib/rules/no-redeclare.js b/lib/rules/no-redeclare.js
index 4d689cc61..6adde0f97 100644
--- a/lib/rules/no-redeclare.js
+++ b/lib/rules/no-redeclare.js
@@ -5,6 +5,12 @@
 
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../util/ast-utils");
+
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
@@ -20,11 +26,17 @@ module.exports = {
             url: "https://eslint.org/docs/rules/no-redeclare"
         },
 
+        messages: {
+            redeclared: "'{{id}}' is already defined.",
+            redeclaredAsBuiltin: "'{{id}}' is already defined as a built-in global variable.",
+            redeclaredBySyntax: "'{{id}}' is already defined by a variable declaration."
+        },
+
         schema: [
             {
                 type: "object",
                 properties: {
-                    builtinGlobals: { type: "boolean", default: false }
+                    builtinGlobals: { type: "boolean", default: true }
                 },
                 additionalProperties: false
             }
@@ -33,72 +45,128 @@ module.exports = {
 
     create(context) {
         const options = {
-            builtinGlobals: context.options[0] && context.options[0].builtinGlobals
+            builtinGlobals: Boolean(
+                context.options.length === 0 ||
+                context.options[0].builtinGlobals
+            )
         };
+        const sourceCode = context.getSourceCode();
 
         /**
-         * Find variables in a given scope and flag redeclared ones.
-         * @param {Scope} scope - An eslint-scope scope object.
-         * @returns {void}
-         * @private
+         * Iterate declarations of a given variable.
+         * @param {escope.variable} variable The variable object to iterate declarations.
+         * @returns {IterableIterator<{type:string,node:ASTNode,loc:SourceLocation}>} The declarations.
          */
-        function findVariablesInScope(scope) {
-            scope.variables.forEach(variable => {
-                const hasBuiltin = options.builtinGlobals && "writeable" in variable;
-                const count = (hasBuiltin ? 1 : 0) + variable.identifiers.length;
+        function *iterateDeclarations(variable) {
+            if (options.builtinGlobals && (
+                variable.eslintImplicitGlobalSetting === "readonly" ||
+                variable.eslintImplicitGlobalSetting === "writable"
+            )) {
+                yield { type: "builtin" };
+            }
 
-                if (count >= 2) {
-                    variable.identifiers.sort((a, b) => a.range[1] - b.range[1]);
+            for (const id of variable.identifiers) {
+                yield { type: "syntax", node: id, loc: id.loc };
+            }
 
-                    for (let i = (hasBuiltin ? 0 : 1), l = variable.identifiers.length; i < l; i++) {
-                        context.report({ node: variable.identifiers[i], message: "'{{a}}' is already defined.", data: { a: variable.name } });
-                    }
+            if (variable.eslintExplicitGlobalComments) {
+                for (const comment of variable.eslintExplicitGlobalComments) {
+                    yield {
+                        type: "comment",
+                        node: comment,
+                        loc: astUtils.getNameLocationInGlobalDirectiveComment(
+                            sourceCode,
+                            comment,
+                            variable.name
+                        )
+                    };
                 }
-            });
-
+            }
         }
 
         /**
-         * Find variables in the current scope.
-         * @param {ASTNode} node - The Program node.
+         * Find variables in a given scope and flag redeclared ones.
+         * @param {Scope} scope - An eslint-scope scope object.
          * @returns {void}
          * @private
          */
-        function checkForGlobal(node) {
-            const scope = context.getScope(),
-                parserOptions = context.parserOptions,
-                ecmaFeatures = parserOptions.ecmaFeatures || {};
-
-            // Nodejs env or modules has a special scope.
-            if (ecmaFeatures.globalReturn || node.sourceType === "module") {
-                findVariablesInScope(scope.childScopes[0]);
-            } else {
-                findVariablesInScope(scope);
+        function findVariablesInScope(scope) {
+            for (const variable of scope.variables) {
+                const [
+                    declaration,
+                    ...extraDeclarations
+                ] = iterateDeclarations(variable);
+
+                if (extraDeclarations.length === 0) {
+                    continue;
+                }
+
+                /*
+                 * If the type of a declaration is different from the type of
+                 * the first declaration, it shows the location of the first
+                 * declaration.
+                 */
+                const detailMessageId = declaration.type === "builtin"
+                    ? "redeclaredAsBuiltin"
+                    : "redeclaredBySyntax";
+                const data = { id: variable.name };
+
+                // Report extra declarations.
+                for (const { type, node, loc } of extraDeclarations) {
+                    const messageId = type === declaration.type
+                        ? "redeclared"
+                        : detailMessageId;
+
+                    context.report({ node, loc, messageId, data });
+                }
             }
         }
 
         /**
          * Find variables in the current scope.
+         * @param {ASTNode} node The node of the current scope.
          * @returns {void}
          * @private
          */
-        function checkForBlock() {
-            findVariablesInScope(context.getScope());
-        }
+        function checkForBlock(node) {
+            const scope = context.getScope();
 
-        if (context.parserOptions.ecmaVersion >= 6) {
-            return {
-                Program: checkForGlobal,
-                BlockStatement: checkForBlock,
-                SwitchStatement: checkForBlock
-            };
+            /*
+             * In ES5, some node type such as `BlockStatement` doesn't have that scope.
+             * `scope.block` is a different node in such a case.
+             */
+            if (scope.block === node) {
+                findVariablesInScope(scope);
+            }
         }
+
         return {
-            Program: checkForGlobal,
+            Program() {
+                const scope = context.getScope();
+
+                findVariablesInScope(scope);
+
+                // Node.js or ES modules has a special scope.
+                if (
+                    scope.type === "global" &&
+                    scope.childScopes[0] &&
+
+                    // The special scope's block is the Program node.
+                    scope.block === scope.childScopes[0].block
+                ) {
+                    findVariablesInScope(scope.childScopes[0]);
+                }
+            },
+
             FunctionDeclaration: checkForBlock,
             FunctionExpression: checkForBlock,
-            ArrowFunctionExpression: checkForBlock
-        };
+            ArrowFunctionExpression: checkForBlock,
 
+            BlockStatement: checkForBlock,
+            ForStatement: checkForBlock,
+            ForInStatement: checkForBlock,
+            ForOfStatement: checkForBlock,
+            SwitchStatement: checkForBlock
+        };
     }
 };
diff --git a/lib/rules/no-shadow-restricted-names.js b/lib/rules/no-shadow-restricted-names.js
index f09f3767d..9030d523b 100644
--- a/lib/rules/no-shadow-restricted-names.js
+++ b/lib/rules/no-shadow-restricted-names.js
@@ -28,7 +28,7 @@ module.exports = {
         docs: {
             description: "disallow identifiers from shadowing restricted names",
             category: "Variables",
-            recommended: false,
+            recommended: true,
             url: "https://eslint.org/docs/rules/no-shadow-restricted-names"
         },
 
diff --git a/lib/rules/no-unneeded-ternary.js b/lib/rules/no-unneeded-ternary.js
index e75a36430..4dbb78a3e 100644
--- a/lib/rules/no-unneeded-ternary.js
+++ b/lib/rules/no-unneeded-ternary.js
@@ -17,6 +17,7 @@ const OPERATOR_INVERSES = {
 
     // Operators like < and >= are not true inverses, since both will return false with NaN.
 };
+const OR_PRECEDENCE = astUtils.getPrecedence({ type: "LogicalExpression", operator: "||" });
 
 //------------------------------------------------------------------------------
 // Rule Definition
@@ -141,15 +142,16 @@ module.exports = {
                         loc: node.consequent.loc.start,
                         message: "Unnecessary use of conditional expression for default assignment.",
                         fix: fixer => {
-                            let nodeAlternate = astUtils.getParenthesisedText(sourceCode, node.alternate);
-
-                            if (node.alternate.type === "ConditionalExpression" || node.alternate.type === "YieldExpression") {
-                                const isAlternateParenthesised = astUtils.isParenthesised(sourceCode, node.alternate);
-
-                                nodeAlternate = isAlternateParenthesised ? nodeAlternate : `(${nodeAlternate})`;
-                            }
-
-                            return fixer.replaceText(node, `${astUtils.getParenthesisedText(sourceCode, node.test)} || ${nodeAlternate}`);
+                            const shouldParenthesizeAlternate = (
+                                astUtils.getPrecedence(node.alternate) < OR_PRECEDENCE &&
+                                !astUtils.isParenthesised(sourceCode, node.alternate)
+                            );
+                            const alternateText = shouldParenthesizeAlternate
+                                ? `(${sourceCode.getText(node.alternate)})`
+                                : astUtils.getParenthesisedText(sourceCode, node.alternate);
+                            const testText = astUtils.getParenthesisedText(sourceCode, node.test);
+
+                            return fixer.replaceText(node, `${testText} || ${alternateText}`);
                         }
                     });
                 }
diff --git a/lib/rules/no-unused-vars.js b/lib/rules/no-unused-vars.js
index 1482a0238..8e32951bd 100644
--- a/lib/rules/no-unused-vars.js
+++ b/lib/rules/no-unused-vars.js
@@ -9,7 +9,6 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const lodash = require("lodash");
 const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
@@ -588,39 +587,6 @@ module.exports = {
             return unusedVars;
         }
 
-        /**
-         * Gets the index of a given variable name in a given comment.
-         * @param {eslint-scope.Variable} variable - A variable to get.
-         * @param {ASTNode} comment - A comment node which includes the variable name.
-         * @returns {number} The index of the variable name's location.
-         * @private
-         */
-        function getColumnInComment(variable, comment) {
-            const namePattern = new RegExp(`[\\s,]${lodash.escapeRegExp(variable.name)}(?:$|[\\s,:])`, "gu");
-
-            // To ignore the first text "global".
-            namePattern.lastIndex = comment.value.indexOf("global") + 6;
-
-            // Search a given variable name.
-            const match = namePattern.exec(comment.value);
-
-            return match ? match.index + 1 : 0;
-        }
-
-        /**
-         * Creates the correct location of a given variables.
-         * The location is at its name string in a `/*global` comment.
-         *
-         * @param {eslint-scope.Variable} variable - A variable to get its location.
-         * @returns {{line: number, column: number}} The location object for the variable.
-         * @private
-         */
-        function getLocation(variable) {
-            const comment = variable.eslintExplicitGlobalComment;
-
-            return sourceCode.getLocFromIndex(comment.range[0] + 2 + getColumnInComment(variable, comment));
-        }
-
         //--------------------------------------------------------------------------
         // Public
         //--------------------------------------------------------------------------
@@ -632,14 +598,8 @@ module.exports = {
                 for (let i = 0, l = unusedVars.length; i < l; ++i) {
                     const unusedVar = unusedVars[i];
 
-                    if (unusedVar.eslintExplicitGlobal) {
-                        context.report({
-                            node: programNode,
-                            loc: getLocation(unusedVar),
-                            message: getDefinedMessage(unusedVar),
-                            data: unusedVar
-                        });
-                    } else if (unusedVar.defs.length > 0) {
+                    // Report the first declaration.
+                    if (unusedVar.defs.length > 0) {
                         context.report({
                             node: unusedVar.identifiers[0],
                             message: unusedVar.references.some(ref => ref.isWrite())
@@ -647,6 +607,17 @@ module.exports = {
                                 : getDefinedMessage(unusedVar),
                             data: unusedVar
                         });
+
+                    // If there are no regular declaration, report the first `/*globals*/` comment directive.
+                    } else if (unusedVar.eslintExplicitGlobalComments) {
+                        const directiveComment = unusedVar.eslintExplicitGlobalComments[0];
+
+                        context.report({
+                            node: programNode,
+                            loc: astUtils.getNameLocationInGlobalDirectiveComment(sourceCode, directiveComment, unusedVar.name),
+                            message: getDefinedMessage(unusedVar),
+                            data: unusedVar
+                        });
                     }
                 }
             }
diff --git a/lib/rules/no-useless-catch.js b/lib/rules/no-useless-catch.js
index e4284cfb4..37bf453ae 100644
--- a/lib/rules/no-useless-catch.js
+++ b/lib/rules/no-useless-catch.js
@@ -16,7 +16,7 @@ module.exports = {
         docs: {
             description: "disallow unnecessary `catch` clauses",
             category: "Best Practices",
-            recommended: false,
+            recommended: true,
             url: "https://eslint.org/docs/rules/no-useless-catch"
         },
 
diff --git a/lib/rules/no-with.js b/lib/rules/no-with.js
index ecdf22c7d..576366158 100644
--- a/lib/rules/no-with.js
+++ b/lib/rules/no-with.js
@@ -16,7 +16,7 @@ module.exports = {
         docs: {
             description: "disallow `with` statements",
             category: "Best Practices",
-            recommended: false,
+            recommended: true,
             url: "https://eslint.org/docs/rules/no-with"
         },
 
diff --git a/lib/rules/require-atomic-updates.js b/lib/rules/require-atomic-updates.js
index e8dbe17b8..351eedf32 100644
--- a/lib/rules/require-atomic-updates.js
+++ b/lib/rules/require-atomic-updates.js
@@ -17,7 +17,7 @@ module.exports = {
         docs: {
             description: "disallow assignments that can lead to race conditions due to usage of `await` or `yield`",
             category: "Possible Errors",
-            recommended: false,
+            recommended: true,
             url: "https://eslint.org/docs/rules/require-atomic-updates"
         },
 
diff --git a/lib/util/ast-utils.js b/lib/util/ast-utils.js
index 85205a75d..0f9ba290a 100644
--- a/lib/util/ast-utils.js
+++ b/lib/util/ast-utils.js
@@ -11,6 +11,7 @@
 
 const esutils = require("esutils");
 const espree = require("espree");
+const lodash = require("lodash");
 
 //------------------------------------------------------------------------------
 // Helpers
@@ -1342,5 +1343,29 @@ module.exports = {
         }
 
         return false;
+    },
+
+    /**
+     * Get the `loc` object of a given name in a `/*globals` directive comment.
+     * @param {SourceCode} sourceCode The source code to convert index to loc.
+     * @param {Comment} comment The `/*globals` directive comment which include the name.
+     * @param {string} name The name to find.
+     * @returns {SourceLocation} The `loc` object.
+     */
+    getNameLocationInGlobalDirectiveComment(sourceCode, comment, name) {
+        const namePattern = new RegExp(`[\\s,]${lodash.escapeRegExp(name)}(?:$|[\\s,:])`, "gu");
+
+        // To ignore the first text "global".
+        namePattern.lastIndex = comment.value.indexOf("global") + 6;
+
+        // Search a given variable name.
+        const match = namePattern.exec(comment.value);
+
+        // Convert the index to loc.
+        return sourceCode.getLocFromIndex(
+            comment.range[0] +
+            "/*".length +
+            (match ? match.index + 1 : 0)
+        );
     }
 };
diff --git a/package.json b/package.json
index d2ea8a004..c99b1559f 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,16 @@
     "profile": "beefy tests/bench/bench.js --open -- -t brfs -t ./tests/bench/xform-rules.js -r espree",
     "coveralls": "cat ./coverage/lcov.info | coveralls"
   },
+  "gitHooks": {
+    "pre-commit": "lint-staged"
+  },
+  "lint-staged": {
+    "*.js": [
+      "node ./bin/eslint.js --fix",
+      "git add"
+    ],
+    "*.md": "markdownlint"
+  },
   "files": [
     "LICENSE",
     "README.md",
@@ -75,6 +85,7 @@
     "@babel/core": "^7.4.3",
     "@babel/polyfill": "^7.4.3",
     "@babel/preset-env": "^7.4.3",
+    "acorn": "^6.1.1",
     "babel-loader": "^8.0.5",
     "beefy": "^2.1.8",
     "brfs": "^2.0.2",
@@ -98,6 +109,7 @@
     "karma-mocha-reporter": "^2.2.3",
     "karma-webpack": "^4.0.0-rc.6",
     "leche": "^2.2.3",
+    "lint-staged": "^8.1.5",
     "load-perf": "^0.2.0",
     "markdownlint": "^0.13.0",
     "mocha": "^6.1.2",
@@ -111,7 +123,8 @@
     "temp": "^0.9.0",
     "through": "^2.3.8",
     "webpack": "^4.29.6",
-    "webpack-cli": "^3.3.0"
+    "webpack-cli": "^3.3.0",
+    "yorkie": "^2.0.0"
   },
   "keywords": [
     "ast",
diff --git a/packages/eslint-config-eslint/default.yml b/packages/eslint-config-eslint/default.yml
index 0cfe3506b..8e8c8c57a 100644
--- a/packages/eslint-config-eslint/default.yml
+++ b/packages/eslint-config-eslint/default.yml
@@ -50,7 +50,6 @@ rules:
     new-parens: "error"
     no-alert: "error"
     no-array-constructor: "error"
-    no-async-promise-executor: "error"
     no-buffer-constructor: "error"
     no-caller: "error"
     no-confusing-arrow: "error"
@@ -87,7 +86,6 @@ rules:
     no-path-concat: "error"
     no-process-exit: "error"
     no-proto: "error"
-    no-prototype-builtins: "error"
     no-redeclare: "error"
     no-restricted-properties: [
         "error",
@@ -104,7 +102,6 @@ rules:
     no-self-compare: "error"
     no-sequences: "error"
     no-shadow: "error"
-    no-shadow-restricted-names: "error"
     no-tabs: "error"
     no-throw-literal: "error"
     no-trailing-spaces: "error"
@@ -118,7 +115,6 @@ rules:
     no-unused-vars: ["error", {vars: "all", args: "after-used"}]
     no-use-before-define: "error"
     no-useless-call: "error"
-    no-useless-catch: "error"
     no-useless-computed-key: "error"
     no-useless-concat: "error"
     no-useless-constructor: "error"
@@ -126,7 +122,6 @@ rules:
     no-useless-rename: "error"
     no-useless-return: "error"
     no-whitespace-before-property: "error"
-    no-with: "error"
     no-var: "error"
     object-curly-newline: ["error", { "consistent": true, "multiline": true }]
     object-curly-spacing: ["error", "always"]
@@ -158,7 +153,6 @@ rules:
     quotes: ["error", "double", {avoidEscape: true}]
     quote-props: ["error", "as-needed"]
     radix: "error"
-    require-atomic-updates: "error"
     require-jsdoc: "error"
     require-unicode-regexp: "error"
     rest-spread-spacing: "error"


Test patch:
diff --git a/tests/lib/cli.js b/tests/lib/cli.js
index a0a197d88..88508dbfe 100644
--- a/tests/lib/cli.js
+++ b/tests/lib/cli.js
@@ -467,7 +467,7 @@ describe("cli", () => {
 
             cli.execute(`--no-eslintrc --config ./conf/eslint-recommended.js --no-ignore ${files.join(" ")}`);
 
-            assert.strictEqual(log.info.args[0][0].split("\n").length, 11);
+            assert.strictEqual(log.info.args[0][0].split("\n").length, 10);
         });
     });
 
diff --git a/tests/lib/config/config-initializer.js b/tests/lib/config/config-initializer.js
index d306db1b1..239e43a67 100644
--- a/tests/lib/config/config-initializer.js
+++ b/tests/lib/config/config-initializer.js
@@ -70,7 +70,7 @@ describe("configInitializer", () => {
 
     // copy into clean area so as not to get "infected" by this project's .eslintrc files
     before(() => {
-        fixtureDir = `${os.tmpdir()}/eslint/fixtures/config-initializer`;
+        fixtureDir = path.join(os.tmpdir(), "eslint/fixtures/config-initializer");
         sh.mkdir("-p", fixtureDir);
         sh.cp("-r", "./tests/fixtures/config-initializer/.", fixtureDir);
         fixtureDir = fs.realpathSync(fixtureDir);
@@ -360,7 +360,7 @@ describe("configInitializer", () => {
 
             it("should extend and not disable recommended rules", () => {
                 assert.strictEqual(config.extends, "eslint:recommended");
-                assert.notProperty(config.rules, "no-console");
+                assert.notProperty(config.rules, "no-debugger");
             });
 
             it("should support new ES features if using later ES version", () => {
diff --git a/tests/lib/config/config-ops.js b/tests/lib/config/config-ops.js
index ffa65660d..6a1f0b219 100644
--- a/tests/lib/config/config-ops.js
+++ b/tests/lib/config/config-ops.js
@@ -902,16 +902,16 @@ describe("ConfigOps", () => {
     describe("normalizeConfigGlobal", () => {
         [
             ["off", "off"],
-            [true, "writeable"],
-            ["true", "writeable"],
-            [false, "readable"],
-            ["false", "readable"],
-            [null, "readable"],
-            ["writeable", "writeable"],
-            ["writable", "writeable"],
-            ["readable", "readable"],
-            ["readonly", "readable"],
-            ["writable", "writeable"]
+            [true, "writable"],
+            ["true", "writable"],
+            [false, "readonly"],
+            ["false", "readonly"],
+            [null, "readonly"],
+            ["writeable", "writable"],
+            ["writable", "writable"],
+            ["readable", "readonly"],
+            ["readonly", "readonly"],
+            ["writable", "writable"]
         ].forEach(([input, output]) => {
             it(util.inspect(input), () => {
                 assert.strictEqual(ConfigOps.normalizeConfigGlobal(input), output);
diff --git a/tests/lib/linter.js b/tests/lib/linter.js
index bf8812c7b..3df167111 100644
--- a/tests/lib/linter.js
+++ b/tests/lib/linter.js
@@ -3469,18 +3469,18 @@ describe("Linter", () => {
 
                             const foo = getVariable(scope, "foo");
 
-                            assert.strictEqual(true, foo.eslintExplicitGlobal);
-                            assert.strictEqual(comments[0], foo.eslintExplicitGlobalComment);
+                            assert.strictEqual(foo.eslintExplicitGlobal, true);
+                            assert.strictEqual(foo.eslintExplicitGlobalComments[0], comments[0]);
 
                             const bar = getVariable(scope, "bar");
 
-                            assert.strictEqual(true, bar.eslintExplicitGlobal);
-                            assert.strictEqual(comments[1], bar.eslintExplicitGlobalComment);
+                            assert.strictEqual(bar.eslintExplicitGlobal, true);
+                            assert.strictEqual(bar.eslintExplicitGlobalComments[0], comments[1]);
 
                             const baz = getVariable(scope, "baz");
 
-                            assert.strictEqual(true, baz.eslintExplicitGlobal);
-                            assert.strictEqual(comments[1], baz.eslintExplicitGlobalComment);
+                            assert.strictEqual(baz.eslintExplicitGlobal, true);
+                            assert.strictEqual(baz.eslintExplicitGlobalComments[0], comments[1]);
 
                             ok = true;
                         }
diff --git a/tests/lib/rules/no-redeclare.js b/tests/lib/rules/no-redeclare.js
index 23a45f021..15d8275d0 100644
--- a/tests/lib/rules/no-redeclare.js
+++ b/tests/lib/rules/no-redeclare.js
@@ -9,13 +9,15 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const rule = require("../../../lib/rules/no-redeclare"),
-    RuleTester = require("../../../lib/testers/rule-tester");
+const path = require("path");
+const rule = require("../../../lib/rules/no-redeclare");
+const RuleTester = require("../../../lib/testers/rule-tester");
 
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
 
+const looseParserPath = path.resolve(__dirname, "../../tools/loose-parser.js");
 const ruleTester = new RuleTester();
 
 ruleTester.run("no-redeclare", rule, {
@@ -28,11 +30,9 @@ ruleTester.run("no-redeclare", rule, {
                 ecmaVersion: 6
             }
         },
-        "var Object = 0;",
         { code: "var Object = 0;", options: [{ builtinGlobals: false }] },
         { code: "var Object = 0;", options: [{ builtinGlobals: true }], parserOptions: { sourceType: "module" } },
         { code: "var Object = 0;", options: [{ builtinGlobals: true }], parserOptions: { ecmaFeatures: { globalReturn: true } } },
-        { code: "var top = 0;", env: { browser: true } },
         { code: "var top = 0;", options: [{ builtinGlobals: true }] },
         { code: "var top = 0;", options: [{ builtinGlobals: true }], parserOptions: { ecmaFeatures: { globalReturn: true } }, env: { browser: true } },
         { code: "var top = 0;", options: [{ builtinGlobals: true }], parserOptions: { sourceType: "module" }, env: { browser: true } },
@@ -40,6 +40,32 @@ ruleTester.run("no-redeclare", rule, {
             code: "var self = 1",
             options: [{ builtinGlobals: true }],
             env: { browser: false }
+        },
+
+        // Comments and built-ins.
+        {
+            code: "/*globals Array */",
+            options: [{ builtinGlobals: false }]
+        },
+        {
+            code: "/*globals a */",
+            options: [{ builtinGlobals: false }],
+            globals: { a: "readonly" }
+        },
+        {
+            code: "/*globals a */",
+            options: [{ builtinGlobals: false }],
+            globals: { a: "writable" }
+        },
+        {
+            code: "/*globals a:off */",
+            options: [{ builtinGlobals: true }],
+            globals: { a: "readonly" }
+        },
+        {
+            code: "/*globals a */",
+            options: [{ builtinGlobals: true }],
+            globals: { a: "off" }
         }
     ],
     invalid: [
@@ -57,13 +83,13 @@ ruleTester.run("no-redeclare", rule, {
         {
             code: "var Object = 0;",
             options: [{ builtinGlobals: true }],
-            errors: [{ message: "'Object' is already defined.", type: "Identifier" }]
+            errors: [{ message: "'Object' is already defined as a built-in global variable.", type: "Identifier" }]
         },
         {
             code: "var top = 0;",
             options: [{ builtinGlobals: true }],
             env: { browser: true },
-            errors: [{ message: "'top' is already defined.", type: "Identifier" }]
+            errors: [{ message: "'top' is already defined as a built-in global variable.", type: "Identifier" }]
         },
         {
             code: "var a; var {a = 0, b: Object = 0} = {};",
@@ -71,7 +97,7 @@ ruleTester.run("no-redeclare", rule, {
             parserOptions: { ecmaVersion: 6 },
             errors: [
                 { message: "'a' is already defined.", type: "Identifier" },
-                { message: "'Object' is already defined.", type: "Identifier" }
+                { message: "'Object' is already defined as a built-in global variable.", type: "Identifier" }
             ]
         },
         {
@@ -98,13 +124,203 @@ ruleTester.run("no-redeclare", rule, {
                 { message: "'a' is already defined.", type: "Identifier" }
             ]
         },
-
-        // Notifications of readonly are moved from no-undef: https://github.com/eslint/eslint/issues/4504
         {
             code: "/*global b:false*/ var b = 1;",
             options: [{ builtinGlobals: true }],
             errors: [
-                { message: "'b' is already defined.", type: "Identifier" }
+                { message: "'b' is already defined by a variable declaration.", type: "Block" }
+            ]
+        },
+        {
+            code: "/*global b:true*/ var b = 1;",
+            options: [{ builtinGlobals: true }],
+            errors: [
+                { message: "'b' is already defined by a variable declaration.", type: "Block" }
+            ]
+        },
+        {
+            code: "function f() { var a; var a; }",
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "function f(a) { var a; }",
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "function f() { var a; if (test) { var a; } }",
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "for (var a, a;;);",
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+
+        {
+            code: "var Object = 0;",
+            errors: [
+                { message: "'Object' is already defined as a built-in global variable.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "var top = 0;",
+            env: { browser: true },
+            errors: [
+                { message: "'top' is already defined as a built-in global variable.", type: "Identifier" }
+            ]
+        },
+
+        // let/const
+        {
+            code: "let a; let a;",
+            parser: looseParserPath,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "let a; let a;",
+            parser: looseParserPath,
+            parserOptions: { ecmaVersion: 2015, sourceType: "module" },
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "let a; let a;",
+            parser: looseParserPath,
+            parserOptions: { ecmaVersion: 2015, ecmaFeatures: { globalReturn: true } },
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "let a; const a = 0;",
+            parser: looseParserPath,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "const a = 0; const a = 0;",
+            parser: looseParserPath,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "if (test) { let a; let a; }",
+            parser: looseParserPath,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "switch (test) { case 0: let a; let a; }",
+            parser: looseParserPath,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "for (let a, a;;);",
+            parser: looseParserPath,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "for (let [a, a] in xs);",
+            parser: looseParserPath,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "for (let [a, a] of xs);",
+            parser: looseParserPath,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "function f() { let a; let a; }",
+            parser: looseParserPath,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "function f(a) { let a; }",
+            parser: looseParserPath,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+        {
+            code: "function f() { if (test) { let a; let a; } }",
+            parser: looseParserPath,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [
+                { message: "'a' is already defined.", type: "Identifier" }
+            ]
+        },
+
+        // Comments and built-ins.
+        {
+            code: "/*globals Array */",
+            options: [{ builtinGlobals: true }],
+            errors: [
+                { message: "'Array' is already defined as a built-in global variable.", type: "Block" }
+            ]
+        },
+        {
+            code: "/*globals a */",
+            options: [{ builtinGlobals: true }],
+            globals: { a: "readonly" },
+            errors: [
+                { message: "'a' is already defined as a built-in global variable.", type: "Block" }
+            ]
+        },
+        {
+            code: "/*globals a */",
+            options: [{ builtinGlobals: true }],
+            globals: { a: "writable" },
+            errors: [
+                { message: "'a' is already defined as a built-in global variable.", type: "Block" }
+            ]
+        },
+        {
+            code: "/*globals a */ /*globals a */",
+            errors: [
+                { message: "'a' is already defined.", type: "Block", column: 26 }
+            ]
+        },
+        {
+            code: "/*globals a */ /*globals a */ var a = 0",
+            options: [{ builtinGlobals: true }],
+            globals: { a: "writable" },
+            errors: [
+                { message: "'a' is already defined as a built-in global variable.", type: "Block", column: 11 },
+                { message: "'a' is already defined as a built-in global variable.", type: "Block", column: 26 },
+                { message: "'a' is already defined as a built-in global variable.", type: "Identifier", column: 35 }
             ]
         }
     ]
diff --git a/tests/lib/rules/no-unneeded-ternary.js b/tests/lib/rules/no-unneeded-ternary.js
index badf529e6..5196b576b 100644
--- a/tests/lib/rules/no-unneeded-ternary.js
+++ b/tests/lib/rules/no-unneeded-ternary.js
@@ -230,6 +230,90 @@ ruleTester.run("no-unneeded-ternary", rule, {
                 line: 1,
                 column: 24
             }]
+        },
+        {
+            code: "var a = b ? b : c => c;",
+            output: "var a = b || (c => c);",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
+        },
+        {
+            code: "var a = b ? b : c = 0;",
+            output: "var a = b || (c = 0);",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
+        },
+        {
+            code: "var a = b ? b : (c => c);",
+            output: "var a = b || (c => c);",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
+        },
+        {
+            code: "var a = b ? b : (c = 0);",
+            output: "var a = b || (c = 0);",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
+        },
+        {
+            code: "var a = b ? b : (c) => (c);",
+            output: "var a = b || ((c) => (c));",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
+        },
+        {
+            code: "var a = b ? b : c, d; // this is ((b ? b : c), (d))",
+            output: "var a = b || c, d; // this is ((b ? b : c), (d))",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
+        },
+        {
+            code: "var a = b ? b : (c, d);",
+            output: "var a = b || (c, d);",
+            options: [{ defaultAssignment: false }],
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                message: "Unnecessary use of conditional expression for default assignment.",
+                type: "ConditionalExpression",
+                line: 1,
+                column: 13
+            }]
         }
     ]
 });
diff --git a/tests/tools/loose-parser.js b/tests/tools/loose-parser.js
new file mode 100644
index 000000000..abb3aeb5b
--- /dev/null
+++ b/tests/tools/loose-parser.js
@@ -0,0 +1,31 @@
+/**
+ * @fileoverview Define a custom parser to ignore recoverable syntax errors.
+ * @author Toru Nagashima <https://github.com/mysticatea>
+ *
+ * no-redeclare rule uses this parser to check redeclarations.
+ */
+"use strict";
+
+const acorn = require("acorn");
+const espree = require("espree/lib/espree");
+
+// eslint-disable-next-line valid-jsdoc
+/**
+ * Define the parser which ignores recoverable errors.
+ * @returns {(parser:acorn.Parser) => acorn.Parser} The function that defines loose parser.
+ */
+function loose() {
+    return Parser => class LooseParser extends Parser {
+        raiseRecoverable() { // eslint-disable-line class-methods-use-this
+            // ignore
+        }
+    };
+}
+
+const LooseEspree = acorn.Parser.extend(espree(), loose());
+
+module.exports = {
+    parse(code, options) {
+        return new LooseEspree(options, code).parse();
+    }
+};