Repository: eslint/eslint (js)

Issue:
🎉 Asynchronous Iteration arrived at Stage 4.
> https://github.com/tc39/proposals/commit/a41dccc91675f41c65dee354c18c19946d6f2f45

- [x] Update Espree to support the syntax in `ecmaVersion:2018`.
    - `for await (const x of xs);` (`ForOfStatement#await`)
    - `async function* f() {}`
    - `f = async function* () {}`
    - `obj = { async* f() {} }`
    - `class A { async* f() {} }`
- [x] Update core rules to support the syntax.

Pull request:
Fix: support Async iteration (fixes #9891)

**What is the purpose of this pull request? (put an "X" next to item)**

[X] Bug fix

Fixes #9891.

**What changes did you make? (Give an overview)**

This PR fixes false positive and wrongly autofix about async iteration.

- b80d41a fixed `keyword-spacing` rule to check the spacing between `await` and `(` in `for-await-of` loops.
- 8c0e801 fixed `no-await-in-loop` rule to not report await expressions on `for-await-of` loops and to report `for-await-of` loops as same as await expressions.
- a64e8fe fixed `object-shorthand` rule to fix async generators correctly.
- 1c977b4 fixed `require-await` rule to handle `for-await-of` loops as same as await expressions.
- e30dd5f added tests to `generator-star-spacing` rule for async generators.
- 4a2a6a2 added tests to `no-useless-computed-key` rule for async generators.

**Is there anything you'd like reviewers to focus on?**

Are there other rules which need a fix?

I have searched by some node types in rules: `ForOfStatement`, `AwaitExpression`, `Property`, and `MethodDefinition`.

@not-an-aardvark I'm not sure about `semi` rule (https://github.com/eslint/eslint/issues/9891#issuecomment-360606541). The `semi` rule addresses at the end of statements, so it doesn't look to be related to the `await` keyword in the middle of statements.

Patch:
diff --git a/docs/developer-guide/nodejs-api.md b/docs/developer-guide/nodejs-api.md
index c774666e0..4988f2696 100644
--- a/docs/developer-guide/nodejs-api.md
+++ b/docs/developer-guide/nodejs-api.md
@@ -496,7 +496,7 @@ If the operation ends with a parsing error, you will get a single message for th
             warningCount: 0,
             fixableErrorCount: 0,
             fixableWarningCount: 0,
-            source: "fucntion foo() {}"
+            source: "function foo() {}"
         }
     ],
     errorCount: 1,
diff --git a/docs/rules/key-spacing.md b/docs/rules/key-spacing.md
index 2c4afae8c..6e2b90541 100644
--- a/docs/rules/key-spacing.md
+++ b/docs/rules/key-spacing.md
@@ -10,14 +10,18 @@ This rule enforces consistent spacing between keys and values in object literal
 
 This rule has an object option:
 
-* `"beforeColon": false` (default) disallows spaces between the key and the colon in object literals.
-* `"beforeColon": true` requires at least one space between the key and the colon in object literals.
-* `"afterColon": true` (default) requires at least one space between the colon and the value in object literals.
-* `"afterColon": false` disallows spaces between the colon and the value in object literals.
-* `"mode": "strict"` (default) enforces exactly one space before or after colons in object literals.
-* `"mode": "minimum"` enforces one or more spaces before or after colons in object literals.
-* `"align": "value"` enforces horizontal alignment of values in object literals.
-* `"align": "colon"` enforces horizontal alignment of both colons and values in object literals.
+* `"beforeColon": false (default) | true`
+    * `false`: disallows spaces between the key and the colon in object literals.
+    * `true`: requires at least one space between the key and the colon in object literals.
+* `"afterColon": true (default) | false`
+    * `true`: requires at least one space between the colon and the value in object literals.
+    * `false`: disallows spaces between the colon and the value in object literals.
+* `"mode": "strict" (default) | "miniumum"`
+    * `"strict"`: enforces exactly one space before or after colons in object literals.
+    * `"minimum"`: enforces one or more spaces before or after colons in object literals.
+* `"align": "value" | "colon"`
+    * `"value"`: enforces horizontal alignment of values in object literals.
+    * `"colon"` enforces horizontal alignment of both colons and values in object literals.
 * `"align"` with an object value allows for fine-grained spacing when values are being aligned in object literals.
 * `"singleLine"` specifies a spacing style for single-line object literals.
 * `"multiLine"` specifies a spacing style for multi-line object literals.
