Repository: eslint/eslint
Pull request #10621 (2018-07-18 13:02:13)

Changes rationale for Object.assign
object spread is not just syntax sugar but actually performs better in browsers. The "When Not To Use It" section should therefore give a more nuanced opinion on when Object.assign is desireable.

<!--
    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
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] 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 (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)**
Changed the "When Not To Use It" section of the `prefer-object-spread` docs.

**Is there anything you'd like reviewers to focus on?**
More concrete ideas for positive uses for `Object.assign`



jsf-clabot: [![CLA assistant check](https://cla.js.foundation/pull/badge/not_signed)](https://cla.js.foundation/eslint/eslint?pullRequest=10621) <br/>Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla.js.foundation/eslint/eslint?pullRequest=10621) before we can accept your contribution.<br/>
platinumazure: Hi @bennypowers, thanks for the PR.

The new wording feels a little opinionated to me. Wondering if it might be better just to suggest that the rule could be disabled when object spread is not available for the user's codebase?
bennypowers: @platinumazure I'm ok with that in principle: my main issue was the phrase "syntactic sugar" which implied that both forms are entirely equivalent.

OTOH I've often learned nice little tidbits from eslint docs, which is why I wanted to offer users a more concrete example of the advantages of the assign method
platinumazure: @bennypowers Fair enough. I'm wondering if maybe the introduction of the rule could be a good place to go into the benefits of object spread vs `Object.assign`? What do you think?
bennypowers: @platinumazure it's my first rodeo in eslint docs land, so I'll defer to your judgement.

Will revise for your review a little later on.
bennypowers: Ok I updated the wording below and added some context above. Please review and let me know if this is more in line with what you had in mind.
ljharb: The wording is fine, but performance is entirely unrelated to whether it’s syntax sugar.

Patch:
diff --git a/Makefile.js b/Makefile.js
index 9c48620ef..cd756a746 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -684,7 +684,7 @@ target.gensite = function(prereleaseVersion) {
     const rules = require(".").linter.getRules();
 
     const RECOMMENDED_TEXT = "\n\n(recommended) The `\"extends\": \"eslint:recommended\"` property in a configuration file enables this rule.";
-    const FIXABLE_TEXT = "\n\n(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fix) can automatically fix some of the problems reported by this rule.";
+    const FIXABLE_TEXT = "\n\n(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.";
 
     // 4. Loop through all files in temporary directory
     process.stdout.write("> Updating files (Steps 4-9): 0/... - ...\r");
diff --git a/conf/eslint-recommended.js b/conf/eslint-recommended.js
index 28d7aa4f9..8b74d8ba9 100644
--- a/conf/eslint-recommended.js
+++ b/conf/eslint-recommended.js
@@ -83,6 +83,7 @@ module.exports = {
         "newline-per-chained-call": "off",
         "no-alert": "off",
         "no-array-constructor": "off",
+        "no-async-promise-executor": "off",
         "no-await-in-loop": "off",
         "no-bitwise": "off",
         "no-buffer-constructor": "off",
@@ -240,6 +241,7 @@ module.exports = {
         "quote-props": "off",
         quotes: "off",
         radix: "off",
+        "require-atomic-updates": "off",
         "require-await": "off",
         "require-jsdoc": "off",
         "require-yield": "error",
diff --git a/docs/developer-guide/nodejs-api.md b/docs/developer-guide/nodejs-api.md
index e56fd5789..2e3390b93 100644
--- a/docs/developer-guide/nodejs-api.md
+++ b/docs/developer-guide/nodejs-api.md
@@ -330,7 +330,7 @@ var CLIEngine = require("eslint").CLIEngine;
 The `CLIEngine` is a constructor, and you can create a new instance by passing in the options you want to use. The available options are:
 
 * `allowInlineConfig` - Set to `false` to disable the use of configuration comments (such as `/*eslint-disable*/`). Corresponds to `--no-inline-config`.
-* `baseConfig` - Set to false to disable use of base config. Could be set to an object to override default base config as well.
+* `baseConfig` - Can optionally be set to a config object. This will used as a default config, and will be merged with any configuration defined in `.eslintrc.*` files, with the `.eslintrc.*` files having precedence.
 * `cache` - Operate only on changed files (default: `false`). Corresponds to `--cache`.
 * `cacheFile` - Name of the file where the cache will be stored (default: `.eslintcache`). Corresponds to `--cache-file`. Deprecated: use `cacheLocation` instead.
 * `cacheLocation` - Name of the file or directory where the cache will be stored (default: `.eslintcache`). Corresponds to `--cache-location`.
diff --git a/docs/rules/dot-location.md b/docs/rules/dot-location.md
index c4283cf7f..1fe4a3129 100644
--- a/docs/rules/dot-location.md
+++ b/docs/rules/dot-location.md
@@ -20,7 +20,7 @@ This rule aims to enforce newline consistency in member expressions. This rule p
 
 The rule takes one option, a string:
 
-* If it is `"object"`, the dot in a member expression should be on the same line as the object portion. The default is `"object"`.
+* If it is `"object"` (default), the dot in a member expression should be on the same line as the object portion.
 * If it is `"property"`, the dot in a member expression should be on the same line as the property portion.
 
 ### object
diff --git a/docs/rules/no-async-promise-executor.md b/docs/rules/no-async-promise-executor.md
new file mode 100644
index 000000000..82fed5369
--- /dev/null
+++ b/docs/rules/no-async-promise-executor.md
@@ -0,0 +1,62 @@
+# disallow using an async function as a Promise executor (no-async-promise-executor)
+
+The `new Promise` constructor accepts an *executor* function as an argument, which has `resolve` and `reject` parameters that can be used to control the state of the created Promise. For example:
+
+```js
+const result = new Promise(function executor(resolve, reject) {
+  readFile('foo.txt', function(err, result) {
+    if (err) {
+      reject(err);
+    } else {
+      resolve(result);
+    }
+  });
+});
+```
+
+The executor function can also be an `async function`. However, this is usually a mistake, for a few reasons:
+
+* If an async executor function throws an error, the error will be lost and won't cause the newly-constructed `Promise` to reject. This could make it difficult to debug and handle some errors.
+* If a Promise executor function is using `await`, this is usually a sign that it is not actually necessary to use the `new Promise` constructor, or the scope of the `new Promise` constructor can be reduced.
+
+## Rule Details
+
+This rule aims to disallow async Promise executor functions.
+
+Examples of **incorrect** code for this rule:
+
+```js
+const result = new Promise(async (resolve, reject) => {
+  readFile('foo.txt', function(err, result) {
+    if (err) {
+      reject(err);
+    } else {
+      resolve(result);
+    }
+  });
+});
+
+const result = new Promise(async (resolve, reject) => {
+  resolve(await foo);
+});
+```
+
+Examples of **correct** code for this rule:
+
+```js
+const result = new Promise((resolve, reject) => {
+  readFile('foo.txt', function(err, result) {
+    if (err) {
+      reject(err);
+    } else {
+      resolve(result);
+    }
+  });
+});
+
+const result = Promise.resolve(foo);
+```
+
+## When Not To Use It
+
+If your codebase doesn't support async function syntax, there's no need to enable this rule.
diff --git a/docs/rules/no-else-return.md b/docs/rules/no-else-return.md
index eb6f8fab3..2f3f83d73 100644
--- a/docs/rules/no-else-return.md
+++ b/docs/rules/no-else-return.md
@@ -20,14 +20,6 @@ This rule is aimed at highlighting an unnecessary block of code following an `if
 
 This rule has an object option:
 
-```json
-{
-    "no-else-return": ["error", { "allowElseIf": true }],
-    // or
-    "no-else-return": ["error", { "allowElseIf": false }]
-}
-```
-
 * `allowElseIf: true` (default) allows `else if` blocks after a return
 * `allowElseIf: false` disallows `else if` blocks after a return
 
diff --git a/docs/rules/no-prototype-builtins.md b/docs/rules/no-prototype-builtins.md
index 1694356a3..e2dd4f5b9 100644
--- a/docs/rules/no-prototype-builtins.md
+++ b/docs/rules/no-prototype-builtins.md
@@ -1,6 +1,6 @@
 # Disallow use of Object.prototypes builtins directly (no-prototype-builtins)
 
-In ECMAScript 5.1, `Object.create` was added, which enables the creation of objects with a specified `[[Prototype]]`. `Object.create(null)` is a common pattern used to create objects that will be used as a Map. This can lead to errors when it is assumed that objects will have properties from `Object.prototype`. This rule prevents calling `Object.prototype` methods directly from an object.
+In ECMAScript 5.1, `Object.create` was added, which enables the creation of objects with a specified `[[Prototype]]`. `Object.create(null)` is a common pattern used to create objects that will be used as a Map. This can lead to errors when it is assumed that objects will have properties from `Object.prototype`. This rule prevents calling some `Object.prototype` methods directly from an object.
 
 ## Rule Details
 
diff --git a/docs/rules/no-unsafe-negation.md b/docs/rules/no-unsafe-negation.md
index 689796209..8e1428ea9 100644
--- a/docs/rules/no-unsafe-negation.md
+++ b/docs/rules/no-unsafe-negation.md
@@ -46,10 +46,6 @@ if(("" + !key) in object) {
 }
 ```
 
-## Options
-
-Nothing.
-
 ## When Not To Use It
 
 If you don't want to notify unsafe logical negations, then it's safe to disable this rule.
diff --git a/docs/rules/prefer-object-spread.md b/docs/rules/prefer-object-spread.md
index 0907170e4..b50e39716 100644
--- a/docs/rules/prefer-object-spread.md
+++ b/docs/rules/prefer-object-spread.md
@@ -2,6 +2,8 @@
 
 When Object.assign is called using an object literal as the first argument, this rule requires using the object spread syntax instead. This rule also warns on cases where an `Object.assign` call is made using a single argument that is an object literal, in this case, the `Object.assign` call is not needed.
 
+Object spread is a declarative alternative which may perform better than the more dynamic, imperative `Object.assign`.
+
 ## Rule Details
 
 Examples of **incorrect** code for this rule:
@@ -44,4 +46,4 @@ Object.assign(foo, { ...baz });
 
 ## When Not To Use It
 
-When you don't care about syntactic sugar added by the object spread property.
+When object spread is not available in your codebase.
diff --git a/docs/rules/require-atomic-updates.md b/docs/rules/require-atomic-updates.md
new file mode 100644
index 000000000..593ccdd60
--- /dev/null
+++ b/docs/rules/require-atomic-updates.md
@@ -0,0 +1,97 @@
+# Disallow assignments that can lead to race conditions due to usage of `await` or `yield` (require-atomic-updates)
+
+When writing asynchronous code, it is possible to create subtle race condition bugs. Consider the following example:
+
+```js
+let totalLength = 0;
+
+async function addLengthOfSinglePage(pageNum) {
+  totalLength += await getPageLength(pageNum);
+}
+
+Promise.all([addLengthOfSinglePage(1), addLengthOfSinglePage(2)]).then(() => {
+  console.log('The combined length of both pages is', totalLength);
+});
+```
+
+This code looks like it will sum the results of calling `getPageLength(1)` and `getPageLength(2)`, but in reality the final value of `totalLength` will only be the length of one of the two pages. The bug is in the statement `totalLength += await getPageLength(pageNum);`. This statement first reads an initial value of `totalLength`, then calls `getPageLength(pageNum)` and waits for that Promise to fulfill. Finally, it sets the value of `totalLength` to the sum of `await getPageLength(pageNum)` and the *initial* value of `totalLength`. If the `totalLength` variable is updated in a separate function call during the time that the `getPageLength(pageNum)` Promise is pending, that update will be lost because the new value is overwritten without being read.
+
+One way to fix this issue would be to ensure that `totalLength` is read at the same time as it's updated, like this:
+
+```js
+async function addLengthOfSinglePage(pageNum) {
+  const lengthOfThisPage = await getPageLength(pageNum);
+
+  totalLength += lengthOfThisPage;
+}
+```
+
+Another solution would be to avoid using a mutable variable reference at all:
+
+```js
+Promise.all([getPageLength(1), getPageLength(2)]).then(pageLengths => {
+  const totalLength = pageLengths.reduce((accumulator, length) => accumulator + length, 0);
+
+  console.log('The combined length of both pages is', totalLength);
+});
+```
+
+## Rule Details
+
+This rule aims to report assignments to variables or properties where all of the following are true:
+
+* A variable or property is reassigned to a new value which is based on its old value.
+* A `yield` or `await` expression interrupts the assignment after the old value is read, and before the new value is set.
+* The rule cannot easily verify that the assignment is safe (e.g. if an assigned variable is local and would not be readable from anywhere else while the function is paused).
+
+Examples of **incorrect** code for this rule:
+
+```js
+/* eslint require-atomic-updates: error */
+
+let result;
+async function foo() {
+  result += await somethingElse;
+
+  result = result + await somethingElse;
+
+  result = result + doSomething(await somethingElse);
+}
+
+function* bar() {
+  result += yield;
+
+  result = result + (yield somethingElse);
+
+  result = result + doSomething(yield somethingElse);
+}
+```
+
+Examples of **correct** code for this rule:
+
+```js
+/* eslint require-atomic-updates: error */
+
+let result;
+async function foo() {
+  result = await somethingElse + result;
+
+  let tmp = await somethingElse;
+  result += tmp;
+
+  let localVariable = 0;
+  localVariable += await somethingElse;
+}
+
+function* bar() {
+  result += yield;
+
+  result = (yield somethingElse) + result;
+
+  result = doSomething(yield somethingElse, result);
+}
+```
+
+## When Not To Use It
+
+If you don't use async or generator functions, you don't need to enable this rule.
diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md
index b1284f92a..4daa7dd59 100644
--- a/docs/user-guide/configuring.md
+++ b/docs/user-guide/configuring.md
@@ -272,6 +272,8 @@ ESLint comes with a large number of rules. You can modify which rules your proje
 * `"warn"` or `1` - turn the rule on as a warning (doesn't affect exit code)
 * `"error"` or `2` - turn the rule on as an error (exit code is 1 when triggered)
 
+### Using Configuration Comments
+
 To configure rules inside of a file using configuration comments, use a comment in the following format:
 
 ```js
@@ -294,6 +296,8 @@ If a rule has additional options, you can specify them using array literal synta
 
 This comment specifies the "double" option for the [`quotes`](../rules/quotes) rule. The first item in the array is always the rule severity (number or string).
 
+### Using Configuration Files
+
 To configure rules inside of a configuration file, use the `rules` key along with an error level and any options you want to use. For example:
 
 
diff --git a/lib/cli-engine.js b/lib/cli-engine.js
index 8442de2de..b0856bd8d 100644
--- a/lib/cli-engine.js
+++ b/lib/cli-engine.js
@@ -22,7 +22,7 @@ const fs = require("fs"),
     IgnoredPaths = require("./ignored-paths"),
     Config = require("./config"),
     LintResultCache = require("./util/lint-result-cache"),
-    globUtil = require("./util/glob-util"),
+    globUtil = require("./util/glob-utils"),
     validator = require("./config/config-validator"),
     hash = require("./util/hash"),
     ModuleResolver = require("./util/module-resolver"),
@@ -41,7 +41,7 @@ const resolver = new ModuleResolver();
  * The options to configure a CLI engine with.
  * @typedef {Object} CLIEngineOptions
  * @property {boolean} allowInlineConfig Enable or disable inline configuration comments.
- * @property {boolean|Object} baseConfig Base config object. True enables recommend rules and environments.
+ * @property {Object} baseConfig Base config object, extended by all configs used with this CLIEngine instance
  * @property {boolean} cache Enable result caching.
  * @property {string} cacheLocation The cache file to use instead of .eslintcache.
  * @property {string} configFile The configuration file to use.
diff --git a/lib/code-path-analysis/code-path-analyzer.js b/lib/code-path-analysis/code-path-analyzer.js
index f4a7fee7f..b81e212e7 100644
--- a/lib/code-path-analysis/code-path-analyzer.js
+++ b/lib/code-path-analysis/code-path-analyzer.js
@@ -14,7 +14,7 @@ const assert = require("assert"),
     CodePathSegment = require("./code-path-segment"),
     IdGenerator = require("./id-generator"),
     debug = require("./debug-helpers"),
-    astUtils = require("../ast-utils");
+    astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/config/config-initializer.js b/lib/config/config-initializer.js
index 4273abae5..a80506a55 100644
--- a/lib/config/config-initializer.js
+++ b/lib/config/config-initializer.js
@@ -16,9 +16,9 @@ const util = require("util"),
     autoconfig = require("./autoconfig.js"),
     ConfigFile = require("./config-file"),
     ConfigOps = require("./config-ops"),
-    getSourceCodeOfFiles = require("../util/source-code-util").getSourceCodeOfFiles,
+    getSourceCodeOfFiles = require("../util/source-code-utils").getSourceCodeOfFiles,
     ModuleResolver = require("../util/module-resolver"),
-    npmUtil = require("../util/npm-util"),
+    npmUtil = require("../util/npm-utils"),
     recConfig = require("../../conf/eslint-recommended"),
     log = require("../util/logging");
 
diff --git a/lib/ignored-paths.js b/lib/ignored-paths.js
index 8134d6d32..152f176b7 100644
--- a/lib/ignored-paths.js
+++ b/lib/ignored-paths.js
@@ -12,7 +12,7 @@
 const fs = require("fs"),
     path = require("path"),
     ignore = require("ignore"),
-    pathUtil = require("./util/path-util");
+    pathUtil = require("./util/path-utils");
 
 const debug = require("debug")("eslint:ignored-paths");
 
@@ -79,6 +79,27 @@ function mergeDefaultOptions(options) {
     return Object.assign({}, DEFAULT_OPTIONS, options);
 }
 
+/**
+ * Converts a glob pattern to a new glob pattern relative to a different directory
+ * @param {string} globPattern The glob pattern, relative the the old base directory
+ * @param {string} relativePathToOldBaseDir A relative path from the new base directory to the old one
+ * @returns {string} A glob pattern relative to the new base directory
+ */
+function relativize(globPattern, relativePathToOldBaseDir) {
+    if (relativePathToOldBaseDir === "") {
+        return globPattern;
+    }
+
+    const prefix = globPattern.startsWith("!") ? "!" : "";
+    const globWithoutPrefix = globPattern.replace(/^!/, "");
+
+    if (globWithoutPrefix.startsWith("/")) {
+        return `${prefix}/${relativePathToOldBaseDir}${globWithoutPrefix}`;
+    }
+
+    return globPattern;
+}
+
 //------------------------------------------------------------------------------
 // Public Interface
 //------------------------------------------------------------------------------
@@ -96,41 +117,35 @@ class IgnoredPaths {
 
         this.cache = {};
 
-        /**
-         * add pattern to node-ignore instance
-         * @param {Object} ig, instance of node-ignore
-         * @param {string} pattern, pattern do add to ig
-         * @returns {array} raw ignore rules
-         */
-        function addPattern(ig, pattern) {
-            return ig.addPattern(pattern);
-        }
-
         this.defaultPatterns = [].concat(DEFAULT_IGNORE_DIRS, options.patterns || []);
-        this.baseDir = options.cwd;
+
+        this.ignoreFileDir = options.ignore !== false && options.ignorePath
+            ? path.dirname(path.resolve(options.cwd, options.ignorePath))
+            : options.cwd;
+        this.options = options;
 
         this.ig = {
             custom: ignore(),
             default: ignore()
         };
 
-        /*
-         * Add a way to keep track of ignored files.  This was present in node-ignore
-         * 2.x, but dropped for now as of 3.0.10.
-         */
-        this.ig.custom.ignoreFiles = [];
-        this.ig.default.ignoreFiles = [];
-
+        this.defaultPatterns.forEach(pattern => this.addPatternRelativeToCwd(this.ig.default, pattern));
         if (options.dotfiles !== true) {
 
             /*
              * ignore files beginning with a dot, but not files in a parent or
              * ancestor directory (which in relative format will begin with `../`).
              */
-            addPattern(this.ig.default, [".*", "!../"]);
+            this.addPatternRelativeToCwd(this.ig.default, ".*");
+            this.addPatternRelativeToCwd(this.ig.default, "!../");
         }
 
-        addPattern(this.ig.default, this.defaultPatterns);
+        /*
+         * Add a way to keep track of ignored files.  This was present in node-ignore
+         * 2.x, but dropped for now as of 3.0.10.
+         */
+        this.ig.custom.ignoreFiles = [];
+        this.ig.default.ignoreFiles = [];
 
         if (options.ignore !== false) {
             let ignorePath;
@@ -154,13 +169,11 @@ class IgnoredPaths {
                     debug(`Loaded ignore file ${ignorePath}`);
                 } catch (e) {
                     debug("Could not find ignore file in cwd");
-                    this.options = options;
                 }
             }
 
             if (ignorePath) {
                 debug(`Adding ${ignorePath}`);
-                this.baseDir = path.dirname(path.resolve(options.cwd, ignorePath));
                 this.addIgnoreFile(this.ig.custom, ignorePath);
                 this.addIgnoreFile(this.ig.default, ignorePath);
             } else {
@@ -187,8 +200,8 @@ class IgnoredPaths {
                         if (packageJSONOptions.eslintIgnore) {
                             if (Array.isArray(packageJSONOptions.eslintIgnore)) {
                                 packageJSONOptions.eslintIgnore.forEach(pattern => {
-                                    addPattern(this.ig.custom, pattern);
-                                    addPattern(this.ig.default, pattern);
+                                    this.addPatternRelativeToIgnoreFile(this.ig.custom, pattern);
+                                    this.addPatternRelativeToIgnoreFile(this.ig.default, pattern);
                                 });
                             } else {
                                 throw new TypeError("Package.json eslintIgnore property requires an array of paths");
@@ -202,12 +215,37 @@ class IgnoredPaths {
             }
 
             if (options.ignorePattern) {
-                addPattern(this.ig.custom, options.ignorePattern);
-                addPattern(this.ig.default, options.ignorePattern);
+                this.addPatternRelativeToCwd(this.ig.custom, options.ignorePattern);
+                this.addPatternRelativeToCwd(this.ig.default, options.ignorePattern);
             }
         }
+    }
 
-        this.options = options;
+    /*
+     * If `ignoreFileDir` is a subdirectory of `cwd`, all paths will be normalized to be relative to `cwd`.
+     * Otherwise, all paths will be normalized to be relative to `ignoreFileDir`.
+     * This ensures that the final normalized ignore rule will not contain `..`, which is forbidden in
+     * ignore rules.
+     */
+
+    addPatternRelativeToCwd(ig, pattern) {
+        ig.addPattern(
+            this.getBaseDir() === this.options.cwd
+                ? pattern
+                : relativize(pattern, path.relative(this.ignoreFileDir, this.options.cwd))
+        );
+    }
+
+    addPatternRelativeToIgnoreFile(ig, pattern) {
+        ig.addPattern(
+            this.getBaseDir() === this.ignoreFileDir
+                ? pattern
+                : relativize(pattern, path.relative(this.options.cwd, this.ignoreFileDir))
+        );
+    }
+
+    getBaseDir() {
+        return this.ignoreFileDir.startsWith(this.options.cwd) ? this.options.cwd : this.ignoreFileDir;
     }
 
     /**
@@ -217,7 +255,7 @@ class IgnoredPaths {
      */
     readIgnoreFile(filePath) {
         if (typeof this.cache[filePath] === "undefined") {
-            this.cache[filePath] = fs.readFileSync(filePath, "utf8");
+            this.cache[filePath] = fs.readFileSync(filePath, "utf8").split("\n").filter(Boolean);
         }
         return this.cache[filePath];
     }
@@ -226,11 +264,13 @@ class IgnoredPaths {
      * add ignore file to node-ignore instance
      * @param {Object} ig, instance of node-ignore
      * @param {string} filePath, file to add to ig
-     * @returns {array} raw ignore rules
+     * @returns {void}
      */
     addIgnoreFile(ig, filePath) {
         ig.ignoreFiles.push(filePath);
-        return ig.add(this.readIgnoreFile(filePath));
+        this
+            .readIgnoreFile(filePath)
+            .forEach(ignoreRule => this.addPatternRelativeToIgnoreFile(ig, ignoreRule));
     }
 
     /**
@@ -243,7 +283,7 @@ class IgnoredPaths {
 
         let result = false;
         const absolutePath = path.resolve(this.options.cwd, filepath);
-        const relativePath = pathUtil.getRelativePath(absolutePath, this.baseDir);
+        const relativePath = pathUtil.getRelativePath(absolutePath, this.getBaseDir());
 
         if (typeof category === "undefined") {
             result = (this.ig.default.filter([relativePath]).length === 0) ||
@@ -261,8 +301,10 @@ class IgnoredPaths {
      * @returns {function()} method to check whether a folder should be ignored by glob.
      */
     getIgnoredFoldersGlobChecker() {
+        const baseDir = this.getBaseDir();
+        const ig = ignore();
 
-        const ig = ignore().add(DEFAULT_IGNORE_DIRS);
+        DEFAULT_IGNORE_DIRS.forEach(ignoreDir => this.addPatternRelativeToCwd(ig, ignoreDir));
 
         if (this.options.dotfiles !== true) {
 
@@ -276,10 +318,8 @@ class IgnoredPaths {
 
         const filter = ig.createFilter();
 
-        const base = this.baseDir;
-
         return function(absolutePath) {
-            const relative = pathUtil.getRelativePath(absolutePath, base);
+            const relative = pathUtil.getRelativePath(absolutePath, baseDir);
 
             if (!relative) {
                 return false;
diff --git a/lib/linter.js b/lib/linter.js
index a6b5a5408..8f7bdf9e4 100644
--- a/lib/linter.js
+++ b/lib/linter.js
@@ -25,7 +25,7 @@ const eslintScope = require("eslint-scope"),
     createReportTranslator = require("./report-translator"),
     Rules = require("./rules"),
     timing = require("./util/timing"),
-    astUtils = require("./ast-utils"),
+    astUtils = require("./util/ast-utils"),
     pkg = require("../package.json"),
     SourceCodeFixer = require("./util/source-code-fixer");
 
diff --git a/lib/rules/array-bracket-newline.js b/lib/rules/array-bracket-newline.js
index e19040572..e8f7c502e 100644
--- a/lib/rules/array-bracket-newline.js
+++ b/lib/rules/array-bracket-newline.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/array-bracket-spacing.js b/lib/rules/array-bracket-spacing.js
index c64ff3dc2..f46c3978d 100644
--- a/lib/rules/array-bracket-spacing.js
+++ b/lib/rules/array-bracket-spacing.js
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/array-callback-return.js b/lib/rules/array-callback-return.js
index 2375dcba9..4d374cf22 100644
--- a/lib/rules/array-callback-return.js
+++ b/lib/rules/array-callback-return.js
@@ -11,7 +11,7 @@
 
 const lodash = require("lodash");
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/array-element-newline.js b/lib/rules/array-element-newline.js
index b437c6932..c4caf8c71 100644
--- a/lib/rules/array-element-newline.js
+++ b/lib/rules/array-element-newline.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/arrow-body-style.js b/lib/rules/arrow-body-style.js
index 49feb0670..92068c75c 100644
--- a/lib/rules/arrow-body-style.js
+++ b/lib/rules/arrow-body-style.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/arrow-parens.js b/lib/rules/arrow-parens.js
index 8cabb7f90..7a6ef6f8b 100644
--- a/lib/rules/arrow-parens.js
+++ b/lib/rules/arrow-parens.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/arrow-spacing.js b/lib/rules/arrow-spacing.js
index 52f8fcab9..a1db18fc9 100644
--- a/lib/rules/arrow-spacing.js
+++ b/lib/rules/arrow-spacing.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/block-spacing.js b/lib/rules/block-spacing.js
index 536381362..838c2c701 100644
--- a/lib/rules/block-spacing.js
+++ b/lib/rules/block-spacing.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const util = require("../ast-utils");
+const util = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js
index e2cbafe21..236a01096 100644
--- a/lib/rules/brace-style.js
+++ b/lib/rules/brace-style.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/capitalized-comments.js b/lib/rules/capitalized-comments.js
index c98a4464a..8fabde287 100644
--- a/lib/rules/capitalized-comments.js
+++ b/lib/rules/capitalized-comments.js
@@ -9,7 +9,7 @@
 //------------------------------------------------------------------------------
 
 const LETTER_PATTERN = require("../util/patterns/letters");
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/comma-dangle.js b/lib/rules/comma-dangle.js
index 50de7b59b..9bc6c3fa8 100644
--- a/lib/rules/comma-dangle.js
+++ b/lib/rules/comma-dangle.js
@@ -10,7 +10,7 @@
 //------------------------------------------------------------------------------
 
 const lodash = require("lodash");
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/comma-spacing.js b/lib/rules/comma-spacing.js
index 2a48e54d5..d3f82b3a4 100644
--- a/lib/rules/comma-spacing.js
+++ b/lib/rules/comma-spacing.js
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/comma-style.js b/lib/rules/comma-style.js
index aac667f73..bd98d7acc 100644
--- a/lib/rules/comma-style.js
+++ b/lib/rules/comma-style.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/complexity.js b/lib/rules/complexity.js
index 0b8644136..f25194595 100644
--- a/lib/rules/complexity.js
+++ b/lib/rules/complexity.js
@@ -12,7 +12,7 @@
 
 const lodash = require("lodash");
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/computed-property-spacing.js b/lib/rules/computed-property-spacing.js
index 51334a2ab..060b2c5b4 100644
--- a/lib/rules/computed-property-spacing.js
+++ b/lib/rules/computed-property-spacing.js
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/consistent-return.js b/lib/rules/consistent-return.js
index c86b1717e..6185d094f 100644
--- a/lib/rules/consistent-return.js
+++ b/lib/rules/consistent-return.js
@@ -10,7 +10,7 @@
 
 const lodash = require("lodash");
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/curly.js b/lib/rules/curly.js
index 07d991b31..ad8da821c 100644
--- a/lib/rules/curly.js
+++ b/lib/rules/curly.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/dot-location.js b/lib/rules/dot-location.js
index 7ff8ca65a..34d2d4eaa 100644
--- a/lib/rules/dot-location.js
+++ b/lib/rules/dot-location.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/dot-notation.js b/lib/rules/dot-notation.js
index c381661d4..a7062df8a 100644
--- a/lib/rules/dot-notation.js
+++ b/lib/rules/dot-notation.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/eqeqeq.js b/lib/rules/eqeqeq.js
index ec9d0a782..2c5e9ae9e 100644
--- a/lib/rules/eqeqeq.js
+++ b/lib/rules/eqeqeq.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/func-call-spacing.js b/lib/rules/func-call-spacing.js
index b13613cf8..83142bae8 100644
--- a/lib/rules/func-call-spacing.js
+++ b/lib/rules/func-call-spacing.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/func-name-matching.js b/lib/rules/func-name-matching.js
index b71799596..d66f630fc 100644
--- a/lib/rules/func-name-matching.js
+++ b/lib/rules/func-name-matching.js
@@ -9,7 +9,7 @@
 // Requirements
 //--------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 const esutils = require("esutils");
 
 //--------------------------------------------------------------------------
diff --git a/lib/rules/func-names.js b/lib/rules/func-names.js
index 722f276c1..500b95a1c 100644
--- a/lib/rules/func-names.js
+++ b/lib/rules/func-names.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 /**
  * Checks whether or not a given variable is a function name.
diff --git a/lib/rules/function-paren-newline.js b/lib/rules/function-paren-newline.js
index 7e4f2792c..c483e40da 100644
--- a/lib/rules/function-paren-newline.js
+++ b/lib/rules/function-paren-newline.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/getter-return.js b/lib/rules/getter-return.js
index fe40e5f09..8fd9f1599 100644
--- a/lib/rules/getter-return.js
+++ b/lib/rules/getter-return.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/indent-legacy.js b/lib/rules/indent-legacy.js
index dc6d1689e..e6dc92163 100644
--- a/lib/rules/indent-legacy.js
+++ b/lib/rules/indent-legacy.js
@@ -12,7 +12,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/indent.js b/lib/rules/indent.js
index a08b4d7e5..716167d2c 100644
--- a/lib/rules/indent.js
+++ b/lib/rules/indent.js
@@ -1,5 +1,5 @@
 /**
- * @fileoverview This option sets a specific tab width for your code
+ * @fileoverview This rule sets a specific indentation style and width for your code
  *
  * @author Teddy Katz
  * @author Vitaly Puzrin
@@ -13,7 +13,7 @@
 //------------------------------------------------------------------------------
 
 const lodash = require("lodash");
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 const createTree = require("functional-red-black-tree");
 
 //------------------------------------------------------------------------------
@@ -855,7 +855,11 @@ module.exports = {
                         previousElement &&
                         previousElementLastToken.loc.end.line - countTrailingLinebreaks(previousElementLastToken.value) > startToken.loc.end.line
                     ) {
-                        offsets.setDesiredOffsets(element.range, firstTokenOfPreviousElement, 0);
+                        offsets.setDesiredOffsets(
+                            [previousElement.range[1], element.range[1]],
+                            firstTokenOfPreviousElement,
+                            0
+                        );
                     }
                 }
             });
@@ -997,6 +1001,31 @@ module.exports = {
             return !node || node.loc.start.line === token.loc.start.line;
         }
 
+        /**
+         * Check whether there are any blank (whitespace-only) lines between
+         * two tokens on separate lines.
+         * @param {Token} firstToken The first token.
+         * @param {Token} secondToken The second token.
+         * @returns {boolean} `true` if the tokens are on separate lines and
+         *   there exists a blank line between them, `false` otherwise.
+         */
+        function hasBlankLinesBetween(firstToken, secondToken) {
+            const firstTokenLine = firstToken.loc.end.line;
+            const secondTokenLine = secondToken.loc.start.line;
+
+            if (firstTokenLine === secondTokenLine || firstTokenLine === secondTokenLine - 1) {
+                return false;
+            }
+
+            for (let line = firstTokenLine + 1; line < secondTokenLine; ++line) {
+                if (!tokenInfo.firstTokensByLineNumber.has(line)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
         const ignoredNodeFirstTokens = new Set();
 
         const baseOffsetListeners = {
@@ -1536,10 +1565,13 @@ module.exports = {
                             const tokenBefore = precedingTokens.get(firstTokenOfLine);
                             const tokenAfter = tokenBefore ? sourceCode.getTokenAfter(tokenBefore) : sourceCode.ast.tokens[0];
 
+                            const mayAlignWithBefore = tokenBefore && !hasBlankLinesBetween(tokenBefore, firstTokenOfLine);
+                            const mayAlignWithAfter = tokenAfter && !hasBlankLinesBetween(firstTokenOfLine, tokenAfter);
+
                             // If a comment matches the expected indentation of the token immediately before or after, don't report it.
                             if (
-                                tokenBefore && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenBefore)) ||
-                                tokenAfter && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenAfter))
+                                mayAlignWithBefore && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenBefore)) ||
+                                mayAlignWithAfter && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenAfter))
                             ) {
                                 return;
                             }
diff --git a/lib/rules/jsx-quotes.js b/lib/rules/jsx-quotes.js
index 54ec481a8..3dd9567d7 100644
--- a/lib/rules/jsx-quotes.js
+++ b/lib/rules/jsx-quotes.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Constants
diff --git a/lib/rules/key-spacing.js b/lib/rules/key-spacing.js
index b1208e19e..75578b2c7 100644
--- a/lib/rules/key-spacing.js
+++ b/lib/rules/key-spacing.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
@@ -540,7 +540,7 @@ module.exports = {
             const length = properties.length,
                 widths = properties.map(getKeyWidth), // Width of keys, including quotes
                 align = alignmentOptions.on; // "value" or "colon"
-            let targetWidth = Math.max.apply(null, widths),
+            let targetWidth = Math.max(...widths),
                 beforeColon, afterColon, mode;
 
             if (alignmentOptions && length > 1) { // When aligning values within a group, use the alignment configuration.
diff --git a/lib/rules/keyword-spacing.js b/lib/rules/keyword-spacing.js
index a2ce79ab5..2a66779e7 100644
--- a/lib/rules/keyword-spacing.js
+++ b/lib/rules/keyword-spacing.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils"),
+const astUtils = require("../util/ast-utils"),
     keywords = require("../util/keywords");
 
 //------------------------------------------------------------------------------
diff --git a/lib/rules/line-comment-position.js b/lib/rules/line-comment-position.js
index ebdb024f6..99aea02b8 100644
--- a/lib/rules/line-comment-position.js
+++ b/lib/rules/line-comment-position.js
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/linebreak-style.js b/lib/rules/linebreak-style.js
index d5a170f73..cd432dbcc 100644
--- a/lib/rules/linebreak-style.js
+++ b/lib/rules/linebreak-style.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/lines-around-comment.js b/lib/rules/lines-around-comment.js
index 5d98473be..67fd2032b 100644
--- a/lib/rules/lines-around-comment.js
+++ b/lib/rules/lines-around-comment.js
@@ -9,7 +9,7 @@
 //------------------------------------------------------------------------------
 
 const lodash = require("lodash"),
-    astUtils = require("../ast-utils");
+    astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/lines-around-directive.js b/lib/rules/lines-around-directive.js
index 3ba3ab5bb..e8d613a41 100644
--- a/lib/rules/lines-around-directive.js
+++ b/lib/rules/lines-around-directive.js
@@ -6,7 +6,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/lines-between-class-members.js b/lib/rules/lines-between-class-members.js
index 252984da5..8d4e6dd8c 100644
--- a/lib/rules/lines-between-class-members.js
+++ b/lib/rules/lines-between-class-members.js
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/max-len.js b/lib/rules/max-len.js
index 273eb8490..88a388309 100644
--- a/lib/rules/max-len.js
+++ b/lib/rules/max-len.js
@@ -266,7 +266,7 @@ module.exports = {
                 // list of comments to ignore
                 comments = ignoreComments || maxCommentLength || ignoreTrailingComments ? sourceCode.getAllComments() : [];
 
-                // we iterate over comments in parallel with the lines
+            // we iterate over comments in parallel with the lines
             let commentsIndex = 0;
 
             const strings = getAllStrings();
diff --git a/lib/rules/max-lines-per-function.js b/lib/rules/max-lines-per-function.js
index b40c7ee88..ccf6304e4 100644
--- a/lib/rules/max-lines-per-function.js
+++ b/lib/rules/max-lines-per-function.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Constants
diff --git a/lib/rules/max-lines.js b/lib/rules/max-lines.js
index 9bb4420ed..e3fccb314 100644
--- a/lib/rules/max-lines.js
+++ b/lib/rules/max-lines.js
@@ -9,7 +9,7 @@
 //------------------------------------------------------------------------------
 
 const lodash = require("lodash");
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/max-params.js b/lib/rules/max-params.js
index d7c22c527..43c649ee8 100644
--- a/lib/rules/max-params.js
+++ b/lib/rules/max-params.js
@@ -11,7 +11,7 @@
 
 const lodash = require("lodash");
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/max-statements-per-line.js b/lib/rules/max-statements-per-line.js
index a6776bdea..566f04ab5 100644
--- a/lib/rules/max-statements-per-line.js
+++ b/lib/rules/max-statements-per-line.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/max-statements.js b/lib/rules/max-statements.js
index faa0d5d84..8501fd688 100644
--- a/lib/rules/max-statements.js
+++ b/lib/rules/max-statements.js
@@ -11,7 +11,7 @@
 
 const lodash = require("lodash");
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/multiline-comment-style.js b/lib/rules/multiline-comment-style.js
index 4da9a619f..5dd39e00a 100644
--- a/lib/rules/multiline-comment-style.js
+++ b/lib/rules/multiline-comment-style.js
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/multiline-ternary.js b/lib/rules/multiline-ternary.js
index 10cf269fb..6a22a1113 100644
--- a/lib/rules/multiline-ternary.js
+++ b/lib/rules/multiline-ternary.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/new-parens.js b/lib/rules/new-parens.js
index abf2e032b..fab302000 100644
--- a/lib/rules/new-parens.js
+++ b/lib/rules/new-parens.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/newline-after-var.js b/lib/rules/newline-after-var.js
index b11d9d995..62ccb3db2 100644
--- a/lib/rules/newline-after-var.js
+++ b/lib/rules/newline-after-var.js
@@ -10,7 +10,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/newline-per-chained-call.js b/lib/rules/newline-per-chained-call.js
index 9691da44e..2adcdfe45 100644
--- a/lib/rules/newline-per-chained-call.js
+++ b/lib/rules/newline-per-chained-call.js
@@ -6,7 +6,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-alert.js b/lib/rules/no-alert.js
index 69d419da1..1c08460f3 100644
--- a/lib/rules/no-alert.js
+++ b/lib/rules/no-alert.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const getPropertyName = require("../ast-utils").getStaticPropertyName;
+const getPropertyName = require("../util/ast-utils").getStaticPropertyName;
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-async-promise-executor.js b/lib/rules/no-async-promise-executor.js
new file mode 100644
index 000000000..55d489eb6
--- /dev/null
+++ b/lib/rules/no-async-promise-executor.js
@@ -0,0 +1,33 @@
+/**
+ * @fileoverview disallow using an async function as a Promise executor
+ * @author Teddy Katz
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        docs: {
+            description: "disallow using an async function as a Promise executor",
+            category: "Possible Errors",
+            recommended: false,
+            url: "https://eslint.org/docs/rules/no-async-promise-executor"
+        },
+        fixable: null,
+        schema: []
+    },
+
+    create(context) {
+        return {
+            "NewExpression[callee.name='Promise'][arguments.0.async=true]"(node) {
+                context.report({
+                    node: context.getSourceCode().getFirstToken(node.arguments[0], token => token.value === "async"),
+                    message: "Promise executor functions should not be async."
+                });
+            }
+        };
+    }
+};
diff --git a/lib/rules/no-catch-shadow.js b/lib/rules/no-catch-shadow.js
index e1e24e0ef..f749490f3 100644
--- a/lib/rules/no-catch-shadow.js
+++ b/lib/rules/no-catch-shadow.js
@@ -10,7 +10,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-class-assign.js b/lib/rules/no-class-assign.js
index 58dddd6a6..9b28d40d4 100644
--- a/lib/rules/no-class-assign.js
+++ b/lib/rules/no-class-assign.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-cond-assign.js b/lib/rules/no-cond-assign.js
index 4c7b5722d..caf9563e9 100644
--- a/lib/rules/no-cond-assign.js
+++ b/lib/rules/no-cond-assign.js
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 const NODE_DESCRIPTIONS = {
     DoWhileStatement: "a 'do...while' statement",
diff --git a/lib/rules/no-confusing-arrow.js b/lib/rules/no-confusing-arrow.js
index 297e3b148..503174e9a 100644
--- a/lib/rules/no-confusing-arrow.js
+++ b/lib/rules/no-confusing-arrow.js
@@ -6,7 +6,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils.js");
+const astUtils = require("../util/ast-utils.js");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-console.js b/lib/rules/no-console.js
index fd5c33a8e..5fb3a159b 100644
--- a/lib/rules/no-console.js
+++ b/lib/rules/no-console.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-const-assign.js b/lib/rules/no-const-assign.js
index 043fe05df..3188bd34d 100644
--- a/lib/rules/no-const-assign.js
+++ b/lib/rules/no-const-assign.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-dupe-keys.js b/lib/rules/no-dupe-keys.js
index 577710c65..31493bd04 100644
--- a/lib/rules/no-dupe-keys.js
+++ b/lib/rules/no-dupe-keys.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-else-return.js b/lib/rules/no-else-return.js
index 5e58acfe0..1000fa807 100644
--- a/lib/rules/no-else-return.js
+++ b/lib/rules/no-else-return.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 const FixTracker = require("../util/fix-tracker");
 
 //------------------------------------------------------------------------------
diff --git a/lib/rules/no-empty-function.js b/lib/rules/no-empty-function.js
index d9948cd4a..c8039314a 100644
--- a/lib/rules/no-empty-function.js
+++ b/lib/rules/no-empty-function.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-empty.js b/lib/rules/no-empty.js
index a598d40f8..2f4c258a2 100644
--- a/lib/rules/no-empty.js
+++ b/lib/rules/no-empty.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-eval.js b/lib/rules/no-eval.js
index 68ed086a3..c3ea87d46 100644
--- a/lib/rules/no-eval.js
+++ b/lib/rules/no-eval.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-ex-assign.js b/lib/rules/no-ex-assign.js
index feace415e..1c7beccf8 100644
--- a/lib/rules/no-ex-assign.js
+++ b/lib/rules/no-ex-assign.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-extend-native.js b/lib/rules/no-extend-native.js
index 3ba13090a..a1bf49f13 100644
--- a/lib/rules/no-extend-native.js
+++ b/lib/rules/no-extend-native.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 const globals = require("globals");
 
 //------------------------------------------------------------------------------
diff --git a/lib/rules/no-extra-bind.js b/lib/rules/no-extra-bind.js
index 8d901808f..b8376adfc 100644
--- a/lib/rules/no-extra-bind.js
+++ b/lib/rules/no-extra-bind.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-extra-boolean-cast.js b/lib/rules/no-extra-boolean-cast.js
index 381913617..5eac6b53f 100644
--- a/lib/rules/no-extra-boolean-cast.js
+++ b/lib/rules/no-extra-boolean-cast.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-extra-label.js b/lib/rules/no-extra-label.js
index 73a3fea9c..9310e90f7 100644
--- a/lib/rules/no-extra-label.js
+++ b/lib/rules/no-extra-label.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js
index 3d4ee19e8..814cf0faf 100644
--- a/lib/rules/no-extra-parens.js
+++ b/lib/rules/no-extra-parens.js
@@ -8,7 +8,7 @@
 // Rule Definition
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils.js");
+const astUtils = require("../util/ast-utils.js");
 
 module.exports = {
     meta: {
diff --git a/lib/rules/no-extra-semi.js b/lib/rules/no-extra-semi.js
index a07410885..4d40b5e15 100644
--- a/lib/rules/no-extra-semi.js
+++ b/lib/rules/no-extra-semi.js
@@ -10,7 +10,7 @@
 //------------------------------------------------------------------------------
 
 const FixTracker = require("../util/fix-tracker");
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-floating-decimal.js b/lib/rules/no-floating-decimal.js
index da8c47912..a62846cc1 100644
--- a/lib/rules/no-floating-decimal.js
+++ b/lib/rules/no-floating-decimal.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-func-assign.js b/lib/rules/no-func-assign.js
index a0a17f369..029f6a9d7 100644
--- a/lib/rules/no-func-assign.js
+++ b/lib/rules/no-func-assign.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-implicit-coercion.js b/lib/rules/no-implicit-coercion.js
index 1dd4d431d..3bfe3f0d3 100644
--- a/lib/rules/no-implicit-coercion.js
+++ b/lib/rules/no-implicit-coercion.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-inline-comments.js b/lib/rules/no-inline-comments.js
index 85a0d3831..2fb219092 100644
--- a/lib/rules/no-inline-comments.js
+++ b/lib/rules/no-inline-comments.js
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-invalid-this.js b/lib/rules/no-invalid-this.js
index a04673be8..480deebc1 100644
--- a/lib/rules/no-invalid-this.js
+++ b/lib/rules/no-invalid-this.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-irregular-whitespace.js b/lib/rules/no-irregular-whitespace.js
index 7f9adc10a..729f829a1 100644
--- a/lib/rules/no-irregular-whitespace.js
+++ b/lib/rules/no-irregular-whitespace.js
@@ -10,7 +10,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Constants
diff --git a/lib/rules/no-label-var.js b/lib/rules/no-label-var.js
index a880abfca..3f5aba4fc 100644
--- a/lib/rules/no-label-var.js
+++ b/lib/rules/no-label-var.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-labels.js b/lib/rules/no-labels.js
index e09bb7b6e..bd6ec57a3 100644
--- a/lib/rules/no-labels.js
+++ b/lib/rules/no-labels.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-mixed-operators.js b/lib/rules/no-mixed-operators.js
index 0df42259d..d4ead2006 100644
--- a/lib/rules/no-mixed-operators.js
+++ b/lib/rules/no-mixed-operators.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils.js");
+const astUtils = require("../util/ast-utils.js");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-multi-spaces.js b/lib/rules/no-multi-spaces.js
index d0ca097ad..41bcd4fb6 100644
--- a/lib/rules/no-multi-spaces.js
+++ b/lib/rules/no-multi-spaces.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-multi-str.js b/lib/rules/no-multi-str.js
index 2b97c261e..ee0aaaa4c 100644
--- a/lib/rules/no-multi-str.js
+++ b/lib/rules/no-multi-str.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-regex-spaces.js b/lib/rules/no-regex-spaces.js
index a50692a4c..089e06028 100644
--- a/lib/rules/no-regex-spaces.js
+++ b/lib/rules/no-regex-spaces.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-restricted-properties.js b/lib/rules/no-restricted-properties.js
index 71cc83c65..c0f1cfb6d 100644
--- a/lib/rules/no-restricted-properties.js
+++ b/lib/rules/no-restricted-properties.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-return-assign.js b/lib/rules/no-return-assign.js
index ca96da9f2..519f9e0d0 100644
--- a/lib/rules/no-return-assign.js
+++ b/lib/rules/no-return-assign.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-return-await.js b/lib/rules/no-return-await.js
index fc3373d3c..5f531a183 100644
--- a/lib/rules/no-return-await.js
+++ b/lib/rules/no-return-await.js
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-self-assign.js b/lib/rules/no-self-assign.js
index 5b65ea4f9..87d1f2ff4 100644
--- a/lib/rules/no-self-assign.js
+++ b/lib/rules/no-self-assign.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-sequences.js b/lib/rules/no-sequences.js
index 8eaf55572..e74943d81 100644
--- a/lib/rules/no-sequences.js
+++ b/lib/rules/no-sequences.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-shadow.js b/lib/rules/no-shadow.js
index 17ca1291d..955fc05e5 100644
--- a/lib/rules/no-shadow.js
+++ b/lib/rules/no-shadow.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-this-before-super.js b/lib/rules/no-this-before-super.js
index 944a32352..8a489879e 100644
--- a/lib/rules/no-this-before-super.js
+++ b/lib/rules/no-this-before-super.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-throw-literal.js b/lib/rules/no-throw-literal.js
index b080578bf..301354b6b 100644
--- a/lib/rules/no-throw-literal.js
+++ b/lib/rules/no-throw-literal.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-trailing-spaces.js b/lib/rules/no-trailing-spaces.js
index 4d70c3b5c..cca7af2aa 100644
--- a/lib/rules/no-trailing-spaces.js
+++ b/lib/rules/no-trailing-spaces.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-undef-init.js b/lib/rules/no-undef-init.js
index 2e0ab2462..f00b05d4f 100644
--- a/lib/rules/no-undef-init.js
+++ b/lib/rules/no-undef-init.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-unexpected-multiline.js b/lib/rules/no-unexpected-multiline.js
index 51ba7cfa7..181a00b2d 100644
--- a/lib/rules/no-unexpected-multiline.js
+++ b/lib/rules/no-unexpected-multiline.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-unmodified-loop-condition.js b/lib/rules/no-unmodified-loop-condition.js
index 623144dc0..fecd8ba5c 100644
--- a/lib/rules/no-unmodified-loop-condition.js
+++ b/lib/rules/no-unmodified-loop-condition.js
@@ -10,13 +10,12 @@
 //------------------------------------------------------------------------------
 
 const Traverser = require("../util/traverser"),
-    astUtils = require("../ast-utils");
+    astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
 //------------------------------------------------------------------------------
 
-const pushAll = Function.apply.bind(Array.prototype.push);
 const SENTINEL_PATTERN = /(?:(?:Call|Class|Function|Member|New|Yield)Expression|Statement|Declaration)$/;
 const LOOP_PATTERN = /^(?:DoWhile|For|While)Statement$/; // for-in/of statements don't have `test` property.
 const GROUP_PATTERN = /^(?:BinaryExpression|ConditionalExpression)$/;
@@ -356,7 +355,7 @@ module.exports = {
                 let scope;
 
                 while ((scope = queue.pop())) {
-                    pushAll(queue, scope.childScopes);
+                    queue.push(...scope.childScopes);
                     scope.variables.forEach(checkReferences);
                 }
 
diff --git a/lib/rules/no-unneeded-ternary.js b/lib/rules/no-unneeded-ternary.js
index b56ad038a..6b58b018d 100644
--- a/lib/rules/no-unneeded-ternary.js
+++ b/lib/rules/no-unneeded-ternary.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 // Operators that always result in a boolean value
 const BOOLEAN_OPERATORS = new Set(["==", "===", "!=", "!==", ">", ">=", "<", "<=", "in", "instanceof"]);
diff --git a/lib/rules/no-unsafe-negation.js b/lib/rules/no-unsafe-negation.js
index 5dc39f183..b04f80106 100644
--- a/lib/rules/no-unsafe-negation.js
+++ b/lib/rules/no-unsafe-negation.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-unused-vars.js b/lib/rules/no-unused-vars.js
index 0d5194bc8..fff3e33d9 100644
--- a/lib/rules/no-unused-vars.js
+++ b/lib/rules/no-unused-vars.js
@@ -10,7 +10,7 @@
 //------------------------------------------------------------------------------
 
 const lodash = require("lodash");
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-useless-call.js b/lib/rules/no-useless-call.js
index 925c9f79a..0778b374f 100644
--- a/lib/rules/no-useless-call.js
+++ b/lib/rules/no-useless-call.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-useless-computed-key.js b/lib/rules/no-useless-computed-key.js
index 07573075e..189f50853 100644
--- a/lib/rules/no-useless-computed-key.js
+++ b/lib/rules/no-useless-computed-key.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-useless-concat.js b/lib/rules/no-useless-concat.js
index a9ac78684..e8e36aa1c 100644
--- a/lib/rules/no-useless-concat.js
+++ b/lib/rules/no-useless-concat.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-useless-escape.js b/lib/rules/no-useless-escape.js
index 2ddab7854..7f4b87ed9 100644
--- a/lib/rules/no-useless-escape.js
+++ b/lib/rules/no-useless-escape.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-useless-return.js b/lib/rules/no-useless-return.js
index d801c0e46..b5b8ebc32 100644
--- a/lib/rules/no-useless-return.js
+++ b/lib/rules/no-useless-return.js
@@ -8,22 +8,13 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils"),
+const astUtils = require("../util/ast-utils"),
     FixTracker = require("../util/fix-tracker");
 
 //------------------------------------------------------------------------------
 // Helpers
 //------------------------------------------------------------------------------
 
-/**
- * Adds all elements of 2nd argument into 1st argument.
- *
- * @param {Array} array - The destination array to add.
- * @param {Array} elements - The source array to add.
- * @returns {void}
- */
-const pushAll = Function.apply.bind(Array.prototype.push);
-
 /**
  * Removes the given element from the array.
  *
@@ -137,7 +128,7 @@ module.exports = {
                     continue;
                 }
 
-                pushAll(uselessReturns, segmentInfoMap.get(segment).uselessReturns);
+                uselessReturns.push(...segmentInfoMap.get(segment).uselessReturns);
             }
 
             return uselessReturns;
diff --git a/lib/rules/no-var.js b/lib/rules/no-var.js
index 5ca868e65..2c32a023d 100644
--- a/lib/rules/no-var.js
+++ b/lib/rules/no-var.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/no-warning-comments.js b/lib/rules/no-warning-comments.js
index 4e05cc411..5ff76e3a3 100644
--- a/lib/rules/no-warning-comments.js
+++ b/lib/rules/no-warning-comments.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-whitespace-before-property.js b/lib/rules/no-whitespace-before-property.js
index fc4c25f84..4b71a9ec1 100644
--- a/lib/rules/no-whitespace-before-property.js
+++ b/lib/rules/no-whitespace-before-property.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/object-curly-newline.js b/lib/rules/object-curly-newline.js
index e979b5ffe..494e78cff 100644
--- a/lib/rules/object-curly-newline.js
+++ b/lib/rules/object-curly-newline.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 const lodash = require("lodash");
 
 //------------------------------------------------------------------------------
diff --git a/lib/rules/object-curly-spacing.js b/lib/rules/object-curly-spacing.js
index b37d6fd90..e7c847a6a 100644
--- a/lib/rules/object-curly-spacing.js
+++ b/lib/rules/object-curly-spacing.js
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/object-shorthand.js b/lib/rules/object-shorthand.js
index 71a320a0e..21f039f8b 100644
--- a/lib/rules/object-shorthand.js
+++ b/lib/rules/object-shorthand.js
@@ -17,7 +17,7 @@ const OPTIONS = {
 //------------------------------------------------------------------------------
 // Requirements
 //------------------------------------------------------------------------------
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/operator-assignment.js b/lib/rules/operator-assignment.js
index ad516bbfc..8e4b7025e 100644
--- a/lib/rules/operator-assignment.js
+++ b/lib/rules/operator-assignment.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/operator-linebreak.js b/lib/rules/operator-linebreak.js
index 558258b27..be2709a1b 100644
--- a/lib/rules/operator-linebreak.js
+++ b/lib/rules/operator-linebreak.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/padding-line-between-statements.js b/lib/rules/padding-line-between-statements.js
index d2254fa6a..f571e1e2e 100644
--- a/lib/rules/padding-line-between-statements.js
+++ b/lib/rules/padding-line-between-statements.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
@@ -367,6 +367,7 @@ const StatementTypes = {
 
     block: newNodeTypeTester("BlockStatement"),
     empty: newNodeTypeTester("EmptyStatement"),
+    function: newNodeTypeTester("FunctionDeclaration"),
 
     break: newKeywordTester("break"),
     case: newKeywordTester("case"),
@@ -378,7 +379,6 @@ const StatementTypes = {
     do: newKeywordTester("do"),
     export: newKeywordTester("export"),
     for: newKeywordTester("for"),
-    function: newKeywordTester("function"),
     if: newKeywordTester("if"),
     import: newKeywordTester("import"),
     let: newKeywordTester("let"),
diff --git a/lib/rules/prefer-const.js b/lib/rules/prefer-const.js
index a784e2e31..774fcf064 100644
--- a/lib/rules/prefer-const.js
+++ b/lib/rules/prefer-const.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
@@ -15,15 +15,6 @@ const PATTERN_TYPE = /^(?:.+?Pattern|RestElement|SpreadProperty|ExperimentalRest
 const DECLARATION_HOST_TYPE = /^(?:Program|BlockStatement|SwitchCase)$/;
 const DESTRUCTURING_HOST_TYPE = /^(?:VariableDeclarator|AssignmentExpression)$/;
 
-/**
- * Adds multiple items to the tail of an array.
- *
- * @param {any[]} array - A destination to add.
- * @param {any[]} values - Items to be added.
- * @returns {void}
- */
-const pushAll = Function.apply.bind(Array.prototype.push);
-
 /**
  * Checks whether a given node is located at `ForStatement.init` or not.
  *
@@ -364,7 +355,7 @@ module.exports = {
 
             VariableDeclaration(node) {
                 if (node.kind === "let" && !isInitOfForStatement(node)) {
-                    pushAll(variables, context.getDeclaredVariables(node));
+                    variables.push(...context.getDeclaredVariables(node));
                 }
             }
         };
diff --git a/lib/rules/prefer-object-spread.js b/lib/rules/prefer-object-spread.js
index 6ddb66540..8e54de249 100644
--- a/lib/rules/prefer-object-spread.js
+++ b/lib/rules/prefer-object-spread.js
@@ -12,7 +12,7 @@ const {
     isOpeningParenToken,
     isClosingParenToken,
     isParenthesised
-} = require("../ast-utils");
+} = require("../util/ast-utils");
 
 const ANY_SPACE = /\s/;
 
@@ -180,8 +180,14 @@ function defineFixer(node, sourceCode) {
                 for (const innerParen of innerParens) {
                     yield fixer.remove(innerParen);
                 }
-                yield fixer.removeRange([left.range[0], getEndWithSpaces(left, sourceCode)]);
-                yield fixer.removeRange([getStartWithSpaces(right, sourceCode), right.range[1]]);
+                const leftRange = [left.range[0], getEndWithSpaces(left, sourceCode)];
+                const rightRange = [
+                    Math.max(getStartWithSpaces(right, sourceCode), leftRange[1]), // Ensure ranges don't overlap
+                    right.range[1]
+                ];
+
+                yield fixer.removeRange(leftRange);
+                yield fixer.removeRange(rightRange);
 
                 // Remove the comma of this argument if it's duplication.
                 if (
diff --git a/lib/rules/prefer-promise-reject-errors.js b/lib/rules/prefer-promise-reject-errors.js
index f92bd58d2..e3d298a74 100644
--- a/lib/rules/prefer-promise-reject-errors.js
+++ b/lib/rules/prefer-promise-reject-errors.js
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/prefer-spread.js b/lib/rules/prefer-spread.js
index 62cfb28e7..9bf69c80f 100644
--- a/lib/rules/prefer-spread.js
+++ b/lib/rules/prefer-spread.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/prefer-template.js b/lib/rules/prefer-template.js
index 14c419942..0471d61ca 100644
--- a/lib/rules/prefer-template.js
+++ b/lib/rules/prefer-template.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/quotes.js b/lib/rules/quotes.js
index 78794e72b..8dd61c3e3 100644
--- a/lib/rules/quotes.js
+++ b/lib/rules/quotes.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Constants
diff --git a/lib/rules/radix.js b/lib/rules/radix.js
index 7e116a6fb..f71220beb 100644
--- a/lib/rules/radix.js
+++ b/lib/rules/radix.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/require-atomic-updates.js b/lib/rules/require-atomic-updates.js
new file mode 100644
index 000000000..5d7d71514
--- /dev/null
+++ b/lib/rules/require-atomic-updates.js
@@ -0,0 +1,239 @@
+/**
+ * @fileoverview disallow assignments that can lead to race conditions due to usage of `await` or `yield`
+ * @author Teddy Katz
+ */
+"use strict";
+
+const astUtils = require("../util/ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        docs: {
+            description: "disallow assignments that can lead to race conditions due to usage of `await` or `yield`",
+            category: "Possible Errors",
+            recommended: false,
+            url: "https://eslint.org/docs/rules/require-atomic-updates"
+        },
+        fixable: null,
+        schema: [],
+        messages: {
+            nonAtomicUpdate: "Possible race condition: `{{value}}` might be reassigned based on an outdated value of `{{value}}`."
+        }
+    },
+
+    create(context) {
+        const sourceCode = context.getSourceCode();
+        const identifierToSurroundingFunctionMap = new WeakMap();
+        const expressionsByCodePathSegment = new Map();
+
+        //----------------------------------------------------------------------
+        // Helpers
+        //----------------------------------------------------------------------
+
+        const resolvedVariableCache = new WeakMap();
+
+        /**
+         * Gets the variable scope around this variable reference
+         * @param {ASTNode} identifier An `Identifier` AST node
+         * @returns {Scope|null} An escope Scope
+         */
+        function getScope(identifier) {
+            for (let currentNode = identifier; currentNode; currentNode = currentNode.parent) {
+                const scope = sourceCode.scopeManager.acquire(currentNode, true);
+
+                if (scope) {
+                    return scope;
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Resolves a given identifier to a given scope
+         * @param {ASTNode} identifier An `Identifier` AST node
+         * @param {Scope} scope An escope Scope
+         * @returns {Variable|null} An escope Variable corresponding to the given identifier
+         */
+        function resolveVariableInScope(identifier, scope) {
+            return scope.variables.find(variable => variable.name === identifier.name) ||
+                (scope.upper ? resolveVariableInScope(identifier, scope.upper) : null);
+        }
+
+        /**
+         * Resolves an identifier to a variable
+         * @param {ASTNode} identifier An identifier node
+         * @returns {Variable|null} The escope Variable that uses this identifier
+         */
+        function resolveVariable(identifier) {
+            if (!resolvedVariableCache.has(identifier)) {
+                const surroundingScope = getScope(identifier);
+
+                if (surroundingScope) {
+                    resolvedVariableCache.set(identifier, resolveVariableInScope(identifier, surroundingScope));
+                } else {
+                    resolvedVariableCache.set(identifier, null);
+                }
+            }
+
+            return resolvedVariableCache.get(identifier);
+        }
+
+        /**
+         * Checks if an expression is a variable that can only be observed within the given function.
+         * @param {ASTNode} expression The expression to check
+         * @param {ASTNode} surroundingFunction The function node
+         * @returns {boolean} `true` if the expression is a variable which is local to the given function, and is never
+         * referenced in a closure.
+         */
+        function isLocalVariableWithoutEscape(expression, surroundingFunction) {
+            if (expression.type !== "Identifier") {
+                return false;
+            }
+
+            const variable = resolveVariable(expression);
+
+            if (!variable) {
+                return false;
+            }
+
+            return variable.references.every(reference => identifierToSurroundingFunctionMap.get(reference.identifier) === surroundingFunction) &&
+                variable.defs.every(def => identifierToSurroundingFunctionMap.get(def.name) === surroundingFunction);
+        }
+
+        /**
+         * Reports an AssignmentExpression node that has a non-atomic update
+         * @param {ASTNode} assignmentExpression The assignment that is potentially unsafe
+         * @returns {void}
+         */
+        function reportAssignment(assignmentExpression) {
+            context.report({
+                node: assignmentExpression,
+                messageId: "nonAtomicUpdate",
+                data: {
+                    value: sourceCode.getText(assignmentExpression.left)
+                }
+            });
+        }
+
+        /**
+         * If the control flow graph of a function enters an assignment expression, then does the
+         * both of the following steps in order (possibly with other steps in between) before exiting the
+         * assignment expression, then the assignment might be using an outdated value.
+         * 1. Enters a read of the variable or property assigned in the expression (not necessary if operator assignment is used)
+         * 2. Exits an `await` or `yield` expression
+         *
+         * This function checks for the outdated values and reports them.
+         * @param {CodePathSegment} codePathSegment The current code path segment to traverse
+         * @param {ASTNode} surroundingFunction The function node containing the code path segment
+         * @returns {void}
+         */
+        function findOutdatedReads(
+            codePathSegment,
+            surroundingFunction,
+            {
+                seenSegments = new Set(),
+                openAssignmentsWithoutReads = new Set(),
+                openAssignmentsWithReads = new Set()
+            } = {}
+        ) {
+            if (seenSegments.has(codePathSegment)) {
+
+                // An AssignmentExpression can't contain loops, so it's not necessary to reenter them with new state.
+                return;
+            }
+
+            expressionsByCodePathSegment.get(codePathSegment).forEach(({ entering, node }) => {
+                if (node.type === "AssignmentExpression") {
+                    if (entering) {
+                        (node.operator === "=" ? openAssignmentsWithoutReads : openAssignmentsWithReads).add(node);
+                    } else {
+                        openAssignmentsWithoutReads.delete(node);
+                        openAssignmentsWithReads.delete(node);
+                    }
+                } else if (!entering && (node.type === "AwaitExpression" || node.type === "YieldExpression")) {
+                    [...openAssignmentsWithReads]
+                        .filter(assignment => !isLocalVariableWithoutEscape(assignment.left, surroundingFunction))
+                        .forEach(reportAssignment);
+
+                    openAssignmentsWithReads.clear();
+                } else if (!entering && (node.type === "Identifier" || node.type === "MemberExpression")) {
+                    [...openAssignmentsWithoutReads]
+                        .filter(assignment => (
+                            assignment.left !== node &&
+                            assignment.left.type === node.type &&
+                            astUtils.equalTokens(assignment.left, node, sourceCode)
+                        ))
+                        .forEach(assignment => {
+                            openAssignmentsWithoutReads.delete(assignment);
+                            openAssignmentsWithReads.add(assignment);
+                        });
+                }
+            });
+
+            codePathSegment.nextSegments.forEach(nextSegment => {
+                findOutdatedReads(
+                    nextSegment,
+                    surroundingFunction,
+                    {
+                        seenSegments: new Set(seenSegments).add(codePathSegment),
+                        openAssignmentsWithoutReads: new Set(openAssignmentsWithoutReads),
+                        openAssignmentsWithReads: new Set(openAssignmentsWithReads)
+                    }
+                );
+            });
+        }
+
+        //----------------------------------------------------------------------
+        // Public
+        //----------------------------------------------------------------------
+
+        const currentCodePathSegmentStack = [];
+        let currentCodePathSegment = null;
+        const functionStack = [];
+
+        return {
+            onCodePathStart() {
+                currentCodePathSegmentStack.push(currentCodePathSegment);
+            },
+
+            onCodePathEnd(codePath, node) {
+                currentCodePathSegment = currentCodePathSegmentStack.pop();
+
+                if (astUtils.isFunction(node) && (node.async || node.generator)) {
+                    findOutdatedReads(codePath.initialSegment, node);
+                }
+            },
+
+            onCodePathSegmentStart(segment) {
+                currentCodePathSegment = segment;
+                expressionsByCodePathSegment.set(segment, []);
+            },
+
+            "AssignmentExpression, Identifier, MemberExpression, AwaitExpression, YieldExpression"(node) {
+                expressionsByCodePathSegment.get(currentCodePathSegment).push({ entering: true, node });
+            },
+
+            "AssignmentExpression, Identifier, MemberExpression, AwaitExpression, YieldExpression:exit"(node) {
+                expressionsByCodePathSegment.get(currentCodePathSegment).push({ entering: false, node });
+            },
+
+            ":function"(node) {
+                functionStack.push(node);
+            },
+
+            ":function:exit"() {
+                functionStack.pop();
+            },
+
+            Identifier(node) {
+                if (functionStack.length) {
+                    identifierToSurroundingFunctionMap.set(node, functionStack[functionStack.length - 1]);
+                }
+            }
+        };
+    }
+};
diff --git a/lib/rules/require-await.js b/lib/rules/require-await.js
index 5517cf867..de39f372f 100644
--- a/lib/rules/require-await.js
+++ b/lib/rules/require-await.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/semi-spacing.js b/lib/rules/semi-spacing.js
index 07af99376..cde93abf3 100644
--- a/lib/rules/semi-spacing.js
+++ b/lib/rules/semi-spacing.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/semi-style.js b/lib/rules/semi-style.js
index de6592813..34899bb54 100644
--- a/lib/rules/semi-style.js
+++ b/lib/rules/semi-style.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/semi.js b/lib/rules/semi.js
index 33a214db7..129d10641 100644
--- a/lib/rules/semi.js
+++ b/lib/rules/semi.js
@@ -9,7 +9,7 @@
 //------------------------------------------------------------------------------
 
 const FixTracker = require("../util/fix-tracker");
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/sort-keys.js b/lib/rules/sort-keys.js
index 85bfe56d5..6e538f732 100644
--- a/lib/rules/sort-keys.js
+++ b/lib/rules/sort-keys.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils"),
+const astUtils = require("../util/ast-utils"),
     naturalCompare = require("natural-compare");
 
 //------------------------------------------------------------------------------
diff --git a/lib/rules/space-before-blocks.js b/lib/rules/space-before-blocks.js
index 2b82066d7..4f22ae6b6 100644
--- a/lib/rules/space-before-blocks.js
+++ b/lib/rules/space-before-blocks.js
@@ -5,7 +5,7 @@
 
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/space-before-function-paren.js b/lib/rules/space-before-function-paren.js
index 51f6cc432..81697d64f 100644
--- a/lib/rules/space-before-function-paren.js
+++ b/lib/rules/space-before-function-paren.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/space-in-parens.js b/lib/rules/space-in-parens.js
index 7e315c448..aa1374380 100644
--- a/lib/rules/space-in-parens.js
+++ b/lib/rules/space-in-parens.js
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/space-unary-ops.js b/lib/rules/space-unary-ops.js
index 6fbcc15c0..6fe25443c 100644
--- a/lib/rules/space-unary-ops.js
+++ b/lib/rules/space-unary-ops.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/spaced-comment.js b/lib/rules/spaced-comment.js
index 3a76c0c26..6fbe2aac7 100644
--- a/lib/rules/spaced-comment.js
+++ b/lib/rules/spaced-comment.js
@@ -5,7 +5,7 @@
 "use strict";
 
 const lodash = require("lodash");
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/strict.js b/lib/rules/strict.js
index 633a8b323..8b5757738 100644
--- a/lib/rules/strict.js
+++ b/lib/rules/strict.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/switch-colon-spacing.js b/lib/rules/switch-colon-spacing.js
index cf19df6ed..23dfff613 100644
--- a/lib/rules/switch-colon-spacing.js
+++ b/lib/rules/switch-colon-spacing.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/symbol-description.js b/lib/rules/symbol-description.js
index 95c1a1deb..271012b54 100644
--- a/lib/rules/symbol-description.js
+++ b/lib/rules/symbol-description.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/template-curly-spacing.js b/lib/rules/template-curly-spacing.js
index 707ea76a3..6702d730c 100644
--- a/lib/rules/template-curly-spacing.js
+++ b/lib/rules/template-curly-spacing.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/rules/wrap-iife.js b/lib/rules/wrap-iife.js
index a21bf2163..d006d30a0 100644
--- a/lib/rules/wrap-iife.js
+++ b/lib/rules/wrap-iife.js
@@ -9,7 +9,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/yoda.js b/lib/rules/yoda.js
index 7f3bc6115..35368dd4d 100644
--- a/lib/rules/yoda.js
+++ b/lib/rules/yoda.js
@@ -8,7 +8,7 @@
 // Requirements
 //--------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //--------------------------------------------------------------------------
 // Helpers
diff --git a/lib/token-store/index.js b/lib/token-store/index.js
index 1446b9ff0..d59a0bba0 100644
--- a/lib/token-store/index.js
+++ b/lib/token-store/index.js
@@ -13,7 +13,7 @@ const cursors = require("./cursors");
 const ForwardTokenCursor = require("./forward-token-cursor");
 const PaddedTokenCursor = require("./padded-token-cursor");
 const utils = require("./utils");
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/ast-utils.js b/lib/util/ast-utils.js
similarity index 100%
rename from lib/ast-utils.js
rename to lib/util/ast-utils.js
diff --git a/lib/util/fix-tracker.js b/lib/util/fix-tracker.js
index 067070df0..3c9bf63d2 100644
--- a/lib/util/fix-tracker.js
+++ b/lib/util/fix-tracker.js
@@ -8,7 +8,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const astUtils = require("../ast-utils");
+const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Public Interface
diff --git a/lib/util/glob-util.js b/lib/util/glob-utils.js
similarity index 98%
rename from lib/util/glob-util.js
rename to lib/util/glob-utils.js
index 9d5d7e5aa..5bb3baa91 100644
--- a/lib/util/glob-util.js
+++ b/lib/util/glob-utils.js
@@ -13,10 +13,10 @@ const lodash = require("lodash"),
     path = require("path"),
     GlobSync = require("./glob"),
 
-    pathUtil = require("./path-util"),
+    pathUtil = require("./path-utils"),
     IgnoredPaths = require("../ignored-paths");
 
-const debug = require("debug")("eslint:glob-util");
+const debug = require("debug")("eslint:glob-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
@@ -194,7 +194,7 @@ function listFilesToProcess(globPatterns, providedOptions) {
     );
 
     /*
-     * The test "should use default options if none are provided" (source-code-util.js) checks that 'module.exports.resolveFileGlobPatterns' was called.
+     * The test "should use default options if none are provided" (source-code-utils.js) checks that 'module.exports.resolveFileGlobPatterns' was called.
      * So it cannot use the local function "resolveFileGlobPatterns".
      */
     const resolvedGlobPatterns = module.exports.resolveFileGlobPatterns(globPatterns, options);
diff --git a/lib/util/naming.js b/lib/util/naming.js
index a5d6b9d79..d91722961 100644
--- a/lib/util/naming.js
+++ b/lib/util/naming.js
@@ -7,7 +7,7 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const pathUtil = require("../util/path-util");
+const pathUtil = require("../util/path-utils");
 
 //------------------------------------------------------------------------------
 // Private
diff --git a/lib/util/node-event-generator.js b/lib/util/node-event-generator.js
index 9d477bbb4..1ae566431 100644
--- a/lib/util/node-event-generator.js
+++ b/lib/util/node-event-generator.js
@@ -45,8 +45,8 @@ function getPossibleTypes(parsedSelector) {
         case "matches": {
             const typesForComponents = parsedSelector.selectors.map(getPossibleTypes);
 
-            if (typesForComponents.every(typesForComponent => typesForComponent)) {
-                return lodash.union.apply(null, typesForComponents);
+            if (typesForComponents.every(Boolean)) {
+                return lodash.union(...typesForComponents);
             }
             return null;
         }
@@ -63,7 +63,7 @@ function getPossibleTypes(parsedSelector) {
              * If at least one of the components could only match a particular type, the compound could only match
              * the intersection of those types.
              */
-            return lodash.intersection.apply(null, typesForComponents);
+            return lodash.intersection(...typesForComponents);
         }
 
         case "child":
diff --git a/lib/util/npm-util.js b/lib/util/npm-utils.js
similarity index 100%
rename from lib/util/npm-util.js
rename to lib/util/npm-utils.js
diff --git a/lib/util/path-util.js b/lib/util/path-utils.js
similarity index 100%
rename from lib/util/path-util.js
rename to lib/util/path-utils.js
diff --git a/lib/util/source-code-util.js b/lib/util/source-code-utils.js
similarity index 97%
rename from lib/util/source-code-util.js
rename to lib/util/source-code-utils.js
index 66d91cd21..2baa34899 100644
--- a/lib/util/source-code-util.js
+++ b/lib/util/source-code-utils.js
@@ -10,10 +10,10 @@
 //------------------------------------------------------------------------------
 
 const CLIEngine = require("../cli-engine"),
-    globUtil = require("./glob-util"),
+    globUtil = require("./glob-utils"),
     baseDefaultOptions = require("../../conf/default-cli-options");
 
-const debug = require("debug")("eslint:source-code-util");
+const debug = require("debug")("eslint:source-code-utils");
 
 //------------------------------------------------------------------------------
 // Helpers
diff --git a/lib/util/source-code.js b/lib/util/source-code.js
index d34151995..dd45f6112 100644
--- a/lib/util/source-code.js
+++ b/lib/util/source-code.js
@@ -10,7 +10,7 @@
 
 const TokenStore = require("../token-store"),
     Traverser = require("./traverser"),
-    astUtils = require("../ast-utils"),
+    astUtils = require("../util/ast-utils"),
     lodash = require("lodash");
 
 //------------------------------------------------------------------------------
diff --git a/packages/eslint-config-eslint/default.yml b/packages/eslint-config-eslint/default.yml
index da4faf6e5..127e7d7d7 100644
--- a/packages/eslint-config-eslint/default.yml
+++ b/packages/eslint-config-eslint/default.yml
@@ -1,10 +1,6 @@
 extends:
     - "eslint:recommended"
     - "plugin:node/recommended"
-
-plugins:
-    - "node"
-
 rules:
     array-bracket-spacing: "error"
     array-callback-return: "error"
@@ -54,6 +50,7 @@ rules:
     new-parens: "error"
     no-alert: "error"
     no-array-constructor: "error"
+    no-async-promise-executor: "error"
     no-buffer-constructor: "error"
     no-caller: "error"
     no-catch-shadow: "error"
@@ -160,6 +157,7 @@ rules:
     quotes: ["error", "double"]
     quote-props: ["error", "as-needed"]
     radix: "error"
+    require-atomic-updates: "error"
     require-jsdoc: "error"
     rest-spread-spacing: "error"
     semi: "error"


Test patch:
diff --git a/lib/testers/rule-tester.js b/lib/testers/rule-tester.js
index a02f0b9be..d7e4c2bfa 100644
--- a/lib/testers/rule-tester.js
+++ b/lib/testers/rule-tester.js
@@ -140,7 +140,7 @@ const IT = Symbol("it");
  */
 function itDefaultHandler(text, method) {
     try {
-        return method.apply(this);
+        return method.call(this);
     } catch (err) {
         if (err instanceof assert.AssertionError) {
             err.message += ` (${util.inspect(err.actual)} ${err.operator} ${util.inspect(err.expected)})`;
@@ -157,7 +157,7 @@ function itDefaultHandler(text, method) {
  * @returns {any} Returned value of `method`.
  */
 function describeDefaultHandler(text, method) {
-    return method.apply(this);
+    return method.call(this);
 }
 
 class RuleTester {
diff --git a/tests/fixtures/ignored-paths/subdir/.eslintignoreInChildDir b/tests/fixtures/ignored-paths/subdir/.eslintignoreInChildDir
new file mode 100644
index 000000000..8c7a20132
--- /dev/null
+++ b/tests/fixtures/ignored-paths/subdir/.eslintignoreInChildDir
@@ -0,0 +1,5 @@
+/undef.js
+foo.js
+*.txt
+!/bar.txt
+!baz.txt
diff --git a/tests/lib/ast-utils.js b/tests/lib/ast-utils.js
index c728b8457..972882a12 100644
--- a/tests/lib/ast-utils.js
+++ b/tests/lib/ast-utils.js
@@ -11,7 +11,7 @@
 
 const assert = require("chai").assert,
     espree = require("espree"),
-    astUtils = require("../../lib/ast-utils"),
+    astUtils = require("../../lib/util/ast-utils"),
     Linter = require("../../lib/linter"),
     SourceCode = require("../../lib/util/source-code");
 
diff --git a/tests/lib/config/autoconfig.js b/tests/lib/config/autoconfig.js
index fb8e12e94..dd059f5d1 100644
--- a/tests/lib/config/autoconfig.js
+++ b/tests/lib/config/autoconfig.js
@@ -11,7 +11,7 @@
 
 const assert = require("chai").assert,
     autoconfig = require("../../../lib/config/autoconfig"),
-    sourceCodeUtil = require("../../../lib/util/source-code-util"),
+    sourceCodeUtil = require("../../../lib/util/source-code-utils"),
     baseDefaultOptions = require("../../../conf/default-cli-options");
 
 const defaultOptions = Object.assign({}, baseDefaultOptions, { cwd: process.cwd() });
diff --git a/tests/lib/config/config-initializer.js b/tests/lib/config/config-initializer.js
index 91cc6476b..de70ac156 100644
--- a/tests/lib/config/config-initializer.js
+++ b/tests/lib/config/config-initializer.js
@@ -16,7 +16,7 @@ const assert = require("chai").assert,
     sinon = require("sinon"),
     sh = require("shelljs"),
     autoconfig = require("../../../lib/config/autoconfig"),
-    npmUtil = require("../../../lib/util/npm-util");
+    npmUtil = require("../../../lib/util/npm-utils");
 
 const originalDir = process.cwd();
 const proxyquire = require("proxyquire").noPreserveCache();
diff --git a/tests/lib/ignored-paths.js b/tests/lib/ignored-paths.js
index 010488931..dfcf310e5 100644
--- a/tests/lib/ignored-paths.js
+++ b/tests/lib/ignored-paths.js
@@ -123,7 +123,7 @@ describe("IgnoredPaths", () => {
             const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath() });
             const ignorePatterns = getIgnorePatterns(ignoredPaths);
 
-            assert.isNotNull(ignoredPaths.baseDir);
+            assert.isNotNull(ignoredPaths.ignoreFileDir);
             assert.deepStrictEqual(getIgnoreFiles(ignoredPaths), [expectedIgnoreFile]);
             assert.include(ignorePatterns, "sampleignorepattern");
         });
@@ -131,7 +131,7 @@ describe("IgnoredPaths", () => {
         it("should set baseDir to cwd when no ignore file was loaded", () => {
             const ignoredPaths = new IgnoredPaths({ cwd: getFixturePath("no-ignore-file") });
 
-            assert.strictEqual(ignoredPaths.baseDir, getFixturePath("no-ignore-file"));
+            assert.strictEqual(ignoredPaths.ignoreFileDir, getFixturePath("no-ignore-file"));
         });
 
         it("should not travel to parent directories to find .eslintignore when it's missing and cwd is provided", () => {
@@ -241,7 +241,7 @@ describe("IgnoredPaths", () => {
         it("should set baseDir to directory containing ignorePath if provided", () => {
             const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: ignoreFilePath, cwd: getFixturePath() });
 
-            assert.strictEqual(ignoredPaths.baseDir, path.dirname(ignoreFilePath));
+            assert.strictEqual(ignoredPaths.ignoreFileDir, path.dirname(ignoreFilePath));
         });
 
     });
@@ -401,6 +401,40 @@ describe("IgnoredPaths", () => {
             assert.isFalse(ignoredPaths.contains(getFixturePath("subdir/undef.js")));
             assert.isTrue(ignoredPaths.contains(getFixturePath("undef.js")));
         });
+
+        it("should resolve relative paths from the ignorePath when it's in a child directory", () => {
+            const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath("subdir/.eslintignoreInChildDir"), cwd: getFixturePath() });
+
+            assert.isTrue(ignoredPaths.contains(getFixturePath("subdir/undef.js")));
+            assert.isFalse(ignoredPaths.contains(getFixturePath("undef.js")));
+            assert.isTrue(ignoredPaths.contains(getFixturePath("foo.js")));
+            assert.isTrue(ignoredPaths.contains(getFixturePath("subdir/foo.js")));
+
+            assert.isTrue(ignoredPaths.contains(getFixturePath("node_modules/bar.js")));
+        });
+
+        it("should resolve relative paths from the ignorePath when it contains negated globs", () => {
+            const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath("subdir/.eslintignoreInChildDir"), cwd: getFixturePath() });
+
+            assert.isTrue(ignoredPaths.contains("subdir/blah.txt"));
+            assert.isTrue(ignoredPaths.contains("blah.txt"));
+            assert.isFalse(ignoredPaths.contains("subdir/bar.txt"));
+            assert.isTrue(ignoredPaths.contains("bar.txt"));
+            assert.isFalse(ignoredPaths.contains("subdir/baz.txt"));
+            assert.isFalse(ignoredPaths.contains("baz.txt"));
+        });
+
+        it("should resolve default ignore patterns from the CWD even when the ignorePath is in a subdirectory", () => {
+            const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath("subdir/.eslintignoreInChildDir"), cwd: getFixturePath() });
+
+            assert.isTrue(ignoredPaths.contains("node_modules/blah.js"));
+        });
+
+        it("should resolve default ignore patterns from the CWD even when the ignorePath is in a parent directory", () => {
+            const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath(".eslintignoreForDifferentCwd"), cwd: getFixturePath("subdir") });
+
+            assert.isTrue(ignoredPaths.contains("node_modules/blah.js"));
+        });
     });
 
     describe("initialization with ignorePath containing commented lines", () => {
diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js
index ab8ba4c18..b25e25949 100644
--- a/tests/lib/rules/indent.js
+++ b/tests/lib/rules/indent.js
@@ -64,7 +64,7 @@ function unIndent(strings) {
     const templateValue = strings[0];
     const lines = templateValue.replace(/^\n/, "").replace(/\n\s*$/, "").split("\n");
     const lineIndents = lines.filter(line => line.trim()).map(line => line.match(/ */)[0].length);
-    const minLineIndent = Math.min.apply(null, lineIndents);
+    const minLineIndent = Math.min(...lineIndents);
 
     return lines.map(line => line.slice(minLineIndent)).join("\n");
 }
@@ -4843,6 +4843,56 @@ ruleTester.run("indent", rule, {
                         2
                 }
             }
+        `,
+
+        //----------------------------------------------------------------------
+        // Comment alignment tests
+        //----------------------------------------------------------------------
+        unIndent`
+            if (foo) {
+            // Comment can align with code immediately above even if "incorrect" alignment
+                doSomething();
+            }
+        `,
+        unIndent`
+            if (foo) {
+                doSomething();
+            // Comment can align with code immediately below even if "incorrect" alignment
+            }
+        `,
+        unIndent`
+            if (foo) {
+                // Comment can be in correct alignment even if not aligned with code above/below
+            }
+        `,
+        unIndent`
+            if (foo) {
+
+                // Comment can be in correct alignment even if gaps between (and not aligned with) code above/below
+
+            }
+        `,
+        unIndent`
+            [{
+                foo
+            },
+
+            // Comment between nodes
+
+            {
+                bar
+            }];
+        `,
+        unIndent`
+            [{
+                foo
+            },
+
+            // Comment between nodes
+
+            { // comment
+                bar
+            }];
         `
     ],
 
@@ -9383,6 +9433,71 @@ ruleTester.run("indent", rule, {
                 }
             `,
             errors: expectedErrors([4, 12, 8, "Numeric"])
+        },
+
+        //----------------------------------------------------------------------
+        // Comment alignment tests
+        //----------------------------------------------------------------------
+        {
+            code: unIndent`
+                if (foo) {
+
+                // Comment cannot align with code immediately above if there is a whitespace gap
+                    doSomething();
+                }
+            `,
+            output: unIndent`
+                if (foo) {
+
+                    // Comment cannot align with code immediately above if there is a whitespace gap
+                    doSomething();
+                }
+            `,
+            errors: expectedErrors([3, 4, 0, "Line"])
+        },
+        {
+            code: unIndent`
+                if (foo) {
+                    foo(
+                        bar);
+                // Comment cannot align with code immediately below if there is a whitespace gap
+
+                }
+            `,
+            output: unIndent`
+                if (foo) {
+                    foo(
+                        bar);
+                    // Comment cannot align with code immediately below if there is a whitespace gap
+
+                }
+            `,
+            errors: expectedErrors([4, 4, 0, "Line"])
+        },
+        {
+            code: unIndent`
+                [{
+                    foo
+                },
+
+                    // Comment between nodes
+
+                {
+                    bar
+                }];
+            `,
+            output: unIndent`
+                [{
+                    foo
+                },
+
+                // Comment between nodes
+
+                {
+                    bar
+                }];
+            `,
+            errors: expectedErrors([5, 0, 4, "Line"])
         }
     ]
 });
diff --git a/tests/lib/rules/no-async-promise-executor.js b/tests/lib/rules/no-async-promise-executor.js
new file mode 100644
index 000000000..d5bcd0ae9
--- /dev/null
+++ b/tests/lib/rules/no-async-promise-executor.js
@@ -0,0 +1,61 @@
+/**
+ * @fileoverview disallow using an async function as a Promise executor
+ * @author Teddy Katz
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const rule = require("../../../lib/rules/no-async-promise-executor");
+const RuleTester = require("../../../lib/testers/rule-tester");
+
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 8 } });
+
+ruleTester.run("no-async-promise-executor", rule, {
+
+    valid: [
+        "new Promise((resolve, reject) => {})",
+        "new Promise((resolve, reject) => {}, async function unrelated() {})",
+        "new Foo(async (resolve, reject) => {})"
+    ],
+
+    invalid: [
+        {
+            code: "new Promise(async function foo(resolve, reject) {})",
+            errors: [{
+                message: "Promise executor functions should not be async.",
+                line: 1,
+                column: 13,
+                endLine: 1,
+                endColumn: 18
+            }]
+        },
+        {
+            code: "new Promise(async (resolve, reject) => {})",
+            errors: [{
+                message: "Promise executor functions should not be async.",
+                line: 1,
+                column: 13,
+                endLine: 1,
+                endColumn: 18
+            }]
+        },
+        {
+            code: "new Promise(((((async () => {})))))",
+            errors: [{
+                message: "Promise executor functions should not be async.",
+                line: 1,
+                column: 17,
+                endLine: 1,
+                endColumn: 22
+            }]
+        }
+    ]
+});
diff --git a/tests/lib/rules/no-invalid-this.js b/tests/lib/rules/no-invalid-this.js
index a87a9aa44..f1d11030c 100644
--- a/tests/lib/rules/no-invalid-this.js
+++ b/tests/lib/rules/no-invalid-this.js
@@ -83,7 +83,7 @@ function extractPatterns(patterns, type) {
     }));
 
     // Flatten.
-    return Array.prototype.concat.apply([], patternsList);
+    return [].concat(...patternsList);
 }
 
 
diff --git a/tests/lib/rules/padding-line-between-statements.js b/tests/lib/rules/padding-line-between-statements.js
index 597c48835..3f72f8ff2 100644
--- a/tests/lib/rules/padding-line-between-statements.js
+++ b/tests/lib/rules/padding-line-between-statements.js
@@ -19,7 +19,7 @@ const RuleTester = require("../../../lib/testers/rule-tester");
 const MESSAGE_NEVER = "Unexpected blank line before this statement.";
 const MESSAGE_ALWAYS = "Expected blank line before this statement.";
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2017 } });
 
 ruleTester.run("padding-line-between-statements", rule, {
     valid: [
@@ -811,6 +811,13 @@ ruleTester.run("padding-line-between-statements", rule, {
                 { blankLine: "always", prev: "function", next: "*" }
             ]
         },
+        {
+            code: "async function foo(){}\n\nfoo()",
+            options: [
+                { blankLine: "never", prev: "*", next: "*" },
+                { blankLine: "always", prev: "function", next: "*" }
+            ]
+        },
 
         //----------------------------------------------------------------------
         // if
@@ -3317,6 +3324,15 @@ ruleTester.run("padding-line-between-statements", rule, {
             ],
             errors: [MESSAGE_ALWAYS]
         },
+        {
+            code: "async function foo(){}\nfoo()",
+            output: "async function foo(){}\n\nfoo()",
+            options: [
+                { blankLine: "never", prev: "*", next: "*" },
+                { blankLine: "always", prev: "function", next: "*" }
+            ],
+            errors: [MESSAGE_ALWAYS]
+        },
 
         //----------------------------------------------------------------------
         // if
diff --git a/tests/lib/rules/prefer-object-spread.js b/tests/lib/rules/prefer-object-spread.js
index bfe43e0f4..acac665e5 100644
--- a/tests/lib/rules/prefer-object-spread.js
+++ b/tests/lib/rules/prefer-object-spread.js
@@ -12,9 +12,6 @@ const RuleTester = require("../../../lib/testers/rule-tester");
 
 const parserOptions = {
     ecmaVersion: 2018,
-    ecmaFeatures: {
-        experimentalObjectRestSpread: true
-    },
     sourceType: "module"
 };
 
@@ -811,6 +808,32 @@ ruleTester.run("prefer-object-spread", rule, {
                     column: 29
                 }
             ]
+        },
+
+        // https://github.com/eslint/eslint/issues/10646
+        {
+            code: "Object.assign({ });",
+            output: "({});",
+            errors: [
+                {
+                    messageId: "useLiteralMessage",
+                    type: "CallExpression",
+                    line: 1,
+                    column: 1
+                }
+            ]
+        },
+        {
+            code: "Object.assign({\n});",
+            output: "({});",
+            errors: [
+                {
+                    messageId: "useLiteralMessage",
+                    type: "CallExpression",
+                    line: 1,
+                    column: 1
+                }
+            ]
         }
     ]
 });
diff --git a/tests/lib/rules/require-atomic-updates.js b/tests/lib/rules/require-atomic-updates.js
new file mode 100644
index 000000000..eabd52149
--- /dev/null
+++ b/tests/lib/rules/require-atomic-updates.js
@@ -0,0 +1,143 @@
+/**
+ * @fileoverview disallow assignments that can lead to race conditions due to usage of `await` or `yield`
+ * @author Teddy Katz
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const rule = require("../../../lib/rules/require-atomic-updates");
+const RuleTester = require("../../../lib/testers/rule-tester");
+
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } });
+
+const VARIABLE_ERROR = {
+    messageId: "nonAtomicUpdate",
+    data: { value: "foo" },
+    type: "AssignmentExpression"
+};
+
+const STATIC_PROPERTY_ERROR = {
+    messageId: "nonAtomicUpdate",
+    data: { value: "foo.bar" },
+    type: "AssignmentExpression"
+};
+
+const COMPUTED_PROPERTY_ERROR = {
+    messageId: "nonAtomicUpdate",
+    data: { value: "foo[bar].baz" },
+    type: "AssignmentExpression"
+};
+
+ruleTester.run("require-atomic-updates", rule, {
+
+    valid: [
+        "let foo; async function x() { foo += bar; }",
+        "let foo; async function x() { foo = foo + bar; }",
+        "let foo; async function x() { foo = await bar + foo; }",
+        "async function x() { let foo; foo += await bar; }",
+        "let foo; async function x() { foo = (await result)(foo); }",
+        "let foo; async function x() { foo = bar(await something, foo) }",
+        "function* x() { let foo; foo += yield bar; }",
+        "const foo = {}; async function x() { foo.bar = await baz; }",
+        "const foo = []; async function x() { foo[x] += 1;  }",
+        "let foo; function* x() { foo = bar + foo; }",
+        "async function x() { let foo; bar(() => baz += 1); foo += await amount; }",
+        "let foo; async function x() { foo = condition ? foo : await bar; }",
+        "async function x() { let foo; bar(() => { let foo; blah(foo); }); foo += await result; }"
+    ],
+
+    invalid: [
+        {
+            code: "let foo; async function x() { foo += await amount; }",
+            errors: [{ messageId: "nonAtomicUpdate", data: { value: "foo" } }]
+        },
+        {
+            code: "let foo; async function x() { while (condition) { foo += await amount; } }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo; async function x() { foo = foo + await amount; }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo; async function x() { foo = foo + (bar ? baz : await amount); }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo; async function x() { foo = foo + (bar ? await amount : baz); }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo; async function x() { foo = condition ? foo + await amount : somethingElse; }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo; async function x() { foo = (condition ? foo : await bar) + await bar; }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo; async function x() { foo += bar + await amount; }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "async function x() { let foo; bar(() => foo); foo += await amount; }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo; function* x() { foo += yield baz }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo; async function x() { foo = bar(foo, await something) }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "const foo = {}; async function x() { foo.bar += await baz }",
+            errors: [STATIC_PROPERTY_ERROR]
+        },
+        {
+            code: "const foo = []; async function x() { foo[bar].baz += await result;  }",
+            errors: [COMPUTED_PROPERTY_ERROR]
+        },
+        {
+            code: "let foo; async function* x() { foo = (yield foo) + await bar; }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo; async function x() { foo = foo + await result(foo); }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo; async function x() { foo = await result(foo, await somethingElse); }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "function* x() { let foo; yield async function y() { foo += await bar; } }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo; async function* x() { foo = await foo + (yield bar); }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo; async function x() { foo = bar + await foo; }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo = {}; async function x() { foo[bar].baz = await (foo.bar += await foo[bar].baz) }",
+            errors: [COMPUTED_PROPERTY_ERROR, STATIC_PROPERTY_ERROR]
+        },
+        {
+            code: "async function x() { foo += await bar; }",
+            errors: [VARIABLE_ERROR]
+        }
+    ]
+});
diff --git a/tests/lib/testers/rule-tester.js b/tests/lib/testers/rule-tester.js
index 4265ab1b9..4a8608906 100644
--- a/tests/lib/testers/rule-tester.js
+++ b/tests/lib/testers/rule-tester.js
@@ -44,12 +44,12 @@ const ruleTesterTestEmitter = new EventEmitter();
 
 RuleTester.describe = function(text, method) {
     ruleTesterTestEmitter.emit("describe", text, method);
-    return method.apply(this);
+    return method.call(this);
 };
 
 RuleTester.it = function(text, method) {
     ruleTesterTestEmitter.emit("it", text, method);
-    return method.apply(this);
+    return method.call(this);
 };
 
 //------------------------------------------------------------------------------
diff --git a/tests/lib/util/glob-util.js b/tests/lib/util/glob-util.js
index afa7d3f70..cbe60b567 100644
--- a/tests/lib/util/glob-util.js
+++ b/tests/lib/util/glob-util.js
@@ -12,7 +12,7 @@ const assert = require("chai").assert,
     path = require("path"),
     os = require("os"),
     sh = require("shelljs"),
-    globUtil = require("../../../lib/util/glob-util"),
+    globUtil = require("../../../lib/util/glob-utils"),
     fs = require("fs");
 
 //------------------------------------------------------------------------------
diff --git a/tests/lib/util/npm-util.js b/tests/lib/util/npm-util.js
index b0237c464..01dc1f025 100644
--- a/tests/lib/util/npm-util.js
+++ b/tests/lib/util/npm-util.js
@@ -11,7 +11,7 @@
 const assert = require("chai").assert,
     spawn = require("cross-spawn"),
     sinon = require("sinon"),
-    npmUtil = require("../../../lib/util/npm-util"),
+    npmUtil = require("../../../lib/util/npm-utils"),
     log = require("../../../lib/util/logging"),
     mockFs = require("mock-fs");
 
diff --git a/tests/lib/util/path-util.js b/tests/lib/util/path-util.js
index bfb6efa5c..c5022cd20 100644
--- a/tests/lib/util/path-util.js
+++ b/tests/lib/util/path-util.js
@@ -11,7 +11,7 @@
 const path = require("path"),
     assert = require("chai").assert,
     sinon = require("sinon"),
-    pathUtil = require("../../../lib/util/path-util");
+    pathUtil = require("../../../lib/util/path-utils");
 
 //------------------------------------------------------------------------------
 // Tests
diff --git a/tests/lib/util/source-code-util.js b/tests/lib/util/source-code-util.js
index 1a406e5d3..6b9ca7d18 100644
--- a/tests/lib/util/source-code-util.js
+++ b/tests/lib/util/source-code-util.js
@@ -15,7 +15,7 @@ const path = require("path"),
     assert = require("chai").assert,
     sinon = require("sinon"),
     sh = require("shelljs"),
-    globUtil = require("../../../lib/util/glob-util"),
+    globUtil = require("../../../lib/util/glob-utils"),
     SourceCode = require("../../../lib/util/source-code");
 
 const proxyquire = require("proxyquire").noCallThru().noPreserveCache();
@@ -63,7 +63,7 @@ describe("SourceCodeUtil", () => {
     });
 
     beforeEach(() => {
-        getSourceCodeOfFiles = proxyquire("../../../lib/util/source-code-util", requireStubs).getSourceCodeOfFiles;
+        getSourceCodeOfFiles = proxyquire("../../../lib/util/source-code-utils", requireStubs).getSourceCodeOfFiles;
     });
 
     afterEach(() => {
diff --git a/tests/lib/util/source-code.js b/tests/lib/util/source-code.js
index 9d7adac04..665176522 100644
--- a/tests/lib/util/source-code.js
+++ b/tests/lib/util/source-code.js
@@ -16,7 +16,7 @@ const fs = require("fs"),
     leche = require("leche"),
     Linter = require("../../../lib/linter"),
     SourceCode = require("../../../lib/util/source-code"),
-    astUtils = require("../../../lib/ast-utils");
+    astUtils = require("../../../lib/util/ast-utils");
 
 //------------------------------------------------------------------------------
 // Helpers