From acaa2ae59e28448756d281abd9f53f33c17f0c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Sat, 28 Dec 2019 22:44:46 -0500 Subject: [PATCH 1/4] refactor: unify optionalMemberExpression generation --- .../babel-parser/src/parser/expression.js | 80 ++++++++----------- 1 file changed, 34 insertions(+), 46 deletions(-) diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 4660d3cc0797..8736ce51fd72 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -582,63 +582,46 @@ export default class ExpressionParser extends LValParser { startLoc, noCalls, ); - } else if (this.match(tt.questionDot)) { + } + let optional = false; + if (this.match(tt.questionDot)) { this.expectPlugin("optionalChaining"); - state.optionalChainMember = true; + state.optionalChainMember = optional = true; if (noCalls && this.lookaheadCharCode() === charCodes.leftParenthesis) { state.stop = true; return base; } this.next(); - - const node = this.startNodeAt(startPos, startLoc); - - if (this.eat(tt.bracketL)) { - node.object = base; - node.property = this.parseExpression(); - node.computed = true; - node.optional = true; - this.expect(tt.bracketR); - return this.finishNode(node, "OptionalMemberExpression"); - } else if (this.eat(tt.parenL)) { - node.callee = base; - node.arguments = this.parseCallExpressionArguments(tt.parenR, false); - node.optional = true; - return this.finishCallExpression(node, /* optional */ true); - } else { - node.object = base; - node.property = this.parseIdentifier(true); - node.computed = false; - node.optional = true; - return this.finishNode(node, "OptionalMemberExpression"); - } - } else if (this.eat(tt.dot)) { + } + const computed = this.eat(tt.bracketL); + if ( + (optional && !this.match(tt.parenL) && !this.match(tt.backQuote)) || + computed || + this.eat(tt.dot) + ) { const node = this.startNodeAt(startPos, startLoc); node.object = base; - node.property = this.parseMaybePrivateName(); - node.computed = false; + node.property = computed + ? this.parseExpression() + : optional + ? this.parseIdentifier(true) + : this.parseMaybePrivateName(); + node.computed = computed; if ( node.property.type === "PrivateName" && node.object.type === "Super" ) { this.raise(startPos, "Private fields can't be accessed on super"); } - if (state.optionalChainMember) { - node.optional = false; - return this.finishNode(node, "OptionalMemberExpression"); + if (computed) { + this.expect(tt.bracketR); } - return this.finishNode(node, "MemberExpression"); - } else if (this.eat(tt.bracketL)) { - const node = this.startNodeAt(startPos, startLoc); - node.object = base; - node.property = this.parseExpression(); - node.computed = true; - this.expect(tt.bracketR); if (state.optionalChainMember) { - node.optional = false; + node.optional = optional; return this.finishNode(node, "OptionalMemberExpression"); + } else { + return this.finishNode(node, "MemberExpression"); } - return this.finishNode(node, "MemberExpression"); } else if (!noCalls && this.match(tt.parenL)) { const oldMaybeInArrowParameters = this.state.maybeInArrowParameters; const oldYieldPos = this.state.yieldPos; @@ -652,13 +635,18 @@ export default class ExpressionParser extends LValParser { let node = this.startNodeAt(startPos, startLoc); node.callee = base; - node.arguments = this.parseCallExpressionArguments( - tt.parenR, - state.maybeAsyncArrow, - base.type === "Import", - base.type !== "Super", - node, - ); + if (optional) { + node.optional = true; + node.arguments = this.parseCallExpressionArguments(tt.parenR, false); + } else { + node.arguments = this.parseCallExpressionArguments( + tt.parenR, + state.maybeAsyncArrow, + base.type === "Import", + base.type !== "Super", + node, + ); + } this.finishCallExpression(node, state.optionalChainMember); if (state.maybeAsyncArrow && this.shouldParseAsyncArrow()) { From 55fb012e30922536ddcee42b8f8afb4bd3c16f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Sat, 28 Dec 2019 23:55:57 -0500 Subject: [PATCH 2/4] test: add optional calls invalid typecasts --- .../typecasts/fail-in-optional-calls/input.js | 1 + .../fail-in-optional-calls/options.json | 7 + .../fail-in-optional-calls/output.json | 167 ++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 packages/babel-parser/test/fixtures/flow/typecasts/fail-in-optional-calls/input.js create mode 100644 packages/babel-parser/test/fixtures/flow/typecasts/fail-in-optional-calls/options.json create mode 100644 packages/babel-parser/test/fixtures/flow/typecasts/fail-in-optional-calls/output.json diff --git a/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-optional-calls/input.js b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-optional-calls/input.js new file mode 100644 index 000000000000..14df4e2b16c7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-optional-calls/input.js @@ -0,0 +1 @@ +funccall?.(a, b: string); diff --git a/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-optional-calls/options.json b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-optional-calls/options.json new file mode 100644 index 000000000000..0e7765c73ae7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-optional-calls/options.json @@ -0,0 +1,7 @@ +{ + "sourceType": "module", + "plugins": [ + "flow", + "optionalChaining" + ] +} diff --git a/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-optional-calls/output.json b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-optional-calls/output.json new file mode 100644 index 000000000000..84fcef591c74 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-optional-calls/output.json @@ -0,0 +1,167 @@ +{ + "type": "File", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "errors": [ + "SyntaxError: The type cast expression is expected to be wrapped with parenthesis (1:15)" + ], + "program": { + "type": "Program", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "expression": { + "type": "OptionalCallExpression", + "start": 0, + "end": 24, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 24 + } + }, + "callee": { + "type": "Identifier", + "start": 0, + "end": 8, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 8 + }, + "identifierName": "funccall" + }, + "name": "funccall" + }, + "optional": true, + "arguments": [ + { + "type": "Identifier", + "start": 11, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 11 + }, + "end": { + "line": 1, + "column": 12 + }, + "identifierName": "a" + }, + "name": "a" + }, + { + "type": "TypeCastExpression", + "start": 14, + "end": 23, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 23 + } + }, + "expression": { + "type": "Identifier", + "start": 14, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 15 + }, + "identifierName": "b" + }, + "name": "b" + }, + "typeAnnotation": { + "type": "TypeAnnotation", + "start": 15, + "end": 23, + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 1, + "column": 23 + } + }, + "typeAnnotation": { + "type": "StringTypeAnnotation", + "start": 17, + "end": 23, + "loc": { + "start": { + "line": 1, + "column": 17 + }, + "end": { + "line": 1, + "column": 23 + } + } + } + } + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file From 00c9c2c281c378b9eac2625105aa22268a70b665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Sun, 29 Dec 2019 00:34:02 -0500 Subject: [PATCH 3/4] fix: do not parse async arrow when call is optional --- .../babel-parser/src/parser/expression.js | 2 +- .../fail-in-async-optional-calls/input.js | 1 + .../fail-in-async-optional-calls/options.json | 7 + .../fail-in-async-optional-calls/output.json | 134 ++++++++++++++++++ 4 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/input.js create mode 100644 packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/options.json create mode 100644 packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/output.json diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 8736ce51fd72..a84e96752a61 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -649,7 +649,7 @@ export default class ExpressionParser extends LValParser { } this.finishCallExpression(node, state.optionalChainMember); - if (state.maybeAsyncArrow && this.shouldParseAsyncArrow()) { + if (state.maybeAsyncArrow && this.shouldParseAsyncArrow() && !optional) { state.stop = true; node = this.parseAsyncArrowFromCallExpression( diff --git a/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/input.js b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/input.js new file mode 100644 index 000000000000..961846976c7a --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/input.js @@ -0,0 +1 @@ +async?.(bar: string) => {} diff --git a/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/options.json b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/options.json new file mode 100644 index 000000000000..0e7765c73ae7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/options.json @@ -0,0 +1,7 @@ +{ + "sourceType": "module", + "plugins": [ + "flow", + "optionalChaining" + ] +} diff --git a/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/output.json b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/output.json new file mode 100644 index 000000000000..ef00ecf9a206 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/output.json @@ -0,0 +1,134 @@ +{ + "type": "File", + "start": 0, + "end": 26, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 26 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 26, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 26 + } + }, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 26, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 26 + } + }, + "expression": { + "type": "ArrowFunctionExpression", + "start": 0, + "end": 26, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 26 + } + }, + "id": null, + "generator": false, + "async": true, + "params": [ + { + "type": "Identifier", + "start": 8, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 19 + }, + "identifierName": "bar" + }, + "name": "bar", + "typeAnnotation": { + "type": "TypeAnnotation", + "start": 11, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 11 + }, + "end": { + "line": 1, + "column": 19 + } + }, + "typeAnnotation": { + "type": "StringTypeAnnotation", + "start": 13, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 13 + }, + "end": { + "line": 1, + "column": 19 + } + } + } + } + } + ], + "body": { + "type": "BlockStatement", + "start": 24, + "end": 26, + "loc": { + "start": { + "line": 1, + "column": 24 + }, + "end": { + "line": 1, + "column": 26 + } + }, + "body": [], + "directives": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file From 7320bd2472786f497accf140b7bea64ae6514542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Sun, 29 Dec 2019 00:38:04 -0500 Subject: [PATCH 4/4] test: update test fixtures --- .../fail-in-async-optional-calls/options.json | 5 +- .../fail-in-async-optional-calls/output.json | 134 ------------------ 2 files changed, 3 insertions(+), 136 deletions(-) delete mode 100644 packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/output.json diff --git a/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/options.json b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/options.json index 0e7765c73ae7..3c7c9c589b44 100644 --- a/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/options.json +++ b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/options.json @@ -3,5 +3,6 @@ "plugins": [ "flow", "optionalChaining" - ] -} + ], + "throws": "Unexpected token, expected \";\" (1:21)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/output.json b/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/output.json deleted file mode 100644 index ef00ecf9a206..000000000000 --- a/packages/babel-parser/test/fixtures/flow/typecasts/fail-in-async-optional-calls/output.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "type": "File", - "start": 0, - "end": 26, - "loc": { - "start": { - "line": 1, - "column": 0 - }, - "end": { - "line": 1, - "column": 26 - } - }, - "program": { - "type": "Program", - "start": 0, - "end": 26, - "loc": { - "start": { - "line": 1, - "column": 0 - }, - "end": { - "line": 1, - "column": 26 - } - }, - "sourceType": "module", - "interpreter": null, - "body": [ - { - "type": "ExpressionStatement", - "start": 0, - "end": 26, - "loc": { - "start": { - "line": 1, - "column": 0 - }, - "end": { - "line": 1, - "column": 26 - } - }, - "expression": { - "type": "ArrowFunctionExpression", - "start": 0, - "end": 26, - "loc": { - "start": { - "line": 1, - "column": 0 - }, - "end": { - "line": 1, - "column": 26 - } - }, - "id": null, - "generator": false, - "async": true, - "params": [ - { - "type": "Identifier", - "start": 8, - "end": 19, - "loc": { - "start": { - "line": 1, - "column": 8 - }, - "end": { - "line": 1, - "column": 19 - }, - "identifierName": "bar" - }, - "name": "bar", - "typeAnnotation": { - "type": "TypeAnnotation", - "start": 11, - "end": 19, - "loc": { - "start": { - "line": 1, - "column": 11 - }, - "end": { - "line": 1, - "column": 19 - } - }, - "typeAnnotation": { - "type": "StringTypeAnnotation", - "start": 13, - "end": 19, - "loc": { - "start": { - "line": 1, - "column": 13 - }, - "end": { - "line": 1, - "column": 19 - } - } - } - } - } - ], - "body": { - "type": "BlockStatement", - "start": 24, - "end": 26, - "loc": { - "start": { - "line": 1, - "column": 24 - }, - "end": { - "line": 1, - "column": 26 - } - }, - "body": [], - "directives": [] - } - } - } - ], - "directives": [] - } -} \ No newline at end of file