diff --git a/docs/rules/no-mixed-operators.md b/docs/rules/no-mixed-operators.md
index 1746c5ed6..3c0bb2fab 100644
--- a/docs/rules/no-mixed-operators.md
+++ b/docs/rules/no-mixed-operators.md
@@ -138,10 +138,19 @@ Examples of **incorrect** code for this rule with `{"allowSamePrecedence": false
 var foo = a + b - c;
 ```
 
+Examples of **correct** code for this rule with `{"allowSamePrecedence": false}` option:
+
+```js
+/*eslint no-mixed-operators: ["error", {"allowSamePrecedence": false}]*/
+
+// + and - have the same precedence.
+var foo = (a + b) - c;
+```
+
 ## When Not To Use It
 
 If you don't want to be notified about mixed operators, then it's safe to disable this rule.
 
 ## Related Rules
 
-* [no-extra-parens](no-extra-parens.md)
\ No newline at end of file
+* [no-extra-parens](no-extra-parens.md)
diff --git a/docs/rules/valid-typeof.md b/docs/rules/valid-typeof.md
index 2ce02b490..60539eecb 100644
--- a/docs/rules/valid-typeof.md
+++ b/docs/rules/valid-typeof.md
@@ -20,7 +20,7 @@ Examples of **incorrect** code for this rule:
 typeof foo === "strnig"
 typeof foo == "undefimed"
 typeof bar != "nunber"
-typeof bar !== "fucntion"
+typeof bar !== "function"
 ```
 
 Examples of **correct** code for this rule:
diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md
index 098fcdf43..5d4f93d55 100644
--- a/docs/user-guide/configuring.md
+++ b/docs/user-guide/configuring.md
@@ -394,13 +394,18 @@ You can also disable or enable specific rules for an entire file:
 alert('foo');
 ```
 
-To disable all rules on a specific line, use a line comment in one of the following formats:
+To disable all rules on a specific line, use a line or block comment in one of the following formats:
 
 ```js
 alert('foo'); // eslint-disable-line
 
 // eslint-disable-next-line
 alert('foo');
+
+/* eslint-disable-next-line */
+alert('foo');
+
+alert('foo'); /* eslint-disable-line */
 ```
 
 To disable a specific rule on a specific line:
@@ -410,6 +415,11 @@ alert('foo'); // eslint-disable-line no-alert
 
 // eslint-disable-next-line no-alert
 alert('foo');
+
+alert('foo'); /* eslint-disable-line no-alert */
+
+/* eslint-disable-next-line no-alert */
+alert('foo');
 ```
 
 To disable multiple rules on a specific line:
@@ -419,12 +429,18 @@ alert('foo'); // eslint-disable-line no-alert, quotes, semi
 
 // eslint-disable-next-line no-alert, quotes, semi
 alert('foo');
+
+alert('foo'); /* eslint-disable-line no-alert, quotes, semi */
+
+/* eslint-disable-next-line no-alert, quotes, semi */
+alert('foo');
 ```
 
 All of the above methods also work for plugin rules. For example, to disable `eslint-plugin-example`'s `rule-name` rule, combine the plugin's name (`example`) and the rule's name (`rule-name`) into `example/rule-name`:
 
 ```js
 foo(); // eslint-disable-line example/rule-name
+foo(); /* eslint-disable-line example/rule-name */
 ```
 
 **Note:** Comments that disable warnings for a portion of a file tell ESLint not to report rule violations for the disabled code. ESLint still parses the entire file, however, so disabled code still needs to be syntactically valid JavaScript.
