From 9ae23639ad12898e3bf2dff7b1d1a9fbd2be15fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Tue, 14 Nov 2017 16:24:14 +0100 Subject: [PATCH] Parse async arrows with flow type parameters (#6802) Fixes #6712 --- packages/babylon/src/parser/expression.js | 9 +- packages/babylon/src/plugins/flow.js | 37 + .../type-generics/async-arrow-like/actual.js | 5 + .../async-arrow-like/expected.json | 657 ++++++++++++++++++ .../flow/type-generics/async-arrow/actual.js | 1 + .../type-generics/async-arrow/expected.json | 204 ++++++ 6 files changed, 909 insertions(+), 4 deletions(-) create mode 100644 packages/babylon/test/fixtures/flow/type-generics/async-arrow-like/actual.js create mode 100644 packages/babylon/test/fixtures/flow/type-generics/async-arrow-like/expected.json create mode 100644 packages/babylon/test/fixtures/flow/type-generics/async-arrow/actual.js create mode 100644 packages/babylon/test/fixtures/flow/type-generics/async-arrow/expected.json diff --git a/packages/babylon/src/parser/expression.js b/packages/babylon/src/parser/expression.js index 28357735cb..6a4fcd0811 100644 --- a/packages/babylon/src/parser/expression.js +++ b/packages/babylon/src/parser/expression.js @@ -1498,11 +1498,12 @@ export default class ExpressionParser extends LValParser { return node; } - // Parse arrow function expression with given parameters. - + // Parse arrow function expression. + // If the parameters are provided, they will be converted to an + // assignable list. parseArrowExpression( node: N.ArrowFunctionExpression, - params: N.Expression[], + params?: ?(N.Expression[]), isAsync?: boolean, ): N.ArrowFunctionExpression { // if we got there, it's no more "yield in possible arrow parameters"; @@ -1518,7 +1519,7 @@ export default class ExpressionParser extends LValParser { const oldInFunc = this.state.inFunction; this.state.inFunction = true; this.initFunction(node, isAsync); - this.setArrowFunctionParameters(node, params); + if (params) this.setArrowFunctionParameters(node, params); const oldInGenerator = this.state.inGenerator; const oldMaybeInArrowParameters = this.state.maybeInArrowParameters; diff --git a/packages/babylon/src/plugins/flow.js b/packages/babylon/src/plugins/flow.js index e26bd4301a..e92c7aefed 100644 --- a/packages/babylon/src/plugins/flow.js +++ b/packages/babylon/src/plugins/flow.js @@ -2219,8 +2219,45 @@ export default (superClass: Class): Class => node.callee = base; node.arguments = this.parseCallExpressionArguments(tt.parenR, false); base = this.finishNode(node, "CallExpression"); + } else if ( + base.type === "Identifier" && + base.name === "async" && + this.isRelational("<") + ) { + const state = this.state.clone(); + let error; + try { + const node = this.parseAsyncArrowWithTypeParameters( + startPos, + startLoc, + ); + if (node) return node; + } catch (e) { + error = e; + } + + this.state = state; + try { + return super.parseSubscripts(base, startPos, startLoc, noCalls); + } catch (e) { + throw error || e; + } } return super.parseSubscripts(base, startPos, startLoc, noCalls); } + + parseAsyncArrowWithTypeParameters( + startPos: number, + startLoc: Position, + ): ?N.ArrowFunctionExpression { + const node = this.startNodeAt(startPos, startLoc); + this.parseFunctionParams(node); + if (!this.parseArrow(node)) return; + return this.parseArrowExpression( + node, + /* params */ undefined, + /* isAsync */ true, + ); + } }; diff --git a/packages/babylon/test/fixtures/flow/type-generics/async-arrow-like/actual.js b/packages/babylon/test/fixtures/flow/type-generics/async-arrow-like/actual.js new file mode 100644 index 0000000000..c43f3d0eea --- /dev/null +++ b/packages/babylon/test/fixtures/flow/type-generics/async-arrow-like/actual.js @@ -0,0 +1,5 @@ +async (fn: () => T); + +// This looks A LOT like an async arrow function, but it isn't because +// T + U isn't a valid type parameter. +(async (fn: T): T => fn); diff --git a/packages/babylon/test/fixtures/flow/type-generics/async-arrow-like/expected.json b/packages/babylon/test/fixtures/flow/type-generics/async-arrow-like/expected.json new file mode 100644 index 0000000000..5b1aa319f9 --- /dev/null +++ b/packages/babylon/test/fixtures/flow/type-generics/async-arrow-like/expected.json @@ -0,0 +1,657 @@ +{ + "type": "File", + "start": 0, + "end": 167, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 32 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 167, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 32 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 23, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 23 + } + }, + "expression": { + "type": "BinaryExpression", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "left": { + "type": "BinaryExpression", + "start": 0, + "end": 8, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 8 + } + }, + "left": { + "type": "Identifier", + "start": 0, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 5 + }, + "identifierName": "async" + }, + "name": "async" + }, + "operator": "<", + "right": { + "type": "Identifier", + "start": 7, + "end": 8, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 8 + }, + "identifierName": "T" + }, + "name": "T" + } + }, + "operator": ">", + "right": { + "type": "TypeCastExpression", + "start": 10, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "expression": { + "type": "Identifier", + "start": 10, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 1, + "column": 12 + }, + "identifierName": "fn" + }, + "name": "fn" + }, + "typeAnnotation": { + "type": "TypeAnnotation", + "start": 12, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 12 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "typeAnnotation": { + "type": "FunctionTypeAnnotation", + "start": 14, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "params": [], + "rest": null, + "returnType": { + "type": "GenericTypeAnnotation", + "start": 20, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 20, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 21 + }, + "identifierName": "T" + }, + "name": "T" + } + }, + "typeParameters": null + } + }, + "extra": { + "parenthesized": true, + "parenStart": 9 + } + } + }, + "trailingComments": [ + { + "type": "CommentLine", + "value": " This looks A LOT like an async arrow function, but it isn't because", + "start": 25, + "end": 95, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 70 + } + } + }, + { + "type": "CommentLine", + "value": " T + U isn't a valid type parameter.", + "start": 96, + "end": 134, + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 38 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 135, + "end": 167, + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 5, + "column": 32 + } + }, + "expression": { + "type": "TypeCastExpression", + "start": 136, + "end": 165, + "loc": { + "start": { + "line": 5, + "column": 1 + }, + "end": { + "line": 5, + "column": 30 + } + }, + "expression": { + "type": "BinaryExpression", + "start": 136, + "end": 156, + "loc": { + "start": { + "line": 5, + "column": 1 + }, + "end": { + "line": 5, + "column": 21 + } + }, + "left": { + "type": "BinaryExpression", + "start": 136, + "end": 148, + "loc": { + "start": { + "line": 5, + "column": 1 + }, + "end": { + "line": 5, + "column": 13 + } + }, + "left": { + "type": "Identifier", + "start": 136, + "end": 141, + "loc": { + "start": { + "line": 5, + "column": 1 + }, + "end": { + "line": 5, + "column": 6 + }, + "identifierName": "async" + }, + "name": "async", + "leadingComments": null + }, + "operator": "<", + "right": { + "type": "BinaryExpression", + "start": 143, + "end": 148, + "loc": { + "start": { + "line": 5, + "column": 8 + }, + "end": { + "line": 5, + "column": 13 + } + }, + "left": { + "type": "Identifier", + "start": 143, + "end": 144, + "loc": { + "start": { + "line": 5, + "column": 8 + }, + "end": { + "line": 5, + "column": 9 + }, + "identifierName": "T" + }, + "name": "T" + }, + "operator": "+", + "right": { + "type": "Identifier", + "start": 147, + "end": 148, + "loc": { + "start": { + "line": 5, + "column": 12 + }, + "end": { + "line": 5, + "column": 13 + }, + "identifierName": "U" + }, + "name": "U" + } + }, + "leadingComments": null + }, + "operator": ">", + "right": { + "type": "TypeCastExpression", + "start": 150, + "end": 155, + "loc": { + "start": { + "line": 5, + "column": 15 + }, + "end": { + "line": 5, + "column": 20 + } + }, + "expression": { + "type": "Identifier", + "start": 150, + "end": 152, + "loc": { + "start": { + "line": 5, + "column": 15 + }, + "end": { + "line": 5, + "column": 17 + }, + "identifierName": "fn" + }, + "name": "fn" + }, + "typeAnnotation": { + "type": "TypeAnnotation", + "start": 152, + "end": 155, + "loc": { + "start": { + "line": 5, + "column": 17 + }, + "end": { + "line": 5, + "column": 20 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 154, + "end": 155, + "loc": { + "start": { + "line": 5, + "column": 19 + }, + "end": { + "line": 5, + "column": 20 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 154, + "end": 155, + "loc": { + "start": { + "line": 5, + "column": 19 + }, + "end": { + "line": 5, + "column": 20 + }, + "identifierName": "T" + }, + "name": "T" + } + } + }, + "extra": { + "parenthesized": true, + "parenStart": 149 + } + }, + "leadingComments": null + }, + "typeAnnotation": { + "type": "TypeAnnotation", + "start": 156, + "end": 165, + "loc": { + "start": { + "line": 5, + "column": 21 + }, + "end": { + "line": 5, + "column": 30 + } + }, + "typeAnnotation": { + "type": "FunctionTypeAnnotation", + "start": 158, + "end": 165, + "loc": { + "start": { + "line": 5, + "column": 23 + }, + "end": { + "line": 5, + "column": 30 + } + }, + "params": [ + { + "type": "FunctionTypeParam", + "start": 158, + "end": 162, + "loc": { + "start": { + "line": 5, + "column": 23 + }, + "end": { + "line": 5, + "column": 27 + } + }, + "name": null, + "optional": false, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 158, + "end": 159, + "loc": { + "start": { + "line": 5, + "column": 23 + }, + "end": { + "line": 5, + "column": 24 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 158, + "end": 159, + "loc": { + "start": { + "line": 5, + "column": 23 + }, + "end": { + "line": 5, + "column": 24 + }, + "identifierName": "T" + }, + "name": "T" + } + } + } + ], + "rest": null, + "returnType": { + "type": "GenericTypeAnnotation", + "start": 163, + "end": 165, + "loc": { + "start": { + "line": 5, + "column": 28 + }, + "end": { + "line": 5, + "column": 30 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 163, + "end": 165, + "loc": { + "start": { + "line": 5, + "column": 28 + }, + "end": { + "line": 5, + "column": 30 + }, + "identifierName": "fn" + }, + "name": "fn" + } + }, + "typeParameters": null + } + }, + "leadingComments": null, + "extra": { + "parenthesized": true, + "parenStart": 135 + } + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " This looks A LOT like an async arrow function, but it isn't because", + "start": 25, + "end": 95, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 70 + } + } + }, + { + "type": "CommentLine", + "value": " T + U isn't a valid type parameter.", + "start": 96, + "end": 134, + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 38 + } + } + } + ] + } + ], + "directives": [] + }, + "comments": [ + { + "type": "CommentLine", + "value": " This looks A LOT like an async arrow function, but it isn't because", + "start": 25, + "end": 95, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 70 + } + } + }, + { + "type": "CommentLine", + "value": " T + U isn't a valid type parameter.", + "start": 96, + "end": 134, + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 38 + } + } + } + ] +} \ No newline at end of file diff --git a/packages/babylon/test/fixtures/flow/type-generics/async-arrow/actual.js b/packages/babylon/test/fixtures/flow/type-generics/async-arrow/actual.js new file mode 100644 index 0000000000..b621a6dad3 --- /dev/null +++ b/packages/babylon/test/fixtures/flow/type-generics/async-arrow/actual.js @@ -0,0 +1 @@ +async (fn: () => T) => fn; \ No newline at end of file diff --git a/packages/babylon/test/fixtures/flow/type-generics/async-arrow/expected.json b/packages/babylon/test/fixtures/flow/type-generics/async-arrow/expected.json new file mode 100644 index 0000000000..b4ae7a1225 --- /dev/null +++ b/packages/babylon/test/fixtures/flow/type-generics/async-arrow/expected.json @@ -0,0 +1,204 @@ +{ + "type": "File", + "start": 0, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 29 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 29 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 29 + } + }, + "expression": { + "type": "ArrowFunctionExpression", + "start": 0, + "end": 28, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 28 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": true, + "typeParameters": { + "type": "TypeParameterDeclaration", + "start": 6, + "end": 9, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 9 + } + }, + "params": [ + { + "type": "TypeParameter", + "start": 7, + "end": 8, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 8 + } + }, + "name": "T", + "variance": null + } + ] + }, + "params": [ + { + "type": "Identifier", + "start": 10, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 1, + "column": 21 + }, + "identifierName": "fn" + }, + "name": "fn", + "typeAnnotation": { + "type": "TypeAnnotation", + "start": 12, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 12 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "typeAnnotation": { + "type": "FunctionTypeAnnotation", + "start": 14, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "params": [], + "rest": null, + "returnType": { + "type": "GenericTypeAnnotation", + "start": 20, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 20, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 21 + }, + "identifierName": "T" + }, + "name": "T" + } + }, + "typeParameters": null + } + } + } + ], + "body": { + "type": "Identifier", + "start": 26, + "end": 28, + "loc": { + "start": { + "line": 1, + "column": 26 + }, + "end": { + "line": 1, + "column": 28 + }, + "identifierName": "fn" + }, + "name": "fn" + } + } + } + ], + "directives": [] + } +} \ No newline at end of file