From 5440ae1cae173774a75e81dc816789b1f6c06445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Mon, 2 Dec 2019 22:41:39 +0100 Subject: [PATCH] Correctly disambiguate / after async fuctions (#10475) * Correctly disambiguate / after async fuctions --- .../babel-parser/src/parser/expression.js | 14 ++ .../input.js | 2 + .../output.json | 185 ++++++++++++++++++ .../context-regex-after-statement/input.js | 2 + .../context-regex-after-statement/output.json | 122 ++++++++++++ 5 files changed, 325 insertions(+) create mode 100644 packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/input.js create mode 100644 packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/output.json create mode 100644 packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/input.js create mode 100644 packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/output.json diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 89088db4d98b..81d34b837d2d 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -19,6 +19,7 @@ // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser import { types as tt, type TokenType } from "../tokenizer/types"; +import { types as ct } from "../tokenizer/context"; import * as N from "../types"; import LValParser from "./lval"; import { @@ -970,6 +971,19 @@ export default class ExpressionParser extends LValParser { this.match(tt._function) && !this.canInsertSemicolon() ) { + const last = this.state.context.length - 1; + if (this.state.context[last] !== ct.functionStatement) { + // Since "async" is an identifier and normally identifiers + // can't be followed by expression, the tokenizer assumes + // that "function" starts a statement. + // Fixing it in the tokenizer would mean tracking not only the + // previous token ("async"), but also the one before to know + // its beforeExpr value. + // It's easier and more efficient to adjust the context here. + throw new Error("Internal error"); + } + this.state.context[last] = ct.functionExpression; + this.next(); return this.parseFunction(node, undefined, true); } else if ( diff --git a/packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/input.js b/packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/input.js new file mode 100644 index 000000000000..1415f9866a40 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/input.js @@ -0,0 +1,2 @@ +void async function fn() {} +/foo/g \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/output.json b/packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/output.json new file mode 100644 index 000000000000..9bd2028bf81f --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/output.json @@ -0,0 +1,185 @@ +{ + "type": "File", + "start": 0, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "expression": { + "type": "BinaryExpression", + "start": 0, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "left": { + "type": "BinaryExpression", + "start": 0, + "end": 32, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 4 + } + }, + "left": { + "type": "UnaryExpression", + "start": 0, + "end": 27, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 27 + } + }, + "operator": "void", + "prefix": true, + "argument": { + "type": "FunctionExpression", + "start": 5, + "end": 27, + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 27 + } + }, + "id": { + "type": "Identifier", + "start": 20, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 22 + }, + "identifierName": "fn" + }, + "name": "fn" + }, + "generator": false, + "async": true, + "params": [], + "body": { + "type": "BlockStatement", + "start": 25, + "end": 27, + "loc": { + "start": { + "line": 1, + "column": 25 + }, + "end": { + "line": 1, + "column": 27 + } + }, + "body": [], + "directives": [] + } + } + }, + "operator": "/", + "right": { + "type": "Identifier", + "start": 29, + "end": 32, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 4 + }, + "identifierName": "foo" + }, + "name": "foo" + } + }, + "operator": "/", + "right": { + "type": "Identifier", + "start": 33, + "end": 34, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 6 + }, + "identifierName": "g" + }, + "name": "g" + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/input.js b/packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/input.js new file mode 100644 index 000000000000..beb4d4ea4675 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/input.js @@ -0,0 +1,2 @@ +async function fn() {} +/foo/g \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/output.json b/packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/output.json new file mode 100644 index 000000000000..05504dd0734f --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/output.json @@ -0,0 +1,122 @@ +{ + "type": "File", + "start": 0, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "FunctionDeclaration", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "id": { + "type": "Identifier", + "start": 15, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 1, + "column": 17 + }, + "identifierName": "fn" + }, + "name": "fn" + }, + "generator": false, + "async": true, + "params": [], + "body": { + "type": "BlockStatement", + "start": 20, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "body": [], + "directives": [] + } + }, + { + "type": "ExpressionStatement", + "start": 23, + "end": 29, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "expression": { + "type": "RegExpLiteral", + "start": 23, + "end": 29, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "extra": { + "raw": "/foo/g" + }, + "pattern": "foo", + "flags": "g" + } + } + ], + "directives": [] + } +} \ No newline at end of file