Repository: eslint/eslint
Pull request #12571 (2019-11-15 18:11:18)

Fix: require-await crash on global await
<!--
    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] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))

<!--
    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)**

`@typescript-eslint/parser` allows global `awaits`.
This currently rule assumes that is not allowed, because espree throws a parsing error on it.

This PR just stops the rule from crashing on global await statements.

Related: https://github.com/typescript-eslint/typescript-eslint/issues/1077

bradzacher: I was being lazy. Thanks for not letting me 😄 
Fixed
kaicataldo: For posterity, top-level `await` is stage 3 at this time and it is ESLint’s policy you fix rules that crash on stage 3 features.

Patch:
diff --git a/lib/rules/require-await.js b/lib/rules/require-await.js
index 0aa6fce7e..22c111b6d 100644
--- a/lib/rules/require-await.js
+++ b/lib/rules/require-await.js
@@ -89,9 +89,17 @@ module.exports = {
             "ArrowFunctionExpression:exit": exitFunction,
 
             AwaitExpression() {
+                if (!scopeInfo) {
+                    return;
+                }
+
                 scopeInfo.hasAwait = true;
             },
             ForOfStatement(node) {
+                if (!scopeInfo) {
+                    return;
+                }
+
                 if (node.await) {
                     scopeInfo.hasAwait = true;
                 }


Test patch:
diff --git a/tests/fixtures/parsers/typescript-parsers/global-await.js b/tests/fixtures/parsers/typescript-parsers/global-await.js
new file mode 100644
index 000000000..d68af6fc6
--- /dev/null
+++ b/tests/fixtures/parsers/typescript-parsers/global-await.js
@@ -0,0 +1,162 @@
+"use strict";
+
+/**
+ * Parser: @typescript-eslint/parser v2.6.1
+ * Source code:
+ * await foo();
+ */
+
+exports.parse = () => ({
+    type: "Program",
+    body: [
+        {
+            type: "ExpressionStatement",
+            expression: {
+                type: "AwaitExpression",
+                argument: {
+                    type: "CallExpression",
+                    callee: {
+                        type: "Identifier",
+                        name: "foo",
+                        range: [6, 9],
+                        loc: {
+                            start: {
+                                line: 1,
+                                column: 6
+                            },
+                            end: {
+                                line: 1,
+                                column: 9
+                            }
+                        }
+                    },
+                    arguments: [],
+                    optional: false,
+                    range: [6, 11],
+                    loc: {
+                        start: {
+                            line: 1,
+                            column: 6
+                        },
+                        end: {
+                            line: 1,
+                            column: 11
+                        }
+                    }
+                },
+                range: [0, 11],
+                loc: {
+                    start: {
+                        line: 1,
+                        column: 0
+                    },
+                    end: {
+                        line: 1,
+                        column: 11
+                    }
+                }
+            },
+            range: [0, 12],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 0
+                },
+                end: {
+                    line: 1,
+                    column: 12
+                }
+            }
+        }
+    ],
+    sourceType: "module",
+    range: [0, 12],
+    loc: {
+        start: {
+            line: 1,
+            column: 0
+        },
+        end: {
+            line: 1,
+            column: 12
+        }
+    },
+    tokens: [
+        {
+            type: "Identifier",
+            value: "await",
+            range: [0, 5],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 0
+                },
+                end: {
+                    line: 1,
+                    column: 5
+                }
+            }
+        },
+        {
+            type: "Identifier",
+            value: "foo",
+            range: [6, 9],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 6
+                },
+                end: {
+                    line: 1,
+                    column: 9
+                }
+            }
+        },
+        {
+            type: "Punctuator",
+            value: "(",
+            range: [9, 10],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 9
+                },
+                end: {
+                    line: 1,
+                    column: 10
+                }
+            }
+        },
+        {
+            type: "Punctuator",
+            value: ")",
+            range: [10, 11],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 10
+                },
+                end: {
+                    line: 1,
+                    column: 11
+                }
+            }
+        },
+        {
+            type: "Punctuator",
+            value: ";",
+            range: [11, 12],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 11
+                },
+                end: {
+                    line: 1,
+                    column: 12
+                }
+            }
+        }
+    ],
+    comments: []
+});
diff --git a/tests/fixtures/parsers/typescript-parsers/global-for-await-of.js b/tests/fixtures/parsers/typescript-parsers/global-for-await-of.js
new file mode 100644
index 000000000..2f61b833b
--- /dev/null
+++ b/tests/fixtures/parsers/typescript-parsers/global-for-await-of.js
@@ -0,0 +1,470 @@
+"use strict";
+
+/**
+ * Parser: @typescript-eslint/parser v2.6.1
+ * Source code:
+for await (let num of asyncIterable) {
+  console.log(num);
+}
+ */
+
+exports.parse = () => ({
+    type: "Program",
+    body: [
+        {
+            type: "ForOfStatement",
+            left: {
+                type: "VariableDeclaration",
+                declarations: [
+                    {
+                        type: "VariableDeclarator",
+                        id: {
+                            type: "Identifier",
+                            name: "num",
+                            range: [15, 18],
+                            loc: {
+                                start: {
+                                    line: 1,
+                                    column: 15
+                                },
+                                end: {
+                                    line: 1,
+                                    column: 18
+                                }
+                            }
+                        },
+                        init: null,
+                        range: [15, 18],
+                        loc: {
+                            start: {
+                                line: 1,
+                                column: 15
+                            },
+                            end: {
+                                line: 1,
+                                column: 18
+                            }
+                        }
+                    }
+                ],
+                kind: "let",
+                range: [11, 18],
+                loc: {
+                    start: {
+                        line: 1,
+                        column: 11
+                    },
+                    end: {
+                        line: 1,
+                        column: 18
+                    }
+                }
+            },
+            right: {
+                type: "Identifier",
+                name: "asyncIterable",
+                range: [22, 35],
+                loc: {
+                    start: {
+                        line: 1,
+                        column: 22
+                    },
+                    end: {
+                        line: 1,
+                        column: 35
+                    }
+                }
+            },
+            body: {
+                type: "BlockStatement",
+                body: [
+                    {
+                        type: "ExpressionStatement",
+                        expression: {
+                            type: "CallExpression",
+                            callee: {
+                                type: "MemberExpression",
+                                object: {
+                                    type: "Identifier",
+                                    name: "console",
+                                    range: [41, 48],
+                                    loc: {
+                                        start: {
+                                            line: 2,
+                                            column: 2
+                                        },
+                                        end: {
+                                            line: 2,
+                                            column: 9
+                                        }
+                                    }
+                                },
+                                property: {
+                                    type: "Identifier",
+                                    name: "log",
+                                    range: [49, 52],
+                                    loc: {
+                                        start: {
+                                            line: 2,
+                                            column: 10
+                                        },
+                                        end: {
+                                            line: 2,
+                                            column: 13
+                                        }
+                                    }
+                                },
+                                computed: false,
+                                optional: false,
+                                range: [41, 52],
+                                loc: {
+                                    start: {
+                                        line: 2,
+                                        column: 2
+                                    },
+                                    end: {
+                                        line: 2,
+                                        column: 13
+                                    }
+                                }
+                            },
+                            arguments: [
+                                {
+                                    type: "Identifier",
+                                    name: "num",
+                                    range: [53, 56],
+                                    loc: {
+                                        start: {
+                                            line: 2,
+                                            column: 14
+                                        },
+                                        end: {
+                                            line: 2,
+                                            column: 17
+                                        }
+                                    }
+                                }
+                            ],
+                            optional: false,
+                            range: [41, 57],
+                            loc: {
+                                start: {
+                                    line: 2,
+                                    column: 2
+                                },
+                                end: {
+                                    line: 2,
+                                    column: 18
+                                }
+                            }
+                        },
+                        range: [41, 58],
+                        loc: {
+                            start: {
+                                line: 2,
+                                column: 2
+                            },
+                            end: {
+                                line: 2,
+                                column: 19
+                            }
+                        }
+                    }
+                ],
+                range: [37, 60],
+                loc: {
+                    start: {
+                        line: 1,
+                        column: 37
+                    },
+                    end: {
+                        line: 3,
+                        column: 1
+                    }
+                }
+            },
+            await: true,
+            range: [0, 60],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 0
+                },
+                end: {
+                    line: 3,
+                    column: 1
+                }
+            }
+        }
+    ],
+    sourceType: "module",
+    range: [0, 60],
+    loc: {
+        start: {
+            line: 1,
+            column: 0
+        },
+        end: {
+            line: 3,
+            column: 1
+        }
+    },
+    tokens: [
+        {
+            type: "Keyword",
+            value: "for",
+            range: [0, 3],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 0
+                },
+                end: {
+                    line: 1,
+                    column: 3
+                }
+            }
+        },
+        {
+            type: "Identifier",
+            value: "await",
+            range: [4, 9],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 4
+                },
+                end: {
+                    line: 1,
+                    column: 9
+                }
+            }
+        },
+        {
+            type: "Punctuator",
+            value: "(",
+            range: [10, 11],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 10
+                },
+                end: {
+                    line: 1,
+                    column: 11
+                }
+            }
+        },
+        {
+            type: "Keyword",
+            value: "let",
+            range: [11, 14],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 11
+                },
+                end: {
+                    line: 1,
+                    column: 14
+                }
+            }
+        },
+        {
+            type: "Identifier",
+            value: "num",
+            range: [15, 18],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 15
+                },
+                end: {
+                    line: 1,
+                    column: 18
+                }
+            }
+        },
+        {
+            type: "Identifier",
+            value: "of",
+            range: [19, 21],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 19
+                },
+                end: {
+                    line: 1,
+                    column: 21
+                }
+            }
+        },
+        {
+            type: "Identifier",
+            value: "asyncIterable",
+            range: [22, 35],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 22
+                },
+                end: {
+                    line: 1,
+                    column: 35
+                }
+            }
+        },
+        {
+            type: "Punctuator",
+            value: ")",
+            range: [35, 36],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 35
+                },
+                end: {
+                    line: 1,
+                    column: 36
+                }
+            }
+        },
+        {
+            type: "Punctuator",
+            value: "{",
+            range: [37, 38],
+            loc: {
+                start: {
+                    line: 1,
+                    column: 37
+                },
+                end: {
+                    line: 1,
+                    column: 38
+                }
+            }
+        },
+        {
+            type: "Identifier",
+            value: "console",
+            range: [41, 48],
+            loc: {
+                start: {
+                    line: 2,
+                    column: 2
+                },
+                end: {
+                    line: 2,
+                    column: 9
+                }
+            }
+        },
+        {
+            type: "Punctuator",
+            value: ".",
+            range: [48, 49],
+            loc: {
+                start: {
+                    line: 2,
+                    column: 9
+                },
+                end: {
+                    line: 2,
+                    column: 10
+                }
+            }
+        },
+        {
+            type: "Identifier",
+            value: "log",
+            range: [49, 52],
+            loc: {
+                start: {
+                    line: 2,
+                    column: 10
+                },
+                end: {
+                    line: 2,
+                    column: 13
+                }
+            }
+        },
+        {
+            type: "Punctuator",
+            value: "(",
+            range: [52, 53],
+            loc: {
+                start: {
+                    line: 2,
+                    column: 13
+                },
+                end: {
+                    line: 2,
+                    column: 14
+                }
+            }
+        },
+        {
+            type: "Identifier",
+            value: "num",
+            range: [53, 56],
+            loc: {
+                start: {
+                    line: 2,
+                    column: 14
+                },
+                end: {
+                    line: 2,
+                    column: 17
+                }
+            }
+        },
+        {
+            type: "Punctuator",
+            value: ")",
+            range: [56, 57],
+            loc: {
+                start: {
+                    line: 2,
+                    column: 17
+                },
+                end: {
+                    line: 2,
+                    column: 18
+                }
+            }
+        },
+        {
+            type: "Punctuator",
+            value: ";",
+            range: [57, 58],
+            loc: {
+                start: {
+                    line: 2,
+                    column: 18
+                },
+                end: {
+                    line: 2,
+                    column: 19
+                }
+            }
+        },
+        {
+            type: "Punctuator",
+            value: "}",
+            range: [59, 60],
+            loc: {
+                start: {
+                    line: 3,
+                    column: 0
+                },
+                end: {
+                    line: 3,
+                    column: 1
+                }
+            }
+        }
+    ],
+    comments: []
+});
diff --git a/tests/lib/rules/require-await.js b/tests/lib/rules/require-await.js
index 7b43f3266..55c305e78 100644
--- a/tests/lib/rules/require-await.js
+++ b/tests/lib/rules/require-await.js
@@ -41,7 +41,21 @@ ruleTester.run("require-await", rule, {
         "function foo() { doSomething() }",
 
         // for-await-of
-        "async function foo() { for await (x of xs); }"
+        "async function foo() { for await (x of xs); }",
+
+        // global await
+        {
+            code: "await foo()",
+            parser: require.resolve("../../fixtures/parsers/typescript-parsers/global-await")
+        },
+        {
+            code: `
+                for await (let num of asyncIterable) {
+                    console.log(num);
+                }
+            `,
+            parser: require.resolve("../../fixtures/parsers/typescript-parsers/global-for-await-of")
+        }
     ],
     invalid: [
         {