Parse for await (async of ...) (#13244)

This commit is contained in:
Stuart Cook
2021-05-02 02:00:21 +10:00
committed by GitHub
parent 2a3e35f026
commit fa01fbe052
31 changed files with 554 additions and 11 deletions

View File

@@ -61,6 +61,7 @@ export const ErrorMessages = makeErrorTemplates(
"'from' is not allowed as an identifier after 'export default'.",
ForInOfLoopInitializer:
"'%0' loop variable declaration may not have an initializer.",
ForOfAsync: "The left-hand side of a for-of loop may not be 'async'.",
ForOfLet: "The left-hand side of a for-of loop may not start with 'let'.",
GeneratorInSingleStatementContext:
"Generators can only be declared at the top level or inside a block.",

View File

@@ -1032,7 +1032,16 @@ export default class ExpressionParser extends LValParser {
true,
);
} else if (this.match(tt.name)) {
return this.parseAsyncArrowUnaryFunction(id);
// If the next token begins with "=", commit to parsing an async
// arrow function. (Peeking ahead for "=" lets us avoid a more
// expensive full-token lookahead on this common path.)
if (this.lookaheadCharCode() === charCodes.equalsTo) {
return this.parseAsyncArrowUnaryFunction(id);
} else {
// Otherwise, treat "async" as an identifier and let calling code
// deal with the current tt.name token.
return id;
}
} else if (this.match(tt._do)) {
return this.parseDo(true);
}

View File

@@ -539,13 +539,33 @@ export default class StatementParser extends ExpressionParser {
return this.parseFor(node, init);
}
// Check whether the first token is possibly a contextual keyword, so that
// we can forbid `for (async of` if this turns out to be a for-of loop.
const startsWithUnescapedName =
this.match(tt.name) && !this.state.containsEsc;
const refExpressionErrors = new ExpressionErrors();
const init = this.parseExpression(true, refExpressionErrors);
const isForOf = this.isContextual("of");
if (isForOf || this.match(tt._in)) {
if (isForOf && startsWithLet) {
if (isForOf) {
// Check for leading tokens that are forbidden in for-of loops:
if (startsWithLet) {
this.raise(init.start, Errors.ForOfLet);
} else if (
// `for await (async of []);` is allowed.
awaitAt === -1 &&
startsWithUnescapedName &&
init.type === "Identifier" &&
init.name === "async"
) {
// This catches the case where the `async` in `for (async of` was
// parsed as an identifier. If it was parsed as the start of an async
// arrow function (e.g. `for (async of => {} of []);`), the LVal check
// further down will raise a more appropriate error.
this.raise(init.start, Errors.ForOfAsync);
}
}
if (isForOf || this.match(tt._in)) {
this.toAssignable(init, /* isLHS */ true);
const description = isForOf ? "for-of statement" : "for-in statement";
this.checkLVal(init, description);