diff --git a/packages/babel-generator/src/node/parentheses.ts b/packages/babel-generator/src/node/parentheses.ts index 0da40cc08e51..e5666b974eb5 100644 --- a/packages/babel-generator/src/node/parentheses.ts +++ b/packages/babel-generator/src/node/parentheses.ts @@ -283,8 +283,13 @@ export function LogicalExpression(node: any, parent: any): boolean { export function Identifier(node: t.Identifier, parent: t.Node): boolean { // ECMAScript specifically forbids a for-of loop from starting with the - // token sequence "for ( async of", because it would be ambiguous with - // "for (async of => {};;)", so we need to add extra parentheses. + // token sequence `for (async of`, because it would be ambiguous with + // `for (async of => {};;)`, so we need to add extra parentheses. + // + // If the parent is a for-await-of loop (i.e. parent.await === true), the + // parentheses aren't strictly needed, but we add them anyway because + // some tools (including earlier Babel versions) can't parse + // `for await (async of [])` without them. return ( node.name === "async" && t.isForOfStatement(parent) && node === parent.left ); diff --git a/packages/babel-generator/test/fixtures/edgecase/for-async-of/input.js b/packages/babel-generator/test/fixtures/edgecase/for-async-of/input.js index 934747c7b4f9..bd94c08f6a06 100644 --- a/packages/babel-generator/test/fixtures/edgecase/for-async-of/input.js +++ b/packages/babel-generator/test/fixtures/edgecase/for-async-of/input.js @@ -4,4 +4,6 @@ for ((async) of async) async; for (\u0061sync of []); +for (async.x of []); + for (async in []); diff --git a/packages/babel-generator/test/fixtures/edgecase/for-async-of/output.js b/packages/babel-generator/test/fixtures/edgecase/for-async-of/output.js index c5734919e906..99c5ce9e1754 100644 --- a/packages/babel-generator/test/fixtures/edgecase/for-async-of/output.js +++ b/packages/babel-generator/test/fixtures/edgecase/for-async-of/output.js @@ -4,4 +4,6 @@ for ((async) of async) async; for ((async) of []); +for (async.x of []); + for (async in []); \ No newline at end of file diff --git a/packages/babel-generator/test/fixtures/edgecase/for-await-async-of/input.js b/packages/babel-generator/test/fixtures/edgecase/for-await-async-of/input.js new file mode 100644 index 000000000000..c13395f75467 --- /dev/null +++ b/packages/babel-generator/test/fixtures/edgecase/for-await-async-of/input.js @@ -0,0 +1,11 @@ +async function f() { + for await (async of []); + + for await (async of async) async; + + for await ((async) of []); + + for await (async.x of []); + + for await (\u0061sync of []); +} diff --git a/packages/babel-generator/test/fixtures/edgecase/for-await-async-of/output.js b/packages/babel-generator/test/fixtures/edgecase/for-await-async-of/output.js new file mode 100644 index 000000000000..10a77c0134fb --- /dev/null +++ b/packages/babel-generator/test/fixtures/edgecase/for-await-async-of/output.js @@ -0,0 +1,11 @@ +async function f() { + for await ((async) of []); + + for await ((async) of async) async; + + for await ((async) of []); + + for await (async.x of []); + + for await ((async) of []); +} \ No newline at end of file diff --git a/packages/babel-parser/src/parser/error-message.js b/packages/babel-parser/src/parser/error-message.js index 0742e32f025d..2efe2ba84569 100644 --- a/packages/babel-parser/src/parser/error-message.js +++ b/packages/babel-parser/src/parser/error-message.js @@ -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.", diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 3da6f2d99197..b7cf9391ad1a 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -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); } diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 21f7ea15e05a..1986220a1ede 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -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); diff --git a/packages/babel-parser/test/fixtures/es2017/async-functions/export-invalid/options.json b/packages/babel-parser/test/fixtures/es2017/async-functions/export-invalid/options.json index 758834b289e0..54925950e059 100644 --- a/packages/babel-parser/test/fixtures/es2017/async-functions/export-invalid/options.json +++ b/packages/babel-parser/test/fixtures/es2017/async-functions/export-invalid/options.json @@ -1,4 +1,3 @@ { - "sourceType": "module", - "throws": "Unexpected token, expected \"=>\" (1:31)" -} + "sourceType": "module" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2017/async-functions/export-invalid/output.json b/packages/babel-parser/test/fixtures/es2017/async-functions/export-invalid/output.json new file mode 100644 index 000000000000..bc95d7243d74 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2017/async-functions/export-invalid/output.json @@ -0,0 +1,46 @@ +{ + "type": "File", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "errors": [ + "SyntaxError: Missing semicolon. (1:20)", + "SyntaxError: Missing semicolon. (1:33)" + ], + "program": { + "type": "Program", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExportDefaultDeclaration", + "start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}}, + "declaration": { + "type": "Identifier", + "start":15,"end":20,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":20},"identifierName":"async"}, + "name": "async" + } + }, + { + "type": "ExpressionStatement", + "start":21,"end":33,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":33}}, + "expression": { + "type": "CallExpression", + "start":21,"end":33,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":33}}, + "callee": { + "type": "Identifier", + "start":21,"end":30,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":30},"identifierName":"functionX"}, + "name": "functionX" + }, + "arguments": [] + } + }, + { + "type": "BlockStatement", + "start":34,"end":36,"loc":{"start":{"line":1,"column":34},"end":{"line":1,"column":36}}, + "body": [], + "directives": [] + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of-arrow-semicolon/input.js b/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of-arrow-semicolon/input.js new file mode 100644 index 000000000000..9ae251c89334 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of-arrow-semicolon/input.js @@ -0,0 +1,3 @@ +async function f() { + for await (async of => {};;); +} diff --git a/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of-arrow-semicolon/options.json b/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of-arrow-semicolon/options.json new file mode 100644 index 000000000000..f86bbd48e917 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of-arrow-semicolon/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Unexpected token (2:6)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of-arrow/input.js b/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of-arrow/input.js new file mode 100644 index 000000000000..5ef5c8c325b7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of-arrow/input.js @@ -0,0 +1,3 @@ +async function f() { + for await (async of => {} of x); +} diff --git a/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of-arrow/output.json b/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of-arrow/output.json new file mode 100644 index 000000000000..204c02bacfb0 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of-arrow/output.json @@ -0,0 +1,69 @@ +{ + "type": "File", + "start":0,"end":57,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "errors": [ + "SyntaxError: Invalid left-hand side in for-of statement. (2:13)" + ], + "program": { + "type": "Program", + "start":0,"end":57,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "FunctionDeclaration", + "start":0,"end":57,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "id": { + "type": "Identifier", + "start":15,"end":16,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":16},"identifierName":"f"}, + "name": "f" + }, + "generator": false, + "async": true, + "params": [], + "body": { + "type": "BlockStatement", + "start":19,"end":57,"loc":{"start":{"line":1,"column":19},"end":{"line":3,"column":1}}, + "body": [ + { + "type": "ForOfStatement", + "start":23,"end":55,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":34}}, + "await": true, + "left": { + "type": "ArrowFunctionExpression", + "start":34,"end":48,"loc":{"start":{"line":2,"column":13},"end":{"line":2,"column":27}}, + "id": null, + "generator": false, + "async": true, + "params": [ + { + "type": "Identifier", + "start":40,"end":42,"loc":{"start":{"line":2,"column":19},"end":{"line":2,"column":21},"identifierName":"of"}, + "name": "of" + } + ], + "body": { + "type": "BlockStatement", + "start":46,"end":48,"loc":{"start":{"line":2,"column":25},"end":{"line":2,"column":27}}, + "body": [], + "directives": [] + } + }, + "right": { + "type": "Identifier", + "start":52,"end":53,"loc":{"start":{"line":2,"column":31},"end":{"line":2,"column":32},"identifierName":"x"}, + "name": "x" + }, + "body": { + "type": "EmptyStatement", + "start":54,"end":55,"loc":{"start":{"line":2,"column":33},"end":{"line":2,"column":34}} + } + } + ], + "directives": [] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of/input.js b/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of/input.js new file mode 100644 index 000000000000..2f48297dc477 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of/input.js @@ -0,0 +1,3 @@ +async function f() { + for await (async of x); +} diff --git a/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of/output.json b/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of/output.json new file mode 100644 index 000000000000..d79dbf303417 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-of/output.json @@ -0,0 +1,51 @@ +{ + "type": "File", + "start":0,"end":48,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "program": { + "type": "Program", + "start":0,"end":48,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "FunctionDeclaration", + "start":0,"end":48,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "id": { + "type": "Identifier", + "start":15,"end":16,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":16},"identifierName":"f"}, + "name": "f" + }, + "generator": false, + "async": true, + "params": [], + "body": { + "type": "BlockStatement", + "start":19,"end":48,"loc":{"start":{"line":1,"column":19},"end":{"line":3,"column":1}}, + "body": [ + { + "type": "ForOfStatement", + "start":23,"end":46,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":25}}, + "await": true, + "left": { + "type": "Identifier", + "start":34,"end":39,"loc":{"start":{"line":2,"column":13},"end":{"line":2,"column":18},"identifierName":"async"}, + "name": "async" + }, + "right": { + "type": "Identifier", + "start":43,"end":44,"loc":{"start":{"line":2,"column":22},"end":{"line":2,"column":23},"identifierName":"x"}, + "name": "x" + }, + "body": { + "type": "EmptyStatement", + "start":45,"end":46,"loc":{"start":{"line":2,"column":24},"end":{"line":2,"column":25}} + } + } + ], + "directives": [] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/dot/input.js b/packages/babel-parser/test/fixtures/es2021/for-async-of/dot/input.js new file mode 100644 index 000000000000..6bb5e9a42ba8 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/dot/input.js @@ -0,0 +1 @@ +for (async.x of y); diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/dot/output.json b/packages/babel-parser/test/fixtures/es2021/for-async-of/dot/output.json new file mode 100644 index 000000000000..a80878cd9ff9 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/dot/output.json @@ -0,0 +1,42 @@ +{ + "type": "File", + "start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}}, + "program": { + "type": "Program", + "start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ForOfStatement", + "start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}}, + "await": false, + "left": { + "type": "MemberExpression", + "start":5,"end":12,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":12}}, + "object": { + "type": "Identifier", + "start":5,"end":10,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":10},"identifierName":"async"}, + "name": "async" + }, + "computed": false, + "property": { + "type": "Identifier", + "start":11,"end":12,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":12},"identifierName":"x"}, + "name": "x" + } + }, + "right": { + "type": "Identifier", + "start":16,"end":17,"loc":{"start":{"line":1,"column":16},"end":{"line":1,"column":17},"identifierName":"y"}, + "name": "y" + }, + "body": { + "type": "EmptyStatement", + "start":18,"end":19,"loc":{"start":{"line":1,"column":18},"end":{"line":1,"column":19}} + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/escaped/input.js b/packages/babel-parser/test/fixtures/es2021/for-async-of/escaped/input.js new file mode 100644 index 000000000000..52ed40e868c0 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/escaped/input.js @@ -0,0 +1 @@ +for (\u0061sync of x); diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/escaped/output.json b/packages/babel-parser/test/fixtures/es2021/for-async-of/escaped/output.json new file mode 100644 index 000000000000..8a71ac139e1c --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/escaped/output.json @@ -0,0 +1,32 @@ +{ + "type": "File", + "start":0,"end":22,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":22}}, + "program": { + "type": "Program", + "start":0,"end":22,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":22}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ForOfStatement", + "start":0,"end":22,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":22}}, + "await": false, + "left": { + "type": "Identifier", + "start":5,"end":15,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":15},"identifierName":"async"}, + "name": "async" + }, + "right": { + "type": "Identifier", + "start":19,"end":20,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":20},"identifierName":"x"}, + "name": "x" + }, + "body": { + "type": "EmptyStatement", + "start":21,"end":22,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":22}} + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/for-statement/input.js b/packages/babel-parser/test/fixtures/es2021/for-async-of/for-statement/input.js new file mode 100644 index 000000000000..24b0e2c995e7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/for-statement/input.js @@ -0,0 +1 @@ +for (async of => {};;); diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/for-statement/output.json b/packages/babel-parser/test/fixtures/es2021/for-async-of/for-statement/output.json new file mode 100644 index 000000000000..52f65bac9bce --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/for-statement/output.json @@ -0,0 +1,43 @@ +{ + "type": "File", + "start":0,"end":23,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":23}}, + "program": { + "type": "Program", + "start":0,"end":23,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":23}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ForStatement", + "start":0,"end":23,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":23}}, + "init": { + "type": "ArrowFunctionExpression", + "start":5,"end":19,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":19}}, + "id": null, + "generator": false, + "async": true, + "params": [ + { + "type": "Identifier", + "start":11,"end":13,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":13},"identifierName":"of"}, + "name": "of" + } + ], + "body": { + "type": "BlockStatement", + "start":17,"end":19,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":19}}, + "body": [], + "directives": [] + } + }, + "test": null, + "update": null, + "body": { + "type": "EmptyStatement", + "start":22,"end":23,"loc":{"start":{"line":1,"column":22},"end":{"line":1,"column":23}} + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden-arrow-function/input.js b/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden-arrow-function/input.js new file mode 100644 index 000000000000..588565b75c11 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden-arrow-function/input.js @@ -0,0 +1 @@ +for (async of => {} of x); diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden-arrow-function/output.json b/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden-arrow-function/output.json new file mode 100644 index 000000000000..b310f007c448 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden-arrow-function/output.json @@ -0,0 +1,50 @@ +{ + "type": "File", + "start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}}, + "errors": [ + "SyntaxError: Invalid left-hand side in for-of statement. (1:5)" + ], + "program": { + "type": "Program", + "start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ForOfStatement", + "start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}}, + "await": false, + "left": { + "type": "ArrowFunctionExpression", + "start":5,"end":19,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":19}}, + "id": null, + "generator": false, + "async": true, + "params": [ + { + "type": "Identifier", + "start":11,"end":13,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":13},"identifierName":"of"}, + "name": "of" + } + ], + "body": { + "type": "BlockStatement", + "start":17,"end":19,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":19}}, + "body": [], + "directives": [] + } + }, + "right": { + "type": "Identifier", + "start":23,"end":24,"loc":{"start":{"line":1,"column":23},"end":{"line":1,"column":24},"identifierName":"x"}, + "name": "x" + }, + "body": { + "type": "EmptyStatement", + "start":25,"end":26,"loc":{"start":{"line":1,"column":25},"end":{"line":1,"column":26}} + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden-equals/input.js b/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden-equals/input.js new file mode 100644 index 000000000000..60558a38a0ea --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden-equals/input.js @@ -0,0 +1,3 @@ +// Check what happens when the async-unary-arrow-function parser looks ahead +// and finds "=" instead of "=>". +for (async of = x); diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden-equals/options.json b/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden-equals/options.json new file mode 100644 index 000000000000..5afe13040ca0 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden-equals/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Unexpected token, expected \"=>\" (3:14)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden/input.js b/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden/input.js new file mode 100644 index 000000000000..bd6aeacece16 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden/input.js @@ -0,0 +1,5 @@ +for (async of x); + +async () => { + for (async of x); +} diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden/output.json b/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden/output.json new file mode 100644 index 000000000000..d8fa836717f7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/forbidden/output.json @@ -0,0 +1,74 @@ +{ + "type": "File", + "start":0,"end":54,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, + "errors": [ + "SyntaxError: The left-hand side of a for-of loop may not be 'async'. (1:5)", + "SyntaxError: The left-hand side of a for-of loop may not be 'async'. (4:7)" + ], + "program": { + "type": "Program", + "start":0,"end":54,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ForOfStatement", + "start":0,"end":17,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":17}}, + "await": false, + "left": { + "type": "Identifier", + "start":5,"end":10,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":10},"identifierName":"async"}, + "name": "async" + }, + "right": { + "type": "Identifier", + "start":14,"end":15,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":15},"identifierName":"x"}, + "name": "x" + }, + "body": { + "type": "EmptyStatement", + "start":16,"end":17,"loc":{"start":{"line":1,"column":16},"end":{"line":1,"column":17}} + } + }, + { + "type": "ExpressionStatement", + "start":19,"end":54,"loc":{"start":{"line":3,"column":0},"end":{"line":5,"column":1}}, + "expression": { + "type": "ArrowFunctionExpression", + "start":19,"end":54,"loc":{"start":{"line":3,"column":0},"end":{"line":5,"column":1}}, + "id": null, + "generator": false, + "async": true, + "params": [], + "body": { + "type": "BlockStatement", + "start":31,"end":54,"loc":{"start":{"line":3,"column":12},"end":{"line":5,"column":1}}, + "body": [ + { + "type": "ForOfStatement", + "start":35,"end":52,"loc":{"start":{"line":4,"column":2},"end":{"line":4,"column":19}}, + "await": false, + "left": { + "type": "Identifier", + "start":40,"end":45,"loc":{"start":{"line":4,"column":7},"end":{"line":4,"column":12},"identifierName":"async"}, + "name": "async" + }, + "right": { + "type": "Identifier", + "start":49,"end":50,"loc":{"start":{"line":4,"column":16},"end":{"line":4,"column":17},"identifierName":"x"}, + "name": "x" + }, + "body": { + "type": "EmptyStatement", + "start":51,"end":52,"loc":{"start":{"line":4,"column":18},"end":{"line":4,"column":19}} + } + } + ], + "directives": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/parens/input.js b/packages/babel-parser/test/fixtures/es2021/for-async-of/parens/input.js new file mode 100644 index 000000000000..6b21ec95e519 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/parens/input.js @@ -0,0 +1 @@ +for ((async) of x); diff --git a/packages/babel-parser/test/fixtures/es2021/for-async-of/parens/output.json b/packages/babel-parser/test/fixtures/es2021/for-async-of/parens/output.json new file mode 100644 index 000000000000..758ab36cf51a --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2021/for-async-of/parens/output.json @@ -0,0 +1,36 @@ +{ + "type": "File", + "start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}}, + "program": { + "type": "Program", + "start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ForOfStatement", + "start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}}, + "await": false, + "left": { + "type": "Identifier", + "start":6,"end":11,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":11},"identifierName":"async"}, + "extra": { + "parenthesized": true, + "parenStart": 5 + }, + "name": "async" + }, + "right": { + "type": "Identifier", + "start":16,"end":17,"loc":{"start":{"line":1,"column":16},"end":{"line":1,"column":17},"identifierName":"x"}, + "name": "x" + }, + "body": { + "type": "EmptyStatement", + "start":18,"end":19,"loc":{"start":{"line":1,"column":18},"end":{"line":1,"column":19}} + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/helpers/runFixtureTests.js b/packages/babel-parser/test/helpers/runFixtureTests.js index 81b10b6ecca2..0aaf0858d6b6 100644 --- a/packages/babel-parser/test/helpers/runFixtureTests.js +++ b/packages/babel-parser/test/helpers/runFixtureTests.js @@ -218,9 +218,21 @@ function runTest(test, parseFunction, compareErrorsOnly = false) { return save(test, ast); } - throw new Error( - "Expected error message: " + opts.throws + ". But parsing succeeded.", - ); + if (ast.errors?.length) { + throw new Error( + `Expected non-recoverable error message: ${ + opts.throws + }. But instead parsing recovered from errors: ${JSON.stringify( + ast.errors, + null, + 2, + )}`, + ); + } else { + throw new Error( + `Expected error message: ${opts.throws}. But parsing succeeded without errors.`, + ); + } } else if (compareErrorsOnly) { const mis = misMatch(JSON.parse(test.expect.code).errors, ast.errors); if (mis) {