diff --git a/lib/linter.js b/lib/linter.js
index d71a79a7c..8e95c3a71 100755
--- a/lib/linter.js
+++ b/lib/linter.js
@@ -307,7 +307,6 @@ function modifyConfigsFromComments(filename, ast, config, ruleMapper) {
 
         if (match) {
             value = value.slice(match.index + match[1].length);
-
             if (comment.type === "Block") {
                 switch (match[1]) {
                     case "exported":
@@ -323,6 +322,18 @@ function modifyConfigsFromComments(filename, ast, config, ruleMapper) {
                         [].push.apply(disableDirectives, createDisableDirectives("disable", comment.loc.start, value));
                         break;
 
+                    case "eslint-disable-line":
+                        if (comment.loc.start.line === comment.loc.end.line) {
+                            [].push.apply(disableDirectives, createDisableDirectives("disable-line", comment.loc.start, value));
+                        }
+                        break;
+
+                    case "eslint-disable-next-line":
+                        if (comment.loc.start.line === comment.loc.end.line) {
+                            [].push.apply(disableDirectives, createDisableDirectives("disable-next-line", comment.loc.start, value));
+                        }
+                        break;
+
                     case "eslint-enable":
                         [].push.apply(disableDirectives, createDisableDirectives("enable", comment.loc.start, value));
                         break;
diff --git a/lib/rules/keyword-spacing.js b/lib/rules/keyword-spacing.js
index d0dd64025..89b973325 100644
--- a/lib/rules/keyword-spacing.js
+++ b/lib/rules/keyword-spacing.js
@@ -436,7 +436,12 @@ module.exports = {
          * @returns {void}
          */
         function checkSpacingForForOfStatement(node) {
-            checkSpacingAroundFirstToken(node);
+            if (node.await) {
+                checkSpacingBefore(sourceCode.getFirstToken(node, 0));
+                checkSpacingAfter(sourceCode.getFirstToken(node, 1));
+            } else {
+                checkSpacingAroundFirstToken(node);
+            }
             checkSpacingAround(sourceCode.getTokenBefore(node.right, astUtils.isNotOpeningParenToken));
         }
 
diff --git a/lib/rules/no-await-in-loop.js b/lib/rules/no-await-in-loop.js
index 7a9427e6a..d0a2a5893 100644
--- a/lib/rules/no-await-in-loop.js
+++ b/lib/rules/no-await-in-loop.js
@@ -4,24 +4,54 @@
  */
 "use strict";
 
-// Node types which are considered loops.
-const loopTypes = new Set([
-    "ForStatement",
-    "ForOfStatement",
-    "ForInStatement",
-    "WhileStatement",
-    "DoWhileStatement"
-]);
+/**
+ * Check whether it should stop traversing ancestors at the given node.
+ * @param {ASTNode} node A node to check.
+ * @returns {boolean} `true` if it should stop traversing.
+ */
+function isBoundary(node) {
+    const t = node.type;
+
+    return (
+        t === "FunctionDeclaration" ||
+        t === "FunctionExpression" ||
+        t === "ArrowFunctionExpression" ||
 
-/*
- * Node types at which we should stop looking for loops. For example, it is fine to declare an async
- * function within a loop, and use await inside of that.
+        /*
+         * Don't report the await expressions on for-await-of loop since it's
+         * asynchronous iteration intentionally.
+         */
+        (t === "ForOfStatement" && node.await === true)
+    );
+}
+
+/**
+ * Check whether the given node is in loop.
+ * @param {ASTNode} node A node to check.
+ * @param {ASTNode} parent A parent node to check.
+ * @returns {boolean} `true` if the node is in loop.
  */
-const boundaryTypes = new Set([
-    "FunctionDeclaration",
-    "FunctionExpression",
-    "ArrowFunctionExpression"
-]);
+function isLooped(node, parent) {
+    switch (parent.type) {
+        case "ForStatement":
+            return (
+                node === parent.test ||
+                node === parent.update ||
+                node === parent.body
+            );
+
+        case "ForOfStatement":
+        case "ForInStatement":
+            return node === parent.body;
+
+        case "WhileStatement":
+        case "DoWhileStatement":
+            return node === parent.test || node === parent.body;
+
+        default:
+            return false;
+    }
+}
 
 module.exports = {
     meta: {
@@ -37,51 +67,36 @@ module.exports = {
         }
     },
     create(context) {
-        return {
-            AwaitExpression(node) {
-                const ancestors = context.getAncestors();
-
-                // Reverse so that we can traverse from the deepest node upwards.
-                ancestors.reverse();
-
-                /*
-                 * Create a set of all the ancestors plus this node so that we can check
-                 * if this use of await appears in the body of the loop as opposed to
-                 * the right-hand side of a for...of, for example.
-                 */
-                const ancestorSet = new Set(ancestors).add(node);
-
-                for (let i = 0; i < ancestors.length; i++) {
-                    const ancestor = ancestors[i];
 
-                    if (boundaryTypes.has(ancestor.type)) {
+        /**
+         * Validate an await expression.
+         * @param {ASTNode} awaitNode An AwaitExpression or ForOfStatement node to validate.
+         * @returns {void}
+         */
+        function validate(awaitNode) {
+            if (awaitNode.type === "ForOfStatement" && !awaitNode.await) {
+                return;
+            }
 
-                        /*
-                         * Short-circuit out if we encounter a boundary type. Loops above
-                         * this do not matter.
-                         */
-                        return;
-                    }
-                    if (loopTypes.has(ancestor.type)) {
+            let node = awaitNode;
+            let parent = node.parent;
 
-                        /*
-                         * Only report if we are actually in the body or another part that gets executed on
-                         * every iteration.
-                         */
-                        if (
-                            ancestorSet.has(ancestor.body) ||
-                            ancestorSet.has(ancestor.test) ||
-                            ancestorSet.has(ancestor.update)
-                        ) {
-                            context.report({
-                                node,
-                                messageId: "unexpectedAwait"
-                            });
-                            return;
-                        }
-                    }
+            while (parent && !isBoundary(parent)) {
+                if (isLooped(node, parent)) {
+                    context.report({
+                        node,
+                        messageId: "unexpectedAwait"
+                    });
+                    return;
                 }
+                node = parent;
+                parent = parent.parent;
             }
+        }
+
+        return {
+            AwaitExpression: validate,
+            ForOfStatement: validate
         };
     }
 };
diff --git a/lib/rules/object-shorthand.js b/lib/rules/object-shorthand.js
index c6c0b1044..aaaf72d26 100644
--- a/lib/rules/object-shorthand.js
+++ b/lib/rules/object-shorthand.js
@@ -233,10 +233,11 @@ module.exports = {
             const keyText = sourceCode.text.slice(firstKeyToken.range[0], lastKeyToken.range[1]);
             let keyPrefix = "";
 
+            if (node.value.async) {
+                keyPrefix += "async ";
+            }
             if (node.value.generator) {
-                keyPrefix = "*";
-            } else if (node.value.async) {
-                keyPrefix = "async ";
+                keyPrefix += "*";
             }
 
             if (node.value.type === "FunctionExpression") {
@@ -273,10 +274,11 @@ module.exports = {
             const keyText = sourceCode.text.slice(firstKeyToken.range[0], lastKeyToken.range[1]);
             let functionHeader = "function";
 
+            if (node.value.async) {
+                functionHeader = `async ${functionHeader}`;
+            }
             if (node.value.generator) {
-                functionHeader = "function*";
-            } else if (node.value.async) {
-                functionHeader = "async function";
+                functionHeader = `${functionHeader}*`;
             }
 
             return fixer.replaceTextRange([node.range[0], lastKeyToken.range[1]], `${keyText}: ${functionHeader}`);
diff --git a/lib/rules/require-await.js b/lib/rules/require-await.js
index 6adc84ae1..5517cf867 100644
--- a/lib/rules/require-await.js
+++ b/lib/rules/require-await.js
@@ -90,6 +90,11 @@ module.exports = {
 
             AwaitExpression() {
                 scopeInfo.hasAwait = true;
+            },
+            ForOfStatement(node) {
+                if (node.await) {
+                    scopeInfo.hasAwait = true;
+                }
             }
         };
     }


Test patch:
diff --git a/tests/lib/linter.js b/tests/lib/linter.js
index fc4298d56..5f42ffd9c 100644
--- a/tests/lib/linter.js
+++ b/tests/lib/linter.js
@@ -1889,7 +1889,7 @@ describe("Linter", () => {
         });
     });
 
-    describe("when evaluating code with comments to ignore reporting on of specific rules on a specific line", () => {
+    describe("when evaluating code with comments to ignore reporting on specific rules on a specific line", () => {
 
         describe("eslint-disable-line", () => {
             it("should report a violation", () => {
@@ -1931,14 +1931,15 @@ describe("Linter", () => {
                 assert.strictEqual(messages[0].ruleId, "no-alert");
             });
 
-            it("should report a violation", () => {
+            it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => {
                 const code = [
-                    "alert('test'); // eslint-disable-line no-alert",
-                    "alert('test'); /*eslint-disable-line no-alert*/" // here
+                    "/* eslint-disable-line",
+                    "*",
+                    "*/ console.log('test');" // here
                 ].join("\n");
                 const config = {
                     rules: {
-                        "no-alert": 1
+                        "no-console": 1
                     }
                 };
 
@@ -1946,7 +1947,44 @@ describe("Linter", () => {
 
                 assert.strictEqual(messages.length, 1);
 
+                assert.strictEqual(messages[0].ruleId, "no-console");
+            });
+
+            it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => {
+                const code = [
+                    "alert('test'); /* eslint-disable-line ",
+                    "no-alert */"
+                ].join("\n");
+                const config = {
+                    rules: {
+                        "no-alert": 1,
+                        quotes: [1, "double"],
+                        semi: [1, "always"]
+                    }
+                };
+
+                const messages = linter.verify(code, config, filename);
+
+                assert.strictEqual(messages.length, 2);
+
                 assert.strictEqual(messages[0].ruleId, "no-alert");
+                assert.strictEqual(messages[1].ruleId, "quotes");
+            });
+
+            it("should not report a violation for eslint-disable-line in block comment", () => {
+                const code = [
+                    "alert('test'); // eslint-disable-line no-alert",
+                    "alert('test'); /*eslint-disable-line no-alert*/"
+                ].join("\n");
+                const config = {
+                    rules: {
+                        "no-alert": 1
+                    }
+                };
+
+                const messages = linter.verify(code, config, filename);
+
+                assert.strictEqual(messages.length, 0);
             });
 
             it("should not report a violation", () => {
@@ -1984,6 +2022,40 @@ describe("Linter", () => {
 
                 assert.strictEqual(messages.length, 0);
             });
+
+            it("should not report a violation", () => {
+                const code = [
+                    "alert('test') /* eslint-disable-line no-alert, quotes, semi */",
+                    "console.log('test'); /* eslint-disable-line */"
+                ].join("\n");
+                const config = {
+                    rules: {
+                        "no-alert": 1,
+                        quotes: [1, "double"],
+                        semi: [1, "always"],
+                        "no-console": 1
+                    }
+                };
+
+                const messages = linter.verify(code, config, filename);
+
+                assert.strictEqual(messages.length, 0);
+            });
+
+            it("should ignore violations of multiple rules when specified in mixed comments", () => {
+                const code = [
+                    " alert(\"test\"); /* eslint-disable-line no-alert */ // eslint-disable-line quotes"
+                ].join("\n");
+                const config = {
+                    rules: {
+                        "no-alert": 1,
+                        quotes: [1, "single"]
+                    }
+                };
+                const messages = linter.verify(code, config, filename);
+
+                assert.strictEqual(messages.length, 0);
+            });
         });
 
         describe("eslint-disable-next-line", () => {
@@ -2005,6 +2077,54 @@ describe("Linter", () => {
                 assert.strictEqual(messages[0].ruleId, "no-console");
             });
 
+            it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
+                const code = [
+                    "/* eslint-disable-next-line no-alert */",
+                    "alert('test');",
+                    "console.log('test');"
+                ].join("\n");
+                const config = {
+                    rules: {
+                        "no-alert": 1,
+                        "no-console": 1
+                    }
+                };
+                const messages = linter.verify(code, config, filename);
+
+                assert.strictEqual(messages.length, 1);
+                assert.strictEqual(messages[0].ruleId, "no-console");
+            });
+            it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
+                const code = [
+                    "/* eslint-disable-next-line no-alert */",
+                    "alert('test');"
+                ].join("\n");
+                const config = {
+                    rules: {
+                        "no-alert": 1
+                    }
+                };
+                const messages = linter.verify(code, config, filename);
+
+                assert.strictEqual(messages.length, 0);
+            });
+
+            it("should not ignore violation if block comment is not on a single line", () => {
+                const code = [
+                    "/* eslint-disable-next-line",
+                    "no-alert */alert('test');"
+                ].join("\n");
+                const config = {
+                    rules: {
+                        "no-alert": 1
+                    }
+                };
+                const messages = linter.verify(code, config, filename);
+
+                assert.strictEqual(messages.length, 1);
+                assert.strictEqual(messages[0].ruleId, "no-alert");
+            });
+
             it("should ignore violations only of specified rule", () => {
                 const code = [
                     "// eslint-disable-next-line no-console",
@@ -2043,6 +2163,22 @@ describe("Linter", () => {
                 assert.strictEqual(messages[0].ruleId, "no-console");
             });
 
+            it("should ignore violations of multiple rules when specified in mixed comments", () => {
+                const code = [
+                    "/* eslint-disable-next-line no-alert */ // eslint-disable-next-line quotes",
+                    "alert(\"test\");"
+                ].join("\n");
+                const config = {
+                    rules: {
+                        "no-alert": 1,
+                        quotes: [1, "single"]
+                    }
+                };
+                const messages = linter.verify(code, config, filename);
+
+                assert.strictEqual(messages.length, 0);
+            });
+
             it("should ignore violations of only the specified rule on next line", () => {
                 const code = [
                     "// eslint-disable-next-line quotes",
@@ -2103,7 +2239,7 @@ describe("Linter", () => {
                 assert.strictEqual(messages[0].ruleId, "no-console");
             });
 
-            it("should not ignore violations if comment is in block quotes", () => {
+            it("should ignore violations if eslint-disable-next-line is a block comment", () => {
                 const code = [
                     "alert('test');",
                     "/* eslint-disable-next-line no-alert */",
@@ -2118,10 +2254,30 @@ describe("Linter", () => {
                 };
                 const messages = linter.verify(code, config, filename);
 
-                assert.strictEqual(messages.length, 3);
+                assert.strictEqual(messages.length, 2);
                 assert.strictEqual(messages[0].ruleId, "no-alert");
-                assert.strictEqual(messages[1].ruleId, "no-alert");
-                assert.strictEqual(messages[2].ruleId, "no-console");
+                assert.strictEqual(messages[1].ruleId, "no-console");
+            });
+
+            it("should report a violation", () => {
+                const code = [
+                    "/* eslint-disable-next-line",
+                    "*",
+                    "*/",
+                    "console.log('test');" // here
+                ].join("\n");
+                const config = {
+                    rules: {
+                        "no-alert": 1,
+                        "no-console": 1
+                    }
+                };
+
+                const messages = linter.verify(code, config, filename);
+
+                assert.strictEqual(messages.length, 1);
+
+                assert.strictEqual(messages[0].ruleId, "no-console");
             });
 
             it("should not ignore violations if comment is of the type Shebang", () => {
diff --git a/tests/lib/rules/generator-star-spacing.js b/tests/lib/rules/generator-star-spacing.js
index 5108091ff..8d378cb5b 100644
--- a/tests/lib/rules/generator-star-spacing.js
+++ b/tests/lib/rules/generator-star-spacing.js
@@ -16,7 +16,7 @@ const rule = require("../../../lib/rules/generator-star-spacing"),
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } });
 
 ruleTester.run("generator-star-spacing", rule, {
 
@@ -1336,6 +1336,80 @@ ruleTester.run("generator-star-spacing", rule, {
                 message: "Missing space before *.",
                 type: "Punctuator"
             }]
+        },
+
+        // async generators
+        {
+            code: "({ async * foo(){} })",
+            output: "({ async*foo(){} })",
+            options: [{ before: false, after: false }],
+            errors: [{
+                message: "Unexpected space before *.",
+                type: "Punctuator"
+            }, {
+                message: "Unexpected space after *.",
+                type: "Punctuator"
+            }]
+        },
+        {
+            code: "({ async*foo(){} })",
+            output: "({ async * foo(){} })",
+            options: [{ before: true, after: true }],
+            errors: [{
+                message: "Missing space before *.",
+                type: "Punctuator"
+            }, {
+                message: "Missing space after *.",
+                type: "Punctuator"
+            }]
+        },
+        {
+            code: "class Foo { async * foo(){} }",
+            output: "class Foo { async*foo(){} }",
+            options: [{ before: false, after: false }],
+            errors: [{
+                message: "Unexpected space before *.",
+                type: "Punctuator"
+            }, {
+                message: "Unexpected space after *.",
+                type: "Punctuator"
+            }]
+        },
+        {
+            code: "class Foo { async*foo(){} }",
+            output: "class Foo { async * foo(){} }",
+            options: [{ before: true, after: true }],
+            errors: [{
+                message: "Missing space before *.",
+                type: "Punctuator"
+            }, {
+                message: "Missing space after *.",
+                type: "Punctuator"
+            }]
+        },
+        {
+            code: "class Foo { static async * foo(){} }",
+            output: "class Foo { static async*foo(){} }",
+            options: [{ before: false, after: false }],
+            errors: [{
+                message: "Unexpected space before *.",
+                type: "Punctuator"
+            }, {
+                message: "Unexpected space after *.",
+                type: "Punctuator"
+            }]
+        },
+        {
+            code: "class Foo { static async*foo(){} }",
+            output: "class Foo { static async * foo(){} }",
+            options: [{ before: true, after: true }],
+            errors: [{
+                message: "Missing space before *.",
+                type: "Punctuator"
+            }, {
+                message: "Missing space after *.",
+                type: "Punctuator"
+            }]
         }
 
     ]
diff --git a/tests/lib/rules/keyword-spacing.js b/tests/lib/rules/keyword-spacing.js
index d97f819fb..2ab9b5d35 100644
--- a/tests/lib/rules/keyword-spacing.js
+++ b/tests/lib/rules/keyword-spacing.js
@@ -180,6 +180,10 @@ ruleTester.run("keyword-spacing", rule, {
         { code: "a[ async function foo() {}]", options: [NEITHER], parserOptions: { ecmaVersion: 8 } },
         { code: "({[ async function foo() {}]: 0})", options: [NEITHER], parserOptions: { ecmaVersion: 8 } },
 
+        // not conflict with `generator-star-spacing`
+        { code: "({ async* foo() {} })", parserOptions: { ecmaVersion: 2018 } },
+        { code: "({ async *foo() {} })", options: [NEITHER], parserOptions: { ecmaVersion: 2018 } },
+
         // not conflict with `key-spacing`
         { code: "({a:async function foo() {} })", parserOptions: { ecmaVersion: 8 } },
         { code: "({a: async function foo() {} })", options: [NEITHER], parserOptions: { ecmaVersion: 8 } },
@@ -1530,6 +1534,34 @@ ruleTester.run("keyword-spacing", rule, {
             errors: unexpectedBefore("await")
         },
 
+        {
+            code: "async function wrap() { for await(x of xs); }",
+            output: "async function wrap() { for await (x of xs); }",
+            parserOptions: { ecmaVersion: 2018 },
+            errors: expectedAfter("await")
+        },
+        {
+            code: "async function wrap() { for await (x of xs); }",
+            output: "async function wrap() { for await(x of xs); }",
+            options: [NEITHER],
+            parserOptions: { ecmaVersion: 2018 },
+            errors: unexpectedAfter("await")
+        },
+        {
+            code: "async function wrap() { for await(x of xs); }",
+            output: "async function wrap() { for await (x of xs); }",
+            options: [override("await", BOTH)],
+            parserOptions: { ecmaVersion: 2018 },
+            errors: expectedAfter("await")
+        },
+        {
+            code: "async function wrap() { for await (x of xs); }",
+            output: "async function wrap() { for await(x of xs); }",
+            options: [override("await", NEITHER)],
+            parserOptions: { ecmaVersion: 2018 },
+            errors: unexpectedAfter("await")
+        },
+
         //----------------------------------------------------------------------
         // break
         //----------------------------------------------------------------------
diff --git a/tests/lib/rules/no-await-in-loop.js b/tests/lib/rules/no-await-in-loop.js
index bb74db1c0..057223879 100644
--- a/tests/lib/rules/no-await-in-loop.js
+++ b/tests/lib/rules/no-await-in-loop.js
@@ -10,13 +10,14 @@ const rule = require("../../../lib/rules/no-await-in-loop"),
 
 const error = { messageId: "unexpectedAwait" };
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: "2017" } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } });
 
 ruleTester.run("no-await-in-loop", rule, {
     valid: [
         "async function foo() { await bar; }",
         "async function foo() { for (var bar in await baz) { } }",
         "async function foo() { for (var bar of await baz) { } }",
+        "async function foo() { for await (var bar of await baz) { } }",
         "async function foo() { for (var bar = await baz in qux) {} }",
 
         // While loops
@@ -35,14 +36,17 @@ ruleTester.run("no-await-in-loop", rule, {
         "async function foo() { while (true) { var y = async () => { await foo; } } }",
 
         // Blocked by a class method
-        "async function foo() { while (true) { class Foo { async foo() { await bar; } } } }"
+        "async function foo() { while (true) { class Foo { async foo() { await bar; } } } }",
 
+        // Asynchronous iteration intentionally
+        "async function foo() { for await (var x of xs) { await f(x) } }"
     ],
     invalid: [
 
         // While loops
         { code: "async function foo() { while (baz) { await bar; } }", errors: [error] },
         { code: "async function foo() { while (await foo()) {  } }", errors: [error] },
+        { code: "async function foo() { while (baz) { for await (x of xs); } }", errors: [error] },
 
         // For of loops
         { code: "async function foo() { for (var bar of baz) { await bar; } }", errors: [error] },
@@ -64,6 +68,9 @@ ruleTester.run("no-await-in-loop", rule, {
         { code: "async function foo() { while (true) { if (bar) { foo(await bar); } } }", errors: [error] },
 
         // Deep in a loop condition
-        { code: "async function foo() { while (xyz || 5 > await x) {  } }", errors: [error] }
+        { code: "async function foo() { while (xyz || 5 > await x) {  } }", errors: [error] },
+
+        // In a nested loop of for-await-of
+        { code: "async function foo() { for await (var x of xs) { while (1) await f(x) } }", errors: [error] }
     ]
 });
diff --git a/tests/lib/rules/no-useless-computed-key.js b/tests/lib/rules/no-useless-computed-key.js
index c825579fc..ed9f3daa0 100644
--- a/tests/lib/rules/no-useless-computed-key.js
+++ b/tests/lib/rules/no-useless-computed-key.js
@@ -16,7 +16,7 @@ const rule = require("../../../lib/rules/no-useless-computed-key"),
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } });
 
 ruleTester.run("no-useless-computed-key", rule, {
     valid: [
@@ -162,6 +162,12 @@ ruleTester.run("no-useless-computed-key", rule, {
             errors: [{
                 message: "Unnecessarily computed property [2] found.", type: "Property"
             }]
+        }, {
+            code: "({ async*[2]() {} })",
+            output: "({ async*2() {} })",
+            errors: [{
+                message: "Unnecessarily computed property [2] found.", type: "Property"
+            }]
         }
     ]
 });
diff --git a/tests/lib/rules/object-shorthand.js b/tests/lib/rules/object-shorthand.js
index 0606d0ed8..e153f9fcb 100644
--- a/tests/lib/rules/object-shorthand.js
+++ b/tests/lib/rules/object-shorthand.js
@@ -24,7 +24,7 @@ const LONGFORM_METHOD_STRING_LITERAL_ERROR = { message: "Expected longform metho
 const ALL_SHORTHAND_ERROR = { message: "Expected shorthand for all properties.", type: "ObjectExpression" };
 const MIXED_SHORTHAND_ERROR = { message: "Unexpected mix of shorthand and non-shorthand properties.", type: "ObjectExpression" };
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } });
 
 ruleTester.run("object-shorthand", rule, {
     valid: [
@@ -979,6 +979,20 @@ ruleTester.run("object-shorthand", rule, {
             output: "({ a() { return foo; } })",
             options: ["always", { avoidExplicitReturnArrows: true }],
             errors: [METHOD_ERROR]
+        },
+
+        // async generators
+        {
+            code: "({ a: async function*() {} })",
+            output: "({ async *a() {} })",
+            options: ["always"],
+            errors: [METHOD_ERROR]
+        },
+        {
+            code: "({ async* a() {} })",
+            output: "({ a: async function*() {} })",
+            options: ["never"],
+            errors: [LONGFORM_METHOD_ERROR]
         }
     ]
 });
diff --git a/tests/lib/rules/require-await.js b/tests/lib/rules/require-await.js
index 6248ac8a5..662dc72cb 100644
--- a/tests/lib/rules/require-await.js
+++ b/tests/lib/rules/require-await.js
@@ -18,7 +18,7 @@ const rule = require("../../../lib/rules/require-await"),
 
 const ruleTester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2017
+        ecmaVersion: 2018
     }
 });
 
@@ -38,7 +38,10 @@ ruleTester.run("require-await", rule, {
         "async () => {}",
 
         // normal functions are ok.
-        "function foo() { doSomething() }"
+        "function foo() { doSomething() }",
+
+        // for-await-of
+        "async function foo() { for await (x of xs); }"
     ],
     invalid: [
         {