From eaef8f8b752ce3c3fc97d36b4ec5973a6457c5c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Fri, 9 Jun 2017 01:44:50 +0200 Subject: [PATCH 1/8] Distinguish between ternary's : and arrow fn's return type --- src/plugins/flow.js | 123 +- src/tokenizer/state.js | 8 + .../regression/issue-58-ambiguous/actual.js | 4 + .../issue-58-ambiguous/options.json | 3 + .../regression/issue-58-failing/actual.js | 2 + .../regression/issue-58-failing/options.json | 3 + .../flow/regression/issue-58/actual.js | 29 + .../flow/regression/issue-58/expected.json | 4318 +++++++++++++++++ 8 files changed, 4488 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/flow/regression/issue-58-ambiguous/actual.js create mode 100644 test/fixtures/flow/regression/issue-58-ambiguous/options.json create mode 100644 test/fixtures/flow/regression/issue-58-failing/actual.js create mode 100644 test/fixtures/flow/regression/issue-58-failing/options.json create mode 100644 test/fixtures/flow/regression/issue-58/actual.js create mode 100644 test/fixtures/flow/regression/issue-58/expected.json diff --git a/src/plugins/flow.js b/src/plugins/flow.js index 5329fef875..e816537c73 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -1073,9 +1073,11 @@ export default (superClass: Class): Class => class extends super } parseConditional(expr: N.Expression, noIn: ?boolean, startPos: number, startLoc: Position, refNeedsArrowPos?: ?Pos): N.Expression { + if (!this.match(tt.question)) return expr; + // only do the expensive clone if there is a question mark // and if we come from inside parens - if (refNeedsArrowPos && this.match(tt.question)) { + if (refNeedsArrowPos) { const state = this.state.clone(); try { return super.parseConditional(expr, noIn, startPos, startLoc); @@ -1091,7 +1093,118 @@ export default (superClass: Class): Class => class extends super } } - return super.parseConditional(expr, noIn, startPos, startLoc); + this.expect(tt.question); + const state = this.state.clone(); + const node = this.startNodeAt(startPos, startLoc); + + let { + consequent, + failed, + // eslint-disable-next-line prefer-const + error: err1, + } = this.tryParseConditionalConsequent(); + + if (failed) { + const positions = this.getArrowLikeExpressionsPos(consequent); + if (positions && positions.length > 1) { + // if there are two or more possible correct ways of parsing, throw an + // error. + // e.g. Source: a ? (b): c => (d): e => f + // Result 1: a ? b : (c => ((d): e => f)) + // Result 2: a ? ((b): c => d) : (e => f) + + // some nested expressions have only one possible parsing, but the inner + // one "thinks" they are two. In this case, the error is handled by the + // outer expression. + // e.g. Source: a ? b ? (c) : d => (e) : f => g + // Correct: a ? (b ? c : (d => e)) : (f => g) + // "b..." could be: b ? c : (d => ((e): f => g)) + // or: b ? ((c): d => e) : (f => g) + + this.raise( + state.start, + "Ambiguous expression: wrap the arrow functions in parentheses to disambiguate." + ); + } + + this.state = state; + + // "positions" is undefined when parseMaybeConditional throws. That + // means either that an invalid lhs expression was parsed as a function + // parameter, or that the inner expression "thought" it had two or more + // possible ways of being parsed. We can safely assume that the error does + // not come from a nested conditional expression (and thus that it is at + // the starting position) because the it would have been handled by the + // parent one. + this.state.noArrowAt = positions ? positions[0] : this.state.start; + + let err2: ?Error; + ({ + consequent, + failed, + // eslint-disable-next-line prefer-const + error: err2, + } = this.tryParseConditionalConsequent()); + + if (failed && (err1 || err2)) { + throw err1 || err2; + } + } + + this.expect(tt.colon); + + node.test = expr; + node.consequent = consequent; + node.alternate = this.parseMaybeAssign(noIn); + + return this.finishNode(node, "ConditionalExpression"); + } + + tryParseConditionalConsequent(): { + consequent: ?N.Expression, + failed: boolean, + error: ?Error, + } { + let failed = false; + let consequent: N.Expression; + let error: Error; + + try { + consequent = this.parseMaybeAssign(); + failed = !this.match(tt.colon); + } catch (err) { + if (err instanceof SyntaxError) { + failed = true; + error = err; + } else { + // istanbul ignore next: no such error is expected + throw err; + } + } + + return { consequent, failed, error }; + } + + getArrowLikeExpressionsPos(node: ?N.Expression): ?number[] { + if (!node) return; + + const stack = [ node ]; + const start: number[] = []; + + while (stack.length !== 0) { + const node = stack.pop(); + if (node.type === "ArrowFunctionExpression") { + if (node.returnType && !node.typeParameters) { + start.push(node.start); + } + stack.push(node.body); + } else if (node.type === "ConditionalExpression") { + stack.push(node.consequent); + stack.push(node.alternate); + } + } + + return start; } parseParenItem(node: N.Expression, startPos: number, startLoc: Position): N.Expression { @@ -1569,4 +1682,10 @@ export default (superClass: Class): Class => class extends super shouldParseArrow(): boolean { return this.match(tt.colon) || super.shouldParseArrow(); } + + parseParenAndDistinguishExpression(canBeArrow: boolean): N.Expression { + return super.parseParenAndDistinguishExpression( + canBeArrow && this.state.noArrowAt !== this.state.start + ); + } }; diff --git a/src/tokenizer/state.js b/src/tokenizer/state.js index cb1fedf74c..736655c5c5 100644 --- a/src/tokenizer/state.js +++ b/src/tokenizer/state.js @@ -18,6 +18,8 @@ export default class State { this.potentialArrowAt = -1; + this.noArrowAt = -1; + this.inMethod = this.inFunction = this.inGenerator = @@ -73,6 +75,12 @@ export default class State { // Used to signify the start of a potential arrow function potentialArrowAt: number; + // Used to signify the start of an expression which looks like a + // typed arrow function, but it isn't + // e.g. a ? (b) : c => d + // ^ + noArrowAt: number; + // Flags to track whether we are in a function, a generator. inFunction: boolean; inGenerator: boolean; diff --git a/test/fixtures/flow/regression/issue-58-ambiguous/actual.js b/test/fixtures/flow/regression/issue-58-ambiguous/actual.js new file mode 100644 index 0000000000..4f0e1d4e5c --- /dev/null +++ b/test/fixtures/flow/regression/issue-58-ambiguous/actual.js @@ -0,0 +1,4 @@ +// This can be parsed in two ways: +// a ? b : (c => ((d): e => f)) +// a ? ((b): c => d) : (e => f) +a ? (b) : c => (d) : e => f; diff --git a/test/fixtures/flow/regression/issue-58-ambiguous/options.json b/test/fixtures/flow/regression/issue-58-ambiguous/options.json new file mode 100644 index 0000000000..b1f344213d --- /dev/null +++ b/test/fixtures/flow/regression/issue-58-ambiguous/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Ambiguous expression: wrap the arrow functions in parentheses to disambiguate. (4:4)" +} \ No newline at end of file diff --git a/test/fixtures/flow/regression/issue-58-failing/actual.js b/test/fixtures/flow/regression/issue-58-failing/actual.js new file mode 100644 index 0000000000..eb019e2916 --- /dev/null +++ b/test/fixtures/flow/regression/issue-58-failing/actual.js @@ -0,0 +1,2 @@ +// Function which looks like a return type +a ? (b) : (c => d) => e; \ No newline at end of file diff --git a/test/fixtures/flow/regression/issue-58-failing/options.json b/test/fixtures/flow/regression/issue-58-failing/options.json new file mode 100644 index 0000000000..fcc81471ca --- /dev/null +++ b/test/fixtures/flow/regression/issue-58-failing/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Invalid left-hand side in arrow function parameters (2:11)" +} \ No newline at end of file diff --git a/test/fixtures/flow/regression/issue-58/actual.js b/test/fixtures/flow/regression/issue-58/actual.js new file mode 100644 index 0000000000..38526da827 --- /dev/null +++ b/test/fixtures/flow/regression/issue-58/actual.js @@ -0,0 +1,29 @@ +// Valid lhs value inside parentheses +a ? (b) : c => d; // a ? b : (c => d) +a ? (b) : c => d : e; // a ? ((b): c => d) : e +a ? (b) : (c) : d => e; // a ? b : ((c): d => e) + +// Nested arrow function inside parentheses +a ? (b = (c) => d) : e => f; // a ? (b = (c) => d) : (e => f) +a ? (b = (c) => d) : e => f : g; // a ? ((b = (c) => d): e => f) : g + +// Nested conditional expressions + b ? c ? (d) : e => (f) : g : h; // b ? (c ? ((d): e => f) : g) : h +a ? b ? c ? (d) : e => (f) : g : h; // a ? (b ? (c ? d : (e => f)) : g) : h + +a ? b ? (c) : (d) : (e) => f : g; // a ? (b ? c : ((d): e => f)) : g + +// Multiple arrow functions +a ? (b) : c => d : (e) : f => g; // a ? ((b): c => d) : ((e): f => g) + +// Multiple nested arrow functions ( is needed to avoid ambiguities) +a ? (b) : c => (d) : e => f : g; // a ? ((b): c => ((d): e => f)) : g +a ? (b) : c => (d) : e => f; // a ? b : (c => ((d): e => f)) +a ? (b) : c => (d) : e => f; // a ? ((b): c => d) : (e => f) + +// Invalid lhs value inside parentheses +a ? (b => c) : d => e; // a ? (b => c) : (d => e) +a ? b ? (c => d) : e => f : g; // a ? (b ? (c => d) : (e => f)) : g + +// Function as type annotation +a ? (b) : (c => d) => e : f; // a ? ((b): (c => d) => e) : f diff --git a/test/fixtures/flow/regression/issue-58/expected.json b/test/fixtures/flow/regression/issue-58/expected.json new file mode 100644 index 0000000000..d0e27af175 --- /dev/null +++ b/test/fixtures/flow/regression/issue-58/expected.json @@ -0,0 +1,4318 @@ +{ + "type": "File", + "start": 0, + "end": 1227, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 29, + "column": 60 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 1227, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 29, + "column": 60 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ExpressionStatement", + "start": 38, + "end": 55, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 17 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 38, + "end": 54, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 16 + } + }, + "test": { + "type": "Identifier", + "start": 38, + "end": 39, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "Identifier", + "start": 43, + "end": 44, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 6 + }, + "identifierName": "b" + }, + "name": "b", + "extra": { + "parenthesized": true, + "parenStart": 42 + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 48, + "end": 54, + "loc": { + "start": { + "line": 2, + "column": 10 + }, + "end": { + "line": 2, + "column": 16 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 48, + "end": 49, + "loc": { + "start": { + "line": 2, + "column": 10 + }, + "end": { + "line": 2, + "column": 11 + }, + "identifierName": "c" + }, + "name": "c" + } + ], + "body": { + "type": "Identifier", + "start": 53, + "end": 54, + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 16 + }, + "identifierName": "d" + }, + "name": "d" + } + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " Valid lhs value inside parentheses", + "start": 0, + "end": 37, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 37 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? b : (c => d)", + "start": 56, + "end": 75, + "loc": { + "start": { + "line": 2, + "column": 18 + }, + "end": { + "line": 2, + "column": 37 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 76, + "end": 97, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 21 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 76, + "end": 96, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 20 + } + }, + "test": { + "type": "Identifier", + "start": 76, + "end": 77, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 80, + "end": 92, + "loc": { + "start": { + "line": 3, + "column": 4 + }, + "end": { + "line": 3, + "column": 16 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 84, + "end": 87, + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 11 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 86, + "end": 87, + "loc": { + "start": { + "line": 3, + "column": 10 + }, + "end": { + "line": 3, + "column": 11 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 86, + "end": 87, + "loc": { + "start": { + "line": 3, + "column": 10 + }, + "end": { + "line": 3, + "column": 11 + }, + "identifierName": "c" + }, + "name": "c" + } + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 81, + "end": 82, + "loc": { + "start": { + "line": 3, + "column": 5 + }, + "end": { + "line": 3, + "column": 6 + }, + "identifierName": "b" + }, + "name": "b" + } + ], + "body": { + "type": "Identifier", + "start": 91, + "end": 92, + "loc": { + "start": { + "line": 3, + "column": 15 + }, + "end": { + "line": 3, + "column": 16 + }, + "identifierName": "d" + }, + "name": "d" + } + }, + "alternate": { + "type": "Identifier", + "start": 95, + "end": 96, + "loc": { + "start": { + "line": 3, + "column": 19 + }, + "end": { + "line": 3, + "column": 20 + }, + "identifierName": "e" + }, + "name": "e" + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? b : (c => d)", + "start": 56, + "end": 75, + "loc": { + "start": { + "line": 2, + "column": 18 + }, + "end": { + "line": 2, + "column": 37 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b): c => d) : e", + "start": 98, + "end": 122, + "loc": { + "start": { + "line": 3, + "column": 22 + }, + "end": { + "line": 3, + "column": 46 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 123, + "end": 146, + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 23 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 123, + "end": 145, + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 22 + } + }, + "test": { + "type": "Identifier", + "start": 123, + "end": 124, + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "Identifier", + "start": 128, + "end": 129, + "loc": { + "start": { + "line": 4, + "column": 5 + }, + "end": { + "line": 4, + "column": 6 + }, + "identifierName": "b" + }, + "name": "b", + "extra": { + "parenthesized": true, + "parenStart": 127 + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 133, + "end": 145, + "loc": { + "start": { + "line": 4, + "column": 10 + }, + "end": { + "line": 4, + "column": 22 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 137, + "end": 140, + "loc": { + "start": { + "line": 4, + "column": 14 + }, + "end": { + "line": 4, + "column": 17 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 139, + "end": 140, + "loc": { + "start": { + "line": 4, + "column": 16 + }, + "end": { + "line": 4, + "column": 17 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 139, + "end": 140, + "loc": { + "start": { + "line": 4, + "column": 16 + }, + "end": { + "line": 4, + "column": 17 + }, + "identifierName": "d" + }, + "name": "d" + } + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 134, + "end": 135, + "loc": { + "start": { + "line": 4, + "column": 11 + }, + "end": { + "line": 4, + "column": 12 + }, + "identifierName": "c" + }, + "name": "c" + } + ], + "body": { + "type": "Identifier", + "start": 144, + "end": 145, + "loc": { + "start": { + "line": 4, + "column": 21 + }, + "end": { + "line": 4, + "column": 22 + }, + "identifierName": "e" + }, + "name": "e" + } + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b): c => d) : e", + "start": 98, + "end": 122, + "loc": { + "start": { + "line": 3, + "column": 22 + }, + "end": { + "line": 3, + "column": 46 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? b : ((c): d => e)", + "start": 147, + "end": 171, + "loc": { + "start": { + "line": 4, + "column": 24 + }, + "end": { + "line": 4, + "column": 48 + } + } + }, + { + "type": "CommentLine", + "value": " Nested arrow function inside parentheses", + "start": 173, + "end": 216, + "loc": { + "start": { + "line": 6, + "column": 0 + }, + "end": { + "line": 6, + "column": 43 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 217, + "end": 245, + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 7, + "column": 28 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 217, + "end": 244, + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 7, + "column": 27 + } + }, + "test": { + "type": "Identifier", + "start": 217, + "end": 218, + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 7, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "AssignmentExpression", + "start": 222, + "end": 234, + "loc": { + "start": { + "line": 7, + "column": 5 + }, + "end": { + "line": 7, + "column": 17 + } + }, + "operator": "=", + "left": { + "type": "Identifier", + "start": 222, + "end": 223, + "loc": { + "start": { + "line": 7, + "column": 5 + }, + "end": { + "line": 7, + "column": 6 + }, + "identifierName": "b" + }, + "name": "b" + }, + "right": { + "type": "ArrowFunctionExpression", + "start": 226, + "end": 234, + "loc": { + "start": { + "line": 7, + "column": 9 + }, + "end": { + "line": 7, + "column": 17 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 227, + "end": 228, + "loc": { + "start": { + "line": 7, + "column": 10 + }, + "end": { + "line": 7, + "column": 11 + }, + "identifierName": "c" + }, + "name": "c" + } + ], + "body": { + "type": "Identifier", + "start": 233, + "end": 234, + "loc": { + "start": { + "line": 7, + "column": 16 + }, + "end": { + "line": 7, + "column": 17 + }, + "identifierName": "d" + }, + "name": "d" + } + }, + "extra": { + "parenthesized": true, + "parenStart": 221 + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 238, + "end": 244, + "loc": { + "start": { + "line": 7, + "column": 21 + }, + "end": { + "line": 7, + "column": 27 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 238, + "end": 239, + "loc": { + "start": { + "line": 7, + "column": 21 + }, + "end": { + "line": 7, + "column": 22 + }, + "identifierName": "e" + }, + "name": "e" + } + ], + "body": { + "type": "Identifier", + "start": 243, + "end": 244, + "loc": { + "start": { + "line": 7, + "column": 26 + }, + "end": { + "line": 7, + "column": 27 + }, + "identifierName": "f" + }, + "name": "f" + } + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? b : ((c): d => e)", + "start": 147, + "end": 171, + "loc": { + "start": { + "line": 4, + "column": 24 + }, + "end": { + "line": 4, + "column": 48 + } + } + }, + { + "type": "CommentLine", + "value": " Nested arrow function inside parentheses", + "start": 173, + "end": 216, + "loc": { + "start": { + "line": 6, + "column": 0 + }, + "end": { + "line": 6, + "column": 43 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? (b = (c) => d) : (e => f)", + "start": 246, + "end": 278, + "loc": { + "start": { + "line": 7, + "column": 29 + }, + "end": { + "line": 7, + "column": 61 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 279, + "end": 311, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 32 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 279, + "end": 310, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 31 + } + }, + "test": { + "type": "Identifier", + "start": 279, + "end": 280, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 283, + "end": 306, + "loc": { + "start": { + "line": 8, + "column": 4 + }, + "end": { + "line": 8, + "column": 27 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 298, + "end": 301, + "loc": { + "start": { + "line": 8, + "column": 19 + }, + "end": { + "line": 8, + "column": 22 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 300, + "end": 301, + "loc": { + "start": { + "line": 8, + "column": 21 + }, + "end": { + "line": 8, + "column": 22 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 300, + "end": 301, + "loc": { + "start": { + "line": 8, + "column": 21 + }, + "end": { + "line": 8, + "column": 22 + }, + "identifierName": "e" + }, + "name": "e" + } + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "AssignmentPattern", + "start": 284, + "end": 296, + "loc": { + "start": { + "line": 8, + "column": 5 + }, + "end": { + "line": 8, + "column": 17 + } + }, + "left": { + "type": "Identifier", + "start": 284, + "end": 285, + "loc": { + "start": { + "line": 8, + "column": 5 + }, + "end": { + "line": 8, + "column": 6 + }, + "identifierName": "b" + }, + "name": "b" + }, + "right": { + "type": "ArrowFunctionExpression", + "start": 288, + "end": 296, + "loc": { + "start": { + "line": 8, + "column": 9 + }, + "end": { + "line": 8, + "column": 17 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 289, + "end": 290, + "loc": { + "start": { + "line": 8, + "column": 10 + }, + "end": { + "line": 8, + "column": 11 + }, + "identifierName": "c" + }, + "name": "c" + } + ], + "body": { + "type": "Identifier", + "start": 295, + "end": 296, + "loc": { + "start": { + "line": 8, + "column": 16 + }, + "end": { + "line": 8, + "column": 17 + }, + "identifierName": "d" + }, + "name": "d" + } + } + } + ], + "body": { + "type": "Identifier", + "start": 305, + "end": 306, + "loc": { + "start": { + "line": 8, + "column": 26 + }, + "end": { + "line": 8, + "column": 27 + }, + "identifierName": "f" + }, + "name": "f" + } + }, + "alternate": { + "type": "Identifier", + "start": 309, + "end": 310, + "loc": { + "start": { + "line": 8, + "column": 30 + }, + "end": { + "line": 8, + "column": 31 + }, + "identifierName": "g" + }, + "name": "g" + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? (b = (c) => d) : (e => f)", + "start": 246, + "end": 278, + "loc": { + "start": { + "line": 7, + "column": 29 + }, + "end": { + "line": 7, + "column": 61 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b = (c) => d): e => f) : g", + "start": 312, + "end": 347, + "loc": { + "start": { + "line": 8, + "column": 33 + }, + "end": { + "line": 8, + "column": 68 + } + } + }, + { + "type": "CommentLine", + "value": " Nested conditional expressions", + "start": 349, + "end": 382, + "loc": { + "start": { + "line": 10, + "column": 0 + }, + "end": { + "line": 10, + "column": 33 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 387, + "end": 418, + "loc": { + "start": { + "line": 11, + "column": 4 + }, + "end": { + "line": 11, + "column": 35 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 387, + "end": 417, + "loc": { + "start": { + "line": 11, + "column": 4 + }, + "end": { + "line": 11, + "column": 34 + } + }, + "test": { + "type": "Identifier", + "start": 387, + "end": 388, + "loc": { + "start": { + "line": 11, + "column": 4 + }, + "end": { + "line": 11, + "column": 5 + }, + "identifierName": "b" + }, + "name": "b", + "leadingComments": null + }, + "consequent": { + "type": "ConditionalExpression", + "start": 391, + "end": 413, + "loc": { + "start": { + "line": 11, + "column": 8 + }, + "end": { + "line": 11, + "column": 30 + } + }, + "test": { + "type": "Identifier", + "start": 391, + "end": 392, + "loc": { + "start": { + "line": 11, + "column": 8 + }, + "end": { + "line": 11, + "column": 9 + }, + "identifierName": "c" + }, + "name": "c" + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 395, + "end": 409, + "loc": { + "start": { + "line": 11, + "column": 12 + }, + "end": { + "line": 11, + "column": 26 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 399, + "end": 402, + "loc": { + "start": { + "line": 11, + "column": 16 + }, + "end": { + "line": 11, + "column": 19 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 401, + "end": 402, + "loc": { + "start": { + "line": 11, + "column": 18 + }, + "end": { + "line": 11, + "column": 19 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 401, + "end": 402, + "loc": { + "start": { + "line": 11, + "column": 18 + }, + "end": { + "line": 11, + "column": 19 + }, + "identifierName": "e" + }, + "name": "e" + } + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 396, + "end": 397, + "loc": { + "start": { + "line": 11, + "column": 13 + }, + "end": { + "line": 11, + "column": 14 + }, + "identifierName": "d" + }, + "name": "d" + } + ], + "body": { + "type": "Identifier", + "start": 407, + "end": 408, + "loc": { + "start": { + "line": 11, + "column": 24 + }, + "end": { + "line": 11, + "column": 25 + }, + "identifierName": "f" + }, + "name": "f", + "extra": { + "parenthesized": true, + "parenStart": 406 + } + } + }, + "alternate": { + "type": "Identifier", + "start": 412, + "end": 413, + "loc": { + "start": { + "line": 11, + "column": 29 + }, + "end": { + "line": 11, + "column": 30 + }, + "identifierName": "g" + }, + "name": "g" + } + }, + "alternate": { + "type": "Identifier", + "start": 416, + "end": 417, + "loc": { + "start": { + "line": 11, + "column": 33 + }, + "end": { + "line": 11, + "column": 34 + }, + "identifierName": "h" + }, + "name": "h" + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b = (c) => d): e => f) : g", + "start": 312, + "end": 347, + "loc": { + "start": { + "line": 8, + "column": 33 + }, + "end": { + "line": 8, + "column": 68 + } + } + }, + { + "type": "CommentLine", + "value": " Nested conditional expressions", + "start": 349, + "end": 382, + "loc": { + "start": { + "line": 10, + "column": 0 + }, + "end": { + "line": 10, + "column": 33 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " b ? (c ? ((d): e => f) : g) : h", + "start": 419, + "end": 453, + "loc": { + "start": { + "line": 11, + "column": 36 + }, + "end": { + "line": 11, + "column": 70 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 454, + "end": 489, + "loc": { + "start": { + "line": 12, + "column": 0 + }, + "end": { + "line": 12, + "column": 35 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 454, + "end": 488, + "loc": { + "start": { + "line": 12, + "column": 0 + }, + "end": { + "line": 12, + "column": 34 + } + }, + "test": { + "type": "Identifier", + "start": 454, + "end": 455, + "loc": { + "start": { + "line": 12, + "column": 0 + }, + "end": { + "line": 12, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ConditionalExpression", + "start": 458, + "end": 484, + "loc": { + "start": { + "line": 12, + "column": 4 + }, + "end": { + "line": 12, + "column": 30 + } + }, + "test": { + "type": "Identifier", + "start": 458, + "end": 459, + "loc": { + "start": { + "line": 12, + "column": 4 + }, + "end": { + "line": 12, + "column": 5 + }, + "identifierName": "b" + }, + "name": "b" + }, + "consequent": { + "type": "ConditionalExpression", + "start": 462, + "end": 480, + "loc": { + "start": { + "line": 12, + "column": 8 + }, + "end": { + "line": 12, + "column": 26 + } + }, + "test": { + "type": "Identifier", + "start": 462, + "end": 463, + "loc": { + "start": { + "line": 12, + "column": 8 + }, + "end": { + "line": 12, + "column": 9 + }, + "identifierName": "c" + }, + "name": "c" + }, + "consequent": { + "type": "Identifier", + "start": 467, + "end": 468, + "loc": { + "start": { + "line": 12, + "column": 13 + }, + "end": { + "line": 12, + "column": 14 + }, + "identifierName": "d" + }, + "name": "d", + "extra": { + "parenthesized": true, + "parenStart": 466 + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 472, + "end": 480, + "loc": { + "start": { + "line": 12, + "column": 18 + }, + "end": { + "line": 12, + "column": 26 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 472, + "end": 473, + "loc": { + "start": { + "line": 12, + "column": 18 + }, + "end": { + "line": 12, + "column": 19 + }, + "identifierName": "e" + }, + "name": "e" + } + ], + "body": { + "type": "Identifier", + "start": 478, + "end": 479, + "loc": { + "start": { + "line": 12, + "column": 24 + }, + "end": { + "line": 12, + "column": 25 + }, + "identifierName": "f" + }, + "name": "f", + "extra": { + "parenthesized": true, + "parenStart": 477 + } + } + } + }, + "alternate": { + "type": "Identifier", + "start": 483, + "end": 484, + "loc": { + "start": { + "line": 12, + "column": 29 + }, + "end": { + "line": 12, + "column": 30 + }, + "identifierName": "g" + }, + "name": "g" + } + }, + "alternate": { + "type": "Identifier", + "start": 487, + "end": 488, + "loc": { + "start": { + "line": 12, + "column": 33 + }, + "end": { + "line": 12, + "column": 34 + }, + "identifierName": "h" + }, + "name": "h" + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " b ? (c ? ((d): e => f) : g) : h", + "start": 419, + "end": 453, + "loc": { + "start": { + "line": 11, + "column": 36 + }, + "end": { + "line": 11, + "column": 70 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? (b ? (c ? d : (e => f)) : g) : h", + "start": 490, + "end": 529, + "loc": { + "start": { + "line": 12, + "column": 36 + }, + "end": { + "line": 12, + "column": 75 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 531, + "end": 564, + "loc": { + "start": { + "line": 14, + "column": 0 + }, + "end": { + "line": 14, + "column": 33 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 531, + "end": 563, + "loc": { + "start": { + "line": 14, + "column": 0 + }, + "end": { + "line": 14, + "column": 32 + } + }, + "test": { + "type": "Identifier", + "start": 531, + "end": 532, + "loc": { + "start": { + "line": 14, + "column": 0 + }, + "end": { + "line": 14, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ConditionalExpression", + "start": 535, + "end": 559, + "loc": { + "start": { + "line": 14, + "column": 4 + }, + "end": { + "line": 14, + "column": 28 + } + }, + "test": { + "type": "Identifier", + "start": 535, + "end": 536, + "loc": { + "start": { + "line": 14, + "column": 4 + }, + "end": { + "line": 14, + "column": 5 + }, + "identifierName": "b" + }, + "name": "b" + }, + "consequent": { + "type": "Identifier", + "start": 540, + "end": 541, + "loc": { + "start": { + "line": 14, + "column": 9 + }, + "end": { + "line": 14, + "column": 10 + }, + "identifierName": "c" + }, + "name": "c", + "extra": { + "parenthesized": true, + "parenStart": 539 + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 545, + "end": 559, + "loc": { + "start": { + "line": 14, + "column": 14 + }, + "end": { + "line": 14, + "column": 28 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 549, + "end": 554, + "loc": { + "start": { + "line": 14, + "column": 18 + }, + "end": { + "line": 14, + "column": 23 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 552, + "end": 553, + "loc": { + "start": { + "line": 14, + "column": 21 + }, + "end": { + "line": 14, + "column": 22 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 552, + "end": 553, + "loc": { + "start": { + "line": 14, + "column": 21 + }, + "end": { + "line": 14, + "column": 22 + }, + "identifierName": "e" + }, + "name": "e" + } + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 546, + "end": 547, + "loc": { + "start": { + "line": 14, + "column": 15 + }, + "end": { + "line": 14, + "column": 16 + }, + "identifierName": "d" + }, + "name": "d" + } + ], + "body": { + "type": "Identifier", + "start": 558, + "end": 559, + "loc": { + "start": { + "line": 14, + "column": 27 + }, + "end": { + "line": 14, + "column": 28 + }, + "identifierName": "f" + }, + "name": "f" + } + } + }, + "alternate": { + "type": "Identifier", + "start": 562, + "end": 563, + "loc": { + "start": { + "line": 14, + "column": 31 + }, + "end": { + "line": 14, + "column": 32 + }, + "identifierName": "g" + }, + "name": "g" + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? (b ? (c ? d : (e => f)) : g) : h", + "start": 490, + "end": 529, + "loc": { + "start": { + "line": 12, + "column": 36 + }, + "end": { + "line": 12, + "column": 75 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? (b ? c : ((d): e => f)) : g", + "start": 565, + "end": 599, + "loc": { + "start": { + "line": 14, + "column": 34 + }, + "end": { + "line": 14, + "column": 68 + } + } + }, + { + "type": "CommentLine", + "value": " Multiple arrow functions", + "start": 601, + "end": 628, + "loc": { + "start": { + "line": 16, + "column": 0 + }, + "end": { + "line": 16, + "column": 27 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 629, + "end": 661, + "loc": { + "start": { + "line": 17, + "column": 0 + }, + "end": { + "line": 17, + "column": 32 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 629, + "end": 660, + "loc": { + "start": { + "line": 17, + "column": 0 + }, + "end": { + "line": 17, + "column": 31 + } + }, + "test": { + "type": "Identifier", + "start": 629, + "end": 630, + "loc": { + "start": { + "line": 17, + "column": 0 + }, + "end": { + "line": 17, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 633, + "end": 645, + "loc": { + "start": { + "line": 17, + "column": 4 + }, + "end": { + "line": 17, + "column": 16 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 637, + "end": 640, + "loc": { + "start": { + "line": 17, + "column": 8 + }, + "end": { + "line": 17, + "column": 11 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 639, + "end": 640, + "loc": { + "start": { + "line": 17, + "column": 10 + }, + "end": { + "line": 17, + "column": 11 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 639, + "end": 640, + "loc": { + "start": { + "line": 17, + "column": 10 + }, + "end": { + "line": 17, + "column": 11 + }, + "identifierName": "c" + }, + "name": "c" + } + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 634, + "end": 635, + "loc": { + "start": { + "line": 17, + "column": 5 + }, + "end": { + "line": 17, + "column": 6 + }, + "identifierName": "b" + }, + "name": "b" + } + ], + "body": { + "type": "Identifier", + "start": 644, + "end": 645, + "loc": { + "start": { + "line": 17, + "column": 15 + }, + "end": { + "line": 17, + "column": 16 + }, + "identifierName": "d" + }, + "name": "d" + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 648, + "end": 660, + "loc": { + "start": { + "line": 17, + "column": 19 + }, + "end": { + "line": 17, + "column": 31 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 652, + "end": 655, + "loc": { + "start": { + "line": 17, + "column": 23 + }, + "end": { + "line": 17, + "column": 26 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 654, + "end": 655, + "loc": { + "start": { + "line": 17, + "column": 25 + }, + "end": { + "line": 17, + "column": 26 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 654, + "end": 655, + "loc": { + "start": { + "line": 17, + "column": 25 + }, + "end": { + "line": 17, + "column": 26 + }, + "identifierName": "f" + }, + "name": "f" + } + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 649, + "end": 650, + "loc": { + "start": { + "line": 17, + "column": 20 + }, + "end": { + "line": 17, + "column": 21 + }, + "identifierName": "e" + }, + "name": "e" + } + ], + "body": { + "type": "Identifier", + "start": 659, + "end": 660, + "loc": { + "start": { + "line": 17, + "column": 30 + }, + "end": { + "line": 17, + "column": 31 + }, + "identifierName": "g" + }, + "name": "g" + } + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? (b ? c : ((d): e => f)) : g", + "start": 565, + "end": 599, + "loc": { + "start": { + "line": 14, + "column": 34 + }, + "end": { + "line": 14, + "column": 68 + } + } + }, + { + "type": "CommentLine", + "value": " Multiple arrow functions", + "start": 601, + "end": 628, + "loc": { + "start": { + "line": 16, + "column": 0 + }, + "end": { + "line": 16, + "column": 27 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b): c => d) : ((e): f => g)", + "start": 662, + "end": 698, + "loc": { + "start": { + "line": 17, + "column": 33 + }, + "end": { + "line": 17, + "column": 69 + } + } + }, + { + "type": "CommentLine", + "value": " Multiple nested arrow functions ( is needed to avoid ambiguities)", + "start": 700, + "end": 771, + "loc": { + "start": { + "line": 19, + "column": 0 + }, + "end": { + "line": 19, + "column": 71 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 772, + "end": 804, + "loc": { + "start": { + "line": 20, + "column": 0 + }, + "end": { + "line": 20, + "column": 32 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 772, + "end": 803, + "loc": { + "start": { + "line": 20, + "column": 0 + }, + "end": { + "line": 20, + "column": 31 + } + }, + "test": { + "type": "Identifier", + "start": 772, + "end": 773, + "loc": { + "start": { + "line": 20, + "column": 0 + }, + "end": { + "line": 20, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 776, + "end": 799, + "loc": { + "start": { + "line": 20, + "column": 4 + }, + "end": { + "line": 20, + "column": 27 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 780, + "end": 783, + "loc": { + "start": { + "line": 20, + "column": 8 + }, + "end": { + "line": 20, + "column": 11 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 782, + "end": 783, + "loc": { + "start": { + "line": 20, + "column": 10 + }, + "end": { + "line": 20, + "column": 11 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 782, + "end": 783, + "loc": { + "start": { + "line": 20, + "column": 10 + }, + "end": { + "line": 20, + "column": 11 + }, + "identifierName": "c" + }, + "name": "c" + } + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 777, + "end": 778, + "loc": { + "start": { + "line": 20, + "column": 5 + }, + "end": { + "line": 20, + "column": 6 + }, + "identifierName": "b" + }, + "name": "b" + } + ], + "body": { + "type": "ArrowFunctionExpression", + "start": 787, + "end": 799, + "loc": { + "start": { + "line": 20, + "column": 15 + }, + "end": { + "line": 20, + "column": 27 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 791, + "end": 794, + "loc": { + "start": { + "line": 20, + "column": 19 + }, + "end": { + "line": 20, + "column": 22 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 793, + "end": 794, + "loc": { + "start": { + "line": 20, + "column": 21 + }, + "end": { + "line": 20, + "column": 22 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 793, + "end": 794, + "loc": { + "start": { + "line": 20, + "column": 21 + }, + "end": { + "line": 20, + "column": 22 + }, + "identifierName": "e" + }, + "name": "e" + } + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 788, + "end": 789, + "loc": { + "start": { + "line": 20, + "column": 16 + }, + "end": { + "line": 20, + "column": 17 + }, + "identifierName": "d" + }, + "name": "d" + } + ], + "body": { + "type": "Identifier", + "start": 798, + "end": 799, + "loc": { + "start": { + "line": 20, + "column": 26 + }, + "end": { + "line": 20, + "column": 27 + }, + "identifierName": "f" + }, + "name": "f" + } + } + }, + "alternate": { + "type": "Identifier", + "start": 802, + "end": 803, + "loc": { + "start": { + "line": 20, + "column": 30 + }, + "end": { + "line": 20, + "column": 31 + }, + "identifierName": "g" + }, + "name": "g" + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b): c => d) : ((e): f => g)", + "start": 662, + "end": 698, + "loc": { + "start": { + "line": 17, + "column": 33 + }, + "end": { + "line": 17, + "column": 69 + } + } + }, + { + "type": "CommentLine", + "value": " Multiple nested arrow functions ( is needed to avoid ambiguities)", + "start": 700, + "end": 771, + "loc": { + "start": { + "line": 19, + "column": 0 + }, + "end": { + "line": 19, + "column": 71 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b): c => ((d): e => f)) : g", + "start": 805, + "end": 841, + "loc": { + "start": { + "line": 20, + "column": 33 + }, + "end": { + "line": 20, + "column": 69 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 842, + "end": 873, + "loc": { + "start": { + "line": 21, + "column": 0 + }, + "end": { + "line": 21, + "column": 31 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 842, + "end": 872, + "loc": { + "start": { + "line": 21, + "column": 0 + }, + "end": { + "line": 21, + "column": 30 + } + }, + "test": { + "type": "Identifier", + "start": 842, + "end": 843, + "loc": { + "start": { + "line": 21, + "column": 0 + }, + "end": { + "line": 21, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "Identifier", + "start": 847, + "end": 848, + "loc": { + "start": { + "line": 21, + "column": 5 + }, + "end": { + "line": 21, + "column": 6 + }, + "identifierName": "b" + }, + "name": "b", + "extra": { + "parenthesized": true, + "parenStart": 846 + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 852, + "end": 872, + "loc": { + "start": { + "line": 21, + "column": 10 + }, + "end": { + "line": 21, + "column": 30 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 852, + "end": 853, + "loc": { + "start": { + "line": 21, + "column": 10 + }, + "end": { + "line": 21, + "column": 11 + }, + "identifierName": "c" + }, + "name": "c" + } + ], + "body": { + "type": "ArrowFunctionExpression", + "start": 857, + "end": 872, + "loc": { + "start": { + "line": 21, + "column": 15 + }, + "end": { + "line": 21, + "column": 30 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 864, + "end": 867, + "loc": { + "start": { + "line": 21, + "column": 22 + }, + "end": { + "line": 21, + "column": 25 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 866, + "end": 867, + "loc": { + "start": { + "line": 21, + "column": 24 + }, + "end": { + "line": 21, + "column": 25 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 866, + "end": 867, + "loc": { + "start": { + "line": 21, + "column": 24 + }, + "end": { + "line": 21, + "column": 25 + }, + "identifierName": "e" + }, + "name": "e" + } + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 861, + "end": 862, + "loc": { + "start": { + "line": 21, + "column": 19 + }, + "end": { + "line": 21, + "column": 20 + }, + "identifierName": "d" + }, + "name": "d" + } + ], + "body": { + "type": "Identifier", + "start": 871, + "end": 872, + "loc": { + "start": { + "line": 21, + "column": 29 + }, + "end": { + "line": 21, + "column": 30 + }, + "identifierName": "f" + }, + "name": "f" + }, + "typeParameters": { + "type": "TypeParameterDeclaration", + "start": 857, + "end": 860, + "loc": { + "start": { + "line": 21, + "column": 15 + }, + "end": { + "line": 21, + "column": 18 + } + }, + "params": [ + { + "type": "TypeParameter", + "start": 858, + "end": 859, + "loc": { + "start": { + "line": 21, + "column": 16 + }, + "end": { + "line": 21, + "column": 17 + } + }, + "name": "T", + "variance": null + } + ] + } + } + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b): c => ((d): e => f)) : g", + "start": 805, + "end": 841, + "loc": { + "start": { + "line": 20, + "column": 33 + }, + "end": { + "line": 20, + "column": 69 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? b : (c => ((d): e => f))", + "start": 874, + "end": 908, + "loc": { + "start": { + "line": 21, + "column": 32 + }, + "end": { + "line": 21, + "column": 66 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 909, + "end": 940, + "loc": { + "start": { + "line": 22, + "column": 0 + }, + "end": { + "line": 22, + "column": 31 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 909, + "end": 939, + "loc": { + "start": { + "line": 22, + "column": 0 + }, + "end": { + "line": 22, + "column": 30 + } + }, + "test": { + "type": "Identifier", + "start": 909, + "end": 910, + "loc": { + "start": { + "line": 22, + "column": 0 + }, + "end": { + "line": 22, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 913, + "end": 930, + "loc": { + "start": { + "line": 22, + "column": 4 + }, + "end": { + "line": 22, + "column": 21 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 920, + "end": 923, + "loc": { + "start": { + "line": 22, + "column": 11 + }, + "end": { + "line": 22, + "column": 14 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 922, + "end": 923, + "loc": { + "start": { + "line": 22, + "column": 13 + }, + "end": { + "line": 22, + "column": 14 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 922, + "end": 923, + "loc": { + "start": { + "line": 22, + "column": 13 + }, + "end": { + "line": 22, + "column": 14 + }, + "identifierName": "c" + }, + "name": "c" + } + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 917, + "end": 918, + "loc": { + "start": { + "line": 22, + "column": 8 + }, + "end": { + "line": 22, + "column": 9 + }, + "identifierName": "b" + }, + "name": "b" + } + ], + "body": { + "type": "Identifier", + "start": 928, + "end": 929, + "loc": { + "start": { + "line": 22, + "column": 19 + }, + "end": { + "line": 22, + "column": 20 + }, + "identifierName": "d" + }, + "name": "d", + "extra": { + "parenthesized": true, + "parenStart": 927 + } + }, + "typeParameters": { + "type": "TypeParameterDeclaration", + "start": 913, + "end": 916, + "loc": { + "start": { + "line": 22, + "column": 4 + }, + "end": { + "line": 22, + "column": 7 + } + }, + "params": [ + { + "type": "TypeParameter", + "start": 914, + "end": 915, + "loc": { + "start": { + "line": 22, + "column": 5 + }, + "end": { + "line": 22, + "column": 6 + } + }, + "name": "T", + "variance": null + } + ] + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 933, + "end": 939, + "loc": { + "start": { + "line": 22, + "column": 24 + }, + "end": { + "line": 22, + "column": 30 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 933, + "end": 934, + "loc": { + "start": { + "line": 22, + "column": 24 + }, + "end": { + "line": 22, + "column": 25 + }, + "identifierName": "e" + }, + "name": "e" + } + ], + "body": { + "type": "Identifier", + "start": 938, + "end": 939, + "loc": { + "start": { + "line": 22, + "column": 29 + }, + "end": { + "line": 22, + "column": 30 + }, + "identifierName": "f" + }, + "name": "f" + } + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? b : (c => ((d): e => f))", + "start": 874, + "end": 908, + "loc": { + "start": { + "line": 21, + "column": 32 + }, + "end": { + "line": 21, + "column": 66 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b): c => d) : (e => f)", + "start": 941, + "end": 975, + "loc": { + "start": { + "line": 22, + "column": 32 + }, + "end": { + "line": 22, + "column": 66 + } + } + }, + { + "type": "CommentLine", + "value": " Invalid lhs value inside parentheses", + "start": 977, + "end": 1016, + "loc": { + "start": { + "line": 24, + "column": 0 + }, + "end": { + "line": 24, + "column": 39 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 1017, + "end": 1039, + "loc": { + "start": { + "line": 25, + "column": 0 + }, + "end": { + "line": 25, + "column": 22 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 1017, + "end": 1038, + "loc": { + "start": { + "line": 25, + "column": 0 + }, + "end": { + "line": 25, + "column": 21 + } + }, + "test": { + "type": "Identifier", + "start": 1017, + "end": 1018, + "loc": { + "start": { + "line": 25, + "column": 0 + }, + "end": { + "line": 25, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 1022, + "end": 1028, + "loc": { + "start": { + "line": 25, + "column": 5 + }, + "end": { + "line": 25, + "column": 11 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1022, + "end": 1023, + "loc": { + "start": { + "line": 25, + "column": 5 + }, + "end": { + "line": 25, + "column": 6 + }, + "identifierName": "b" + }, + "name": "b" + } + ], + "body": { + "type": "Identifier", + "start": 1027, + "end": 1028, + "loc": { + "start": { + "line": 25, + "column": 10 + }, + "end": { + "line": 25, + "column": 11 + }, + "identifierName": "c" + }, + "name": "c" + }, + "extra": { + "parenthesized": true, + "parenStart": 1021 + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 1032, + "end": 1038, + "loc": { + "start": { + "line": 25, + "column": 15 + }, + "end": { + "line": 25, + "column": 21 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1032, + "end": 1033, + "loc": { + "start": { + "line": 25, + "column": 15 + }, + "end": { + "line": 25, + "column": 16 + }, + "identifierName": "d" + }, + "name": "d" + } + ], + "body": { + "type": "Identifier", + "start": 1037, + "end": 1038, + "loc": { + "start": { + "line": 25, + "column": 20 + }, + "end": { + "line": 25, + "column": 21 + }, + "identifierName": "e" + }, + "name": "e" + } + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b): c => d) : (e => f)", + "start": 941, + "end": 975, + "loc": { + "start": { + "line": 22, + "column": 32 + }, + "end": { + "line": 22, + "column": 66 + } + } + }, + { + "type": "CommentLine", + "value": " Invalid lhs value inside parentheses", + "start": 977, + "end": 1016, + "loc": { + "start": { + "line": 24, + "column": 0 + }, + "end": { + "line": 24, + "column": 39 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? (b => c) : (d => e)", + "start": 1040, + "end": 1066, + "loc": { + "start": { + "line": 25, + "column": 23 + }, + "end": { + "line": 25, + "column": 49 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 1067, + "end": 1097, + "loc": { + "start": { + "line": 26, + "column": 0 + }, + "end": { + "line": 26, + "column": 30 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 1067, + "end": 1096, + "loc": { + "start": { + "line": 26, + "column": 0 + }, + "end": { + "line": 26, + "column": 29 + } + }, + "test": { + "type": "Identifier", + "start": 1067, + "end": 1068, + "loc": { + "start": { + "line": 26, + "column": 0 + }, + "end": { + "line": 26, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ConditionalExpression", + "start": 1071, + "end": 1092, + "loc": { + "start": { + "line": 26, + "column": 4 + }, + "end": { + "line": 26, + "column": 25 + } + }, + "test": { + "type": "Identifier", + "start": 1071, + "end": 1072, + "loc": { + "start": { + "line": 26, + "column": 4 + }, + "end": { + "line": 26, + "column": 5 + }, + "identifierName": "b" + }, + "name": "b" + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 1076, + "end": 1082, + "loc": { + "start": { + "line": 26, + "column": 9 + }, + "end": { + "line": 26, + "column": 15 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1076, + "end": 1077, + "loc": { + "start": { + "line": 26, + "column": 9 + }, + "end": { + "line": 26, + "column": 10 + }, + "identifierName": "c" + }, + "name": "c" + } + ], + "body": { + "type": "Identifier", + "start": 1081, + "end": 1082, + "loc": { + "start": { + "line": 26, + "column": 14 + }, + "end": { + "line": 26, + "column": 15 + }, + "identifierName": "d" + }, + "name": "d" + }, + "extra": { + "parenthesized": true, + "parenStart": 1075 + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 1086, + "end": 1092, + "loc": { + "start": { + "line": 26, + "column": 19 + }, + "end": { + "line": 26, + "column": 25 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1086, + "end": 1087, + "loc": { + "start": { + "line": 26, + "column": 19 + }, + "end": { + "line": 26, + "column": 20 + }, + "identifierName": "e" + }, + "name": "e" + } + ], + "body": { + "type": "Identifier", + "start": 1091, + "end": 1092, + "loc": { + "start": { + "line": 26, + "column": 24 + }, + "end": { + "line": 26, + "column": 25 + }, + "identifierName": "f" + }, + "name": "f" + } + } + }, + "alternate": { + "type": "Identifier", + "start": 1095, + "end": 1096, + "loc": { + "start": { + "line": 26, + "column": 28 + }, + "end": { + "line": 26, + "column": 29 + }, + "identifierName": "g" + }, + "name": "g" + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? (b => c) : (d => e)", + "start": 1040, + "end": 1066, + "loc": { + "start": { + "line": 25, + "column": 23 + }, + "end": { + "line": 25, + "column": 49 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? (b ? (c => d) : (e => f)) : g", + "start": 1098, + "end": 1134, + "loc": { + "start": { + "line": 26, + "column": 31 + }, + "end": { + "line": 26, + "column": 67 + } + } + }, + { + "type": "CommentLine", + "value": " Function as type annotation", + "start": 1136, + "end": 1166, + "loc": { + "start": { + "line": 28, + "column": 0 + }, + "end": { + "line": 28, + "column": 30 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 1167, + "end": 1195, + "loc": { + "start": { + "line": 29, + "column": 0 + }, + "end": { + "line": 29, + "column": 28 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 1167, + "end": 1194, + "loc": { + "start": { + "line": 29, + "column": 0 + }, + "end": { + "line": 29, + "column": 27 + } + }, + "test": { + "type": "Identifier", + "start": 1167, + "end": 1168, + "loc": { + "start": { + "line": 29, + "column": 0 + }, + "end": { + "line": 29, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 1171, + "end": 1190, + "loc": { + "start": { + "line": 29, + "column": 4 + }, + "end": { + "line": 29, + "column": 23 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 1175, + "end": 1185, + "loc": { + "start": { + "line": 29, + "column": 8 + }, + "end": { + "line": 29, + "column": 18 + } + }, + "typeAnnotation": { + "type": "FunctionTypeAnnotation", + "start": 1178, + "end": 1184, + "loc": { + "start": { + "line": 29, + "column": 11 + }, + "end": { + "line": 29, + "column": 17 + } + }, + "params": [ + { + "type": "FunctionTypeParam", + "start": 1178, + "end": 1182, + "loc": { + "start": { + "line": 29, + "column": 11 + }, + "end": { + "line": 29, + "column": 15 + } + }, + "name": null, + "optional": false, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 1178, + "end": 1179, + "loc": { + "start": { + "line": 29, + "column": 11 + }, + "end": { + "line": 29, + "column": 12 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 1178, + "end": 1179, + "loc": { + "start": { + "line": 29, + "column": 11 + }, + "end": { + "line": 29, + "column": 12 + }, + "identifierName": "c" + }, + "name": "c" + } + } + } + ], + "rest": null, + "returnType": { + "type": "GenericTypeAnnotation", + "start": 1183, + "end": 1184, + "loc": { + "start": { + "line": 29, + "column": 16 + }, + "end": { + "line": 29, + "column": 17 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 1183, + "end": 1184, + "loc": { + "start": { + "line": 29, + "column": 16 + }, + "end": { + "line": 29, + "column": 17 + }, + "identifierName": "d" + }, + "name": "d" + } + }, + "typeParameters": null + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1172, + "end": 1173, + "loc": { + "start": { + "line": 29, + "column": 5 + }, + "end": { + "line": 29, + "column": 6 + }, + "identifierName": "b" + }, + "name": "b" + } + ], + "body": { + "type": "Identifier", + "start": 1189, + "end": 1190, + "loc": { + "start": { + "line": 29, + "column": 22 + }, + "end": { + "line": 29, + "column": 23 + }, + "identifierName": "e" + }, + "name": "e" + } + }, + "alternate": { + "type": "Identifier", + "start": 1193, + "end": 1194, + "loc": { + "start": { + "line": 29, + "column": 26 + }, + "end": { + "line": 29, + "column": 27 + }, + "identifierName": "f" + }, + "name": "f" + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? (b ? (c => d) : (e => f)) : g", + "start": 1098, + "end": 1134, + "loc": { + "start": { + "line": 26, + "column": 31 + }, + "end": { + "line": 26, + "column": 67 + } + } + }, + { + "type": "CommentLine", + "value": " Function as type annotation", + "start": 1136, + "end": 1166, + "loc": { + "start": { + "line": 28, + "column": 0 + }, + "end": { + "line": 28, + "column": 30 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b): (c => d) => e) : f", + "start": 1196, + "end": 1227, + "loc": { + "start": { + "line": 29, + "column": 29 + }, + "end": { + "line": 29, + "column": 60 + } + } + } + ] + } + ], + "directives": [] + }, + "comments": [ + { + "type": "CommentLine", + "value": " Valid lhs value inside parentheses", + "start": 0, + "end": 37, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 37 + } + } + }, + { + "type": "CommentLine", + "value": " a ? b : (c => d)", + "start": 56, + "end": 75, + "loc": { + "start": { + "line": 2, + "column": 18 + }, + "end": { + "line": 2, + "column": 37 + } + } + }, + { + "type": "CommentLine", + "value": " a ? ((b): c => d) : e", + "start": 98, + "end": 122, + "loc": { + "start": { + "line": 3, + "column": 22 + }, + "end": { + "line": 3, + "column": 46 + } + } + }, + { + "type": "CommentLine", + "value": " a ? b : ((c): d => e)", + "start": 147, + "end": 171, + "loc": { + "start": { + "line": 4, + "column": 24 + }, + "end": { + "line": 4, + "column": 48 + } + } + }, + { + "type": "CommentLine", + "value": " Nested arrow function inside parentheses", + "start": 173, + "end": 216, + "loc": { + "start": { + "line": 6, + "column": 0 + }, + "end": { + "line": 6, + "column": 43 + } + } + }, + { + "type": "CommentLine", + "value": " a ? (b = (c) => d) : (e => f)", + "start": 246, + "end": 278, + "loc": { + "start": { + "line": 7, + "column": 29 + }, + "end": { + "line": 7, + "column": 61 + } + } + }, + { + "type": "CommentLine", + "value": " a ? ((b = (c) => d): e => f) : g", + "start": 312, + "end": 347, + "loc": { + "start": { + "line": 8, + "column": 33 + }, + "end": { + "line": 8, + "column": 68 + } + } + }, + { + "type": "CommentLine", + "value": " Nested conditional expressions", + "start": 349, + "end": 382, + "loc": { + "start": { + "line": 10, + "column": 0 + }, + "end": { + "line": 10, + "column": 33 + } + } + }, + { + "type": "CommentLine", + "value": " b ? (c ? ((d): e => f) : g) : h", + "start": 419, + "end": 453, + "loc": { + "start": { + "line": 11, + "column": 36 + }, + "end": { + "line": 11, + "column": 70 + } + } + }, + { + "type": "CommentLine", + "value": " a ? (b ? (c ? d : (e => f)) : g) : h", + "start": 490, + "end": 529, + "loc": { + "start": { + "line": 12, + "column": 36 + }, + "end": { + "line": 12, + "column": 75 + } + } + }, + { + "type": "CommentLine", + "value": " a ? (b ? c : ((d): e => f)) : g", + "start": 565, + "end": 599, + "loc": { + "start": { + "line": 14, + "column": 34 + }, + "end": { + "line": 14, + "column": 68 + } + } + }, + { + "type": "CommentLine", + "value": " Multiple arrow functions", + "start": 601, + "end": 628, + "loc": { + "start": { + "line": 16, + "column": 0 + }, + "end": { + "line": 16, + "column": 27 + } + } + }, + { + "type": "CommentLine", + "value": " a ? ((b): c => d) : ((e): f => g)", + "start": 662, + "end": 698, + "loc": { + "start": { + "line": 17, + "column": 33 + }, + "end": { + "line": 17, + "column": 69 + } + } + }, + { + "type": "CommentLine", + "value": " Multiple nested arrow functions ( is needed to avoid ambiguities)", + "start": 700, + "end": 771, + "loc": { + "start": { + "line": 19, + "column": 0 + }, + "end": { + "line": 19, + "column": 71 + } + } + }, + { + "type": "CommentLine", + "value": " a ? ((b): c => ((d): e => f)) : g", + "start": 805, + "end": 841, + "loc": { + "start": { + "line": 20, + "column": 33 + }, + "end": { + "line": 20, + "column": 69 + } + } + }, + { + "type": "CommentLine", + "value": " a ? b : (c => ((d): e => f))", + "start": 874, + "end": 908, + "loc": { + "start": { + "line": 21, + "column": 32 + }, + "end": { + "line": 21, + "column": 66 + } + } + }, + { + "type": "CommentLine", + "value": " a ? ((b): c => d) : (e => f)", + "start": 941, + "end": 975, + "loc": { + "start": { + "line": 22, + "column": 32 + }, + "end": { + "line": 22, + "column": 66 + } + } + }, + { + "type": "CommentLine", + "value": " Invalid lhs value inside parentheses", + "start": 977, + "end": 1016, + "loc": { + "start": { + "line": 24, + "column": 0 + }, + "end": { + "line": 24, + "column": 39 + } + } + }, + { + "type": "CommentLine", + "value": " a ? (b => c) : (d => e)", + "start": 1040, + "end": 1066, + "loc": { + "start": { + "line": 25, + "column": 23 + }, + "end": { + "line": 25, + "column": 49 + } + } + }, + { + "type": "CommentLine", + "value": " a ? (b ? (c => d) : (e => f)) : g", + "start": 1098, + "end": 1134, + "loc": { + "start": { + "line": 26, + "column": 31 + }, + "end": { + "line": 26, + "column": 67 + } + } + }, + { + "type": "CommentLine", + "value": " Function as type annotation", + "start": 1136, + "end": 1166, + "loc": { + "start": { + "line": 28, + "column": 0 + }, + "end": { + "line": 28, + "column": 30 + } + } + }, + { + "type": "CommentLine", + "value": " a ? ((b): (c => d) => e) : f", + "start": 1196, + "end": 1227, + "loc": { + "start": { + "line": 29, + "column": 29 + }, + "end": { + "line": 29, + "column": 60 + } + } + } + ] +} \ No newline at end of file From 9d6e62e323175947fdbba79f70b7888bcf2f335c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Mon, 12 Jun 2017 12:00:57 +0200 Subject: [PATCH 2/8] Correctly parse nested arrow functions inside conditional expressions Defer the conversion of arrow function parameters to assignable nodes so that it is possible to use the (invalid) ast to get the exact position of the (wrong) arrow functions. --- src/parser/expression.js | 89 +- src/plugins/estree.js | 4 +- src/plugins/flow.js | 171 ++-- src/plugins/jsx/index.js | 4 +- src/tokenizer/state.js | 4 +- .../flow/regression/issue-58/actual.js | 4 + .../flow/regression/issue-58/expected.json | 864 ++++++++++++++++-- 7 files changed, 936 insertions(+), 204 deletions(-) diff --git a/src/parser/expression.js b/src/parser/expression.js index 44a9051a7e..1065916d0f 100644 --- a/src/parser/expression.js +++ b/src/parser/expression.js @@ -96,7 +96,7 @@ export default class ExpressionParser extends LValParser { // Parse an assignment expression. This includes applications of // operators like `+=`. - parseMaybeAssign(noIn?: ?boolean, refShorthandDefaultPos?: ?Pos, afterLeftParse?: Function, refNeedsArrowPos?: ?Pos): N.Expression { + parseMaybeAssign(noIn?: ?boolean, refShorthandDefaultPos?: ?Pos, afterLeftParse?: Function, refNeedsArrowPos?: ?Pos, noArrowParamsConversion?: boolean): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; if (this.match(tt._yield) && this.state.inGenerator) { @@ -117,7 +117,7 @@ export default class ExpressionParser extends LValParser { this.state.potentialArrowAt = this.state.start; } - let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos, refNeedsArrowPos); + let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos, refNeedsArrowPos, noArrowParamsConversion); if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc); if (this.state.type.isAssign) { const node = this.startNodeAt(startPos, startLoc); @@ -151,13 +151,13 @@ export default class ExpressionParser extends LValParser { // Parse a ternary conditional (`?:`) operator. - parseMaybeConditional(noIn: ?boolean, refShorthandDefaultPos: Pos, refNeedsArrowPos?: ?Pos): N.Expression { + parseMaybeConditional(noIn: ?boolean, refShorthandDefaultPos: Pos, refNeedsArrowPos?: ?Pos, noArrowParamsConversion?: boolean): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; - const expr = this.parseExprOps(noIn, refShorthandDefaultPos); + const expr = this.parseExprOps(noIn, refShorthandDefaultPos, noArrowParamsConversion); if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; - return this.parseConditional(expr, noIn, startPos, startLoc, refNeedsArrowPos); + return this.parseConditional(expr, noIn, startPos, startLoc, refNeedsArrowPos, noArrowParamsConversion); } parseConditional( @@ -167,7 +167,9 @@ export default class ExpressionParser extends LValParser { startLoc: Position, // FIXME: Disabling this for now since can't seem to get it to play nicely // eslint-disable-next-line no-unused-vars - refNeedsArrowPos?: ?Pos + refNeedsArrowPos?: ?Pos, + // eslint-disable-next-line no-unused-vars + noArrowParamsConversion?: boolean ): N.Expression { if (this.eat(tt.question)) { const node = this.startNodeAt(startPos, startLoc); @@ -182,10 +184,10 @@ export default class ExpressionParser extends LValParser { // Start the precedence parser. - parseExprOps(noIn: ?boolean, refShorthandDefaultPos: Pos): N.Expression { + parseExprOps(noIn: ?boolean, refShorthandDefaultPos: Pos, noArrowParamsConversion?: boolean): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; - const expr = this.parseMaybeUnary(refShorthandDefaultPos); + const expr = this.parseMaybeUnary(refShorthandDefaultPos, noArrowParamsConversion); if (refShorthandDefaultPos && refShorthandDefaultPos.start) { return expr; } else { @@ -233,7 +235,7 @@ export default class ExpressionParser extends LValParser { // Parse unary operators, both prefix and postfix. - parseMaybeUnary(refShorthandDefaultPos: ?Pos): N.Expression { + parseMaybeUnary(refShorthandDefaultPos: ?Pos, noArrowParamsConversion?: boolean): N.Expression { if (this.state.type.prefix) { const node = this.startNode(); const update = this.match(tt.incDec); @@ -261,7 +263,7 @@ export default class ExpressionParser extends LValParser { const startPos = this.state.start; const startLoc = this.state.startLoc; - let expr = this.parseExprSubscripts(refShorthandDefaultPos); + let expr = this.parseExprSubscripts(refShorthandDefaultPos, noArrowParamsConversion); if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; while (this.state.type.postfix && !this.canInsertSemicolon()) { const node = this.startNodeAt(startPos, startLoc); @@ -277,11 +279,11 @@ export default class ExpressionParser extends LValParser { // Parse call, dot, and `[]`-subscript expressions. - parseExprSubscripts(refShorthandDefaultPos: ?Pos): N.Expression { + parseExprSubscripts(refShorthandDefaultPos: ?Pos, noArrowParamsConversion?: boolean): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; const potentialArrowAt = this.state.potentialArrowAt; - const expr = this.parseExprAtom(refShorthandDefaultPos); + const expr = this.parseExprAtom(refShorthandDefaultPos, noArrowParamsConversion); if (expr.type === "ArrowFunctionExpression" && expr.start === potentialArrowAt) { return expr; @@ -440,7 +442,7 @@ export default class ExpressionParser extends LValParser { // `new`, or an expression wrapped in punctuation like `()`, `[]`, // or `{}`. - parseExprAtom(refShorthandDefaultPos?: ?Pos): N.Expression { + parseExprAtom(refShorthandDefaultPos?: ?Pos, noArrowParamsConversion?: boolean): N.Expression { const canBeArrow = this.state.potentialArrowAt === this.state.start; let node; @@ -507,7 +509,7 @@ export default class ExpressionParser extends LValParser { } if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) { - return this.parseArrowExpression(node, [id]); + return this.parseArrowExpression(node, [id], undefined, noArrowParamsConversion); } return id; @@ -551,7 +553,7 @@ export default class ExpressionParser extends LValParser { return this.finishNode(node, "BooleanLiteral"); case tt.parenL: - return this.parseParenAndDistinguishExpression(canBeArrow); + return this.parseParenAndDistinguishExpression(canBeArrow, noArrowParamsConversion); case tt.bracketL: node = this.startNode(); @@ -665,7 +667,7 @@ export default class ExpressionParser extends LValParser { return val; } - parseParenAndDistinguishExpression(canBeArrow: boolean): N.Expression { + parseParenAndDistinguishExpression(canBeArrow: boolean, noArrowParamsConversion?: boolean): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; @@ -713,7 +715,7 @@ export default class ExpressionParser extends LValParser { if (param.extra && param.extra.parenthesized) this.unexpected(param.extra.parenStart); } - return this.parseArrowExpression(arrowNode, exprList); + return this.parseArrowExpression(arrowNode, exprList, false, noArrowParamsConversion); } if (!exprList.length) { @@ -1053,10 +1055,16 @@ export default class ExpressionParser extends LValParser { // Parse arrow function expression with given parameters. - parseArrowExpression(node: N.ArrowFunctionExpression, params: N.Expression[], isAsync?: boolean): N.ArrowFunctionExpression { + parseArrowExpression(node: N.ArrowFunctionExpression, params: N.Expression[], isAsync?: boolean, noArrowParamsConversion?: boolean): N.ArrowFunctionExpression { this.initFunction(node, isAsync); - node.params = this.toAssignableList(params, true, "arrow function parameters"); - this.parseFunctionBody(node, true); + + if (noArrowParamsConversion) { + node.params = params; + } else { + node.params = this.toAssignableList(params, true, "arrow function parameters"); + } + + this.parseFunctionBody(node, true, noArrowParamsConversion); return this.finishNode(node, "ArrowFunctionExpression"); } @@ -1073,13 +1081,13 @@ export default class ExpressionParser extends LValParser { } // Parse function body and check parameters. - parseFunctionBody(node: N.Function, allowExpression?: boolean): void { + parseFunctionBody(node: N.Function, allowExpression?: boolean, noArrowParamsConversion?: boolean): void { const isExpression = allowExpression && !this.match(tt.braceL); const oldInAsync = this.state.inAsync; this.state.inAsync = node.async; if (isExpression) { - node.body = this.parseMaybeAssign(); + node.body = this.parseMaybeAssign(undefined, undefined, undefined, undefined, noArrowParamsConversion); node.expression = true; } else { // Start a new scope with regard to labels and the `inFunction` @@ -1098,28 +1106,37 @@ export default class ExpressionParser extends LValParser { // are not repeated, and it does not try to bind the words `eval` // or `arguments`. const isStrict = this.isStrictBody(node, isExpression); - // Also check when allowExpression === true for arrow functions - const checkLVal = this.state.strict || allowExpression || isStrict; + // If the body is an expression, check LVal only if parameters are converted + // to assignable nodes. + const checkLVal = isExpression ? !noArrowParamsConversion : this.state.strict || isStrict; if (isStrict && node.id && node.id.type === "Identifier" && node.id.name === "yield") { this.raise(node.id.start, "Binding yield in strict mode"); } if (checkLVal) { - const nameHash = Object.create(null); - const oldStrict = this.state.strict; - if (isStrict) this.state.strict = true; - if (node.id) { - this.checkLVal(node.id, true, undefined, "function name"); - } - for (const param of node.params) { - if (isStrict && param.type !== "Identifier") { - this.raise(param.start, "Non-simple parameter in strict mode"); - } - this.checkLVal(param, true, nameHash, "function parameter list"); + this.checkFunctionLValues(node, isStrict); + } + } + + // Checks that functions parameters and function name (if it exists) are valid + // left hand side values. + checkFunctionLValues(node: N.Function, isStrict: boolean): void { + const nameHash = Object.create(null); + const oldStrict = this.state.strict; + + if (isStrict) this.state.strict = true; + if (node.id) { + this.checkLVal(node.id, true, undefined, "function name"); + } + for (const param of node.params) { + if (isStrict && param.type !== "Identifier") { + this.raise(param.start, "Non-simple parameter in strict mode"); } - this.state.strict = oldStrict; + this.checkLVal(param, true, nameHash, "function parameter list"); } + + this.state.strict = oldStrict; } // Parses a comma-separated list of expressions, and returns them as diff --git a/src/plugins/estree.js b/src/plugins/estree.js index 61e2a8e69c..3988c0c32c 100644 --- a/src/plugins/estree.js +++ b/src/plugins/estree.js @@ -164,7 +164,7 @@ export default (superClass: Class): Class => class extends super classBody.body.push(this.finishNode(method, "MethodDefinition")); } - parseExprAtom(refShorthandDefaultPos?: ?Pos): N.Expression { + parseExprAtom(refShorthandDefaultPos?: ?Pos, noArrowParamsConversion?: boolean): N.Expression { switch (this.state.type) { case tt.regexp: return this.estreeParseRegExpLiteral(this.state.value); @@ -183,7 +183,7 @@ export default (superClass: Class): Class => class extends super return this.estreeParseLiteral(false); default: - return super.parseExprAtom(refShorthandDefaultPos); + return super.parseExprAtom(refShorthandDefaultPos, noArrowParamsConversion); } } diff --git a/src/plugins/flow.js b/src/plugins/flow.js index e816537c73..017945f6f7 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -37,6 +37,16 @@ const exportSuggestions = { interface: "export interface", }; +// Like Array#filter, but returns a tuple [ acceptedElements, discardedElements ] +function partition(list: T[], test: (T, number, T[]) => ?boolean): [ T[], T[] ] { + const list1 = []; + const list2 = []; + for (let i = 0; i < list.length; i++) { + (test(list[i], i, list) ? list1 : list2).push(list[i]); + } + return [ list1, list2 ]; +} + export default (superClass: Class): Class => class extends superClass { flowParseTypeInitialiser(tok?: TokenType): N.FlowType { const oldInType = this.state.inType; @@ -1007,7 +1017,7 @@ export default (superClass: Class): Class => class extends super // ================================== // plain function return types: function name(): string {} - parseFunctionBody(node: N.Function, allowExpression?: boolean): void { + parseFunctionBody(node: N.Function, allowExpression?: boolean, noArrowParamsConversion?: boolean): void { if (this.match(tt.colon) && !allowExpression) { // if allowExpression is true then we're parsing an arrow function and if // there's a return type then it's been handled elsewhere @@ -1020,7 +1030,7 @@ export default (superClass: Class): Class => class extends super : null; } - return super.parseFunctionBody(node, allowExpression); + return super.parseFunctionBody(node, allowExpression, noArrowParamsConversion); } // interfaces @@ -1072,7 +1082,7 @@ export default (superClass: Class): Class => class extends super return super.isExportDefaultSpecifier(); } - parseConditional(expr: N.Expression, noIn: ?boolean, startPos: number, startLoc: Position, refNeedsArrowPos?: ?Pos): N.Expression { + parseConditional(expr: N.Expression, noIn: ?boolean, startPos: number, startLoc: Position, refNeedsArrowPos?: ?Pos, noArrowParamsConversion?: boolean): N.Expression { if (!this.match(tt.question)) return expr; // only do the expensive clone if there is a question mark @@ -1095,107 +1105,83 @@ export default (superClass: Class): Class => class extends super this.expect(tt.question); const state = this.state.clone(); + const originalNoArrowAt = this.state.noArrowAt; const node = this.startNodeAt(startPos, startLoc); + let { consequent, failed } = this.tryParseConditionalConsequent(); + let [ valid, invalid ] = this.getArrowLikeExpressions(consequent); + + if (failed || invalid.length > 0) { + const noArrowAt = [ ...originalNoArrowAt ]; + + if (invalid.length > 0) { + this.state = state; + this.state.noArrowAt = noArrowAt; - let { - consequent, - failed, - // eslint-disable-next-line prefer-const - error: err1, - } = this.tryParseConditionalConsequent(); + for (let i = 0; i < invalid.length; i++) { + noArrowAt.push(invalid[i].start); + } + + ({ consequent, failed } = this.tryParseConditionalConsequent()); + [ valid, invalid ] = this.getArrowLikeExpressions(consequent); + } - if (failed) { - const positions = this.getArrowLikeExpressionsPos(consequent); - if (positions && positions.length > 1) { + if (failed && valid.length > 1) { // if there are two or more possible correct ways of parsing, throw an // error. // e.g. Source: a ? (b): c => (d): e => f // Result 1: a ? b : (c => ((d): e => f)) // Result 2: a ? ((b): c => d) : (e => f) - - // some nested expressions have only one possible parsing, but the inner - // one "thinks" they are two. In this case, the error is handled by the - // outer expression. - // e.g. Source: a ? b ? (c) : d => (e) : f => g - // Correct: a ? (b ? c : (d => e)) : (f => g) - // "b..." could be: b ? c : (d => ((e): f => g)) - // or: b ? ((c): d => e) : (f => g) - this.raise( state.start, "Ambiguous expression: wrap the arrow functions in parentheses to disambiguate." ); } - this.state = state; - - // "positions" is undefined when parseMaybeConditional throws. That - // means either that an invalid lhs expression was parsed as a function - // parameter, or that the inner expression "thought" it had two or more - // possible ways of being parsed. We can safely assume that the error does - // not come from a nested conditional expression (and thus that it is at - // the starting position) because the it would have been handled by the - // parent one. - this.state.noArrowAt = positions ? positions[0] : this.state.start; - - let err2: ?Error; - ({ - consequent, - failed, - // eslint-disable-next-line prefer-const - error: err2, - } = this.tryParseConditionalConsequent()); - - if (failed && (err1 || err2)) { - throw err1 || err2; + if (failed && valid.length === 1) { + this.state = state; + this.state.noArrowAt = noArrowAt.concat(valid[0].start); + ({ consequent, failed } = this.tryParseConditionalConsequent()); } } + this.getArrowLikeExpressions(consequent, true); + + this.state.noArrowAt = originalNoArrowAt; this.expect(tt.colon); node.test = expr; node.consequent = consequent; - node.alternate = this.parseMaybeAssign(noIn); + node.alternate = this.parseMaybeAssign(noIn, undefined, undefined, undefined, noArrowParamsConversion); return this.finishNode(node, "ConditionalExpression"); } - tryParseConditionalConsequent(): { - consequent: ?N.Expression, - failed: boolean, - error: ?Error, - } { - let failed = false; - let consequent: N.Expression; - let error: Error; - - try { - consequent = this.parseMaybeAssign(); - failed = !this.match(tt.colon); - } catch (err) { - if (err instanceof SyntaxError) { - failed = true; - error = err; - } else { - // istanbul ignore next: no such error is expected - throw err; - } - } + tryParseConditionalConsequent(): { consequent: N.Expression, failed: boolean } { + const consequent = this.parseMaybeAssign( + undefined, undefined, undefined, undefined, + /* noArrowParamsConversion */ true + ); + const failed = !this.match(tt.colon); - return { consequent, failed, error }; + return { consequent, failed }; } - getArrowLikeExpressionsPos(node: ?N.Expression): ?number[] { - if (!node) return; - + // Given an expression, walks throught its arrow functions whose body is + // an expression and throught conditional expressions. It returns every + // function which has been parsed with a return type but could have been + // parenthesized expressions. + // These functions are separated into two arrays: one containing the ones + // whose parameters can be converted to assignable lists, one containing the + // others. + getArrowLikeExpressions(node: N.Expression, disallowInvalid?: boolean): [ N.ArrowFunctionExpression[], N.ArrowFunctionExpression[] ] { const stack = [ node ]; - const start: number[] = []; + const arrows: N.ArrowFunctionExpression[] = []; while (stack.length !== 0) { const node = stack.pop(); if (node.type === "ArrowFunctionExpression") { if (node.returnType && !node.typeParameters) { - start.push(node.start); + arrows.push(node); } stack.push(node.body); } else if (node.type === "ConditionalExpression") { @@ -1204,7 +1190,21 @@ export default (superClass: Class): Class => class extends super } } - return start; + if (disallowInvalid) { + for (let i = 0; i < arrows.length; i++) { + this.toAssignableList(arrows[i].params, true, "arrow function parameters"); + } + return [ arrows, [] ]; + } + + return partition(arrows, (node) => { + try { + this.toAssignableList(node.params, true, "arrow function parameters"); + return true; + } catch (err) { + return false; + } + }); } parseParenItem(node: N.Expression, startPos: number, startLoc: Position): N.Expression { @@ -1594,12 +1594,12 @@ export default (superClass: Class): Class => class extends super // parse the rest, make sure the rest is an arrow function, and go from // there // 3. This is neither. Just call the super method - parseMaybeAssign(noIn?: ?boolean, refShorthandDefaultPos?: ?Pos, afterLeftParse?: Function, refNeedsArrowPos?: ?Pos): N.Expression { + parseMaybeAssign(noIn?: ?boolean, refShorthandDefaultPos?: ?Pos, afterLeftParse?: Function, refNeedsArrowPos?: ?Pos, noArrowParamsConversion?: boolean): N.Expression { let jsxError = null; if (tt.jsxTagStart && this.match(tt.jsxTagStart)) { const state = this.state.clone(); try { - return super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos); + return super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos, noArrowParamsConversion); } catch (err) { if (err instanceof SyntaxError) { this.state = state; @@ -1623,7 +1623,7 @@ export default (superClass: Class): Class => class extends super try { typeParameters = this.flowParseTypeParameterDeclaration(); - arrowExpression = super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos); + arrowExpression = super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos, noArrowParamsConversion); arrowExpression.typeParameters = typeParameters; this.resetStartLocationFromNode(arrowExpression, typeParameters); } catch (err) { @@ -1642,7 +1642,7 @@ export default (superClass: Class): Class => class extends super } } - return super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos); + return super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos, noArrowParamsConversion); } // handle return types for arrow functions @@ -1683,9 +1683,26 @@ export default (superClass: Class): Class => class extends super return this.match(tt.colon) || super.shouldParseArrow(); } - parseParenAndDistinguishExpression(canBeArrow: boolean): N.Expression { + parseArrowExpression(node: N.ArrowFunctionExpression, params: N.Expression[], isAsync?: boolean, noArrowParamsConversion?: boolean): N.ArrowFunctionExpression { + const convertParameters = noArrowParamsConversion && node.typeParameters; + if (convertParameters) { + this.toAssignableList(params, true, "arrow function parameters"); + } + + const arrowNode = super.parseArrowExpression(node, params, isAsync, noArrowParamsConversion); + + if (convertParameters) { + const isStrict = this.isStrictBody(arrowNode, arrowNode.expression); + this.checkFunctionLValues(arrowNode, isStrict); + } + + return arrowNode; + } + + parseParenAndDistinguishExpression(canBeArrow: boolean, noArrowParamsConversion?: boolean): N.Expression { return super.parseParenAndDistinguishExpression( - canBeArrow && this.state.noArrowAt !== this.state.start + canBeArrow && this.state.noArrowAt.indexOf(this.state.start) === -1, + noArrowParamsConversion ); } }; diff --git a/src/plugins/jsx/index.js b/src/plugins/jsx/index.js index 0d8e6b91a5..f1e3fb0424 100644 --- a/src/plugins/jsx/index.js +++ b/src/plugins/jsx/index.js @@ -407,13 +407,13 @@ export default (superClass: Class): Class => class extends super // Overrides // ================================== - parseExprAtom(refShortHandDefaultPos: ?Pos): N.Expression { + parseExprAtom(refShortHandDefaultPos: ?Pos, noArrowParamsConversion?: boolean): N.Expression { if (this.match(tt.jsxText)) { return this.parseLiteral(this.state.value, "JSXText"); } else if (this.match(tt.jsxTagStart)) { return this.jsxParseElement(); } else { - return super.parseExprAtom(refShortHandDefaultPos); + return super.parseExprAtom(refShortHandDefaultPos, noArrowParamsConversion); } } diff --git a/src/tokenizer/state.js b/src/tokenizer/state.js index 736655c5c5..790fca609b 100644 --- a/src/tokenizer/state.js +++ b/src/tokenizer/state.js @@ -18,7 +18,7 @@ export default class State { this.potentialArrowAt = -1; - this.noArrowAt = -1; + this.noArrowAt = []; this.inMethod = this.inFunction = @@ -79,7 +79,7 @@ export default class State { // typed arrow function, but it isn't // e.g. a ? (b) : c => d // ^ - noArrowAt: number; + noArrowAt: number[]; // Flags to track whether we are in a function, a generator. inFunction: boolean; diff --git a/test/fixtures/flow/regression/issue-58/actual.js b/test/fixtures/flow/regression/issue-58/actual.js index 38526da827..44cac8e1f6 100644 --- a/test/fixtures/flow/regression/issue-58/actual.js +++ b/test/fixtures/flow/regression/issue-58/actual.js @@ -25,5 +25,9 @@ a ? (b) : c => (d) : e => f; // a ? ((b): c => d) : (e => f) a ? (b => c) : d => e; // a ? (b => c) : (d => e) a ? b ? (c => d) : e => f : g; // a ? (b ? (c => d) : (e => f)) : g +// Invalid lhs value inside parentheses inside arrow function +a ? (b) : c => (d => e) : f => g; // a ? ((b): c => (d => e)) : (f => g) +a ? b ? (c => d) : e => (f => g) : h => i; // a ? (b ? (c => d) : (e => (f => g))) : (h => i) + // Function as type annotation a ? (b) : (c => d) => e : f; // a ? ((b): (c => d) => e) : f diff --git a/test/fixtures/flow/regression/issue-58/expected.json b/test/fixtures/flow/regression/issue-58/expected.json index d0e27af175..23d799a63a 100644 --- a/test/fixtures/flow/regression/issue-58/expected.json +++ b/test/fixtures/flow/regression/issue-58/expected.json @@ -1,28 +1,28 @@ { "type": "File", "start": 0, - "end": 1227, + "end": 1457, "loc": { "start": { "line": 1, "column": 0 }, "end": { - "line": 29, + "line": 33, "column": 60 } }, "program": { "type": "Program", "start": 0, - "end": 1227, + "end": 1457, "loc": { "start": { "line": 1, "column": 0 }, "end": { - "line": 29, + "line": 33, "column": 60 } }, @@ -3649,18 +3649,664 @@ } } }, + { + "type": "CommentLine", + "value": " Invalid lhs value inside parentheses inside arrow function", + "start": 1136, + "end": 1197, + "loc": { + "start": { + "line": 28, + "column": 0 + }, + "end": { + "line": 28, + "column": 61 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 1198, + "end": 1231, + "loc": { + "start": { + "line": 29, + "column": 0 + }, + "end": { + "line": 29, + "column": 33 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 1198, + "end": 1230, + "loc": { + "start": { + "line": 29, + "column": 0 + }, + "end": { + "line": 29, + "column": 32 + } + }, + "test": { + "type": "Identifier", + "start": 1198, + "end": 1199, + "loc": { + "start": { + "line": 29, + "column": 0 + }, + "end": { + "line": 29, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 1202, + "end": 1221, + "loc": { + "start": { + "line": 29, + "column": 4 + }, + "end": { + "line": 29, + "column": 23 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 1206, + "end": 1209, + "loc": { + "start": { + "line": 29, + "column": 8 + }, + "end": { + "line": 29, + "column": 11 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 1208, + "end": 1209, + "loc": { + "start": { + "line": 29, + "column": 10 + }, + "end": { + "line": 29, + "column": 11 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 1208, + "end": 1209, + "loc": { + "start": { + "line": 29, + "column": 10 + }, + "end": { + "line": 29, + "column": 11 + }, + "identifierName": "c" + }, + "name": "c" + } + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1203, + "end": 1204, + "loc": { + "start": { + "line": 29, + "column": 5 + }, + "end": { + "line": 29, + "column": 6 + }, + "identifierName": "b" + }, + "name": "b" + } + ], + "body": { + "type": "ArrowFunctionExpression", + "start": 1214, + "end": 1220, + "loc": { + "start": { + "line": 29, + "column": 16 + }, + "end": { + "line": 29, + "column": 22 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1214, + "end": 1215, + "loc": { + "start": { + "line": 29, + "column": 16 + }, + "end": { + "line": 29, + "column": 17 + }, + "identifierName": "d" + }, + "name": "d" + } + ], + "body": { + "type": "Identifier", + "start": 1219, + "end": 1220, + "loc": { + "start": { + "line": 29, + "column": 21 + }, + "end": { + "line": 29, + "column": 22 + }, + "identifierName": "e" + }, + "name": "e" + }, + "extra": { + "parenthesized": true, + "parenStart": 1213 + } + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 1224, + "end": 1230, + "loc": { + "start": { + "line": 29, + "column": 26 + }, + "end": { + "line": 29, + "column": 32 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1224, + "end": 1225, + "loc": { + "start": { + "line": 29, + "column": 26 + }, + "end": { + "line": 29, + "column": 27 + }, + "identifierName": "f" + }, + "name": "f" + } + ], + "body": { + "type": "Identifier", + "start": 1229, + "end": 1230, + "loc": { + "start": { + "line": 29, + "column": 31 + }, + "end": { + "line": 29, + "column": 32 + }, + "identifierName": "g" + }, + "name": "g" + } + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? (b ? (c => d) : (e => f)) : g", + "start": 1098, + "end": 1134, + "loc": { + "start": { + "line": 26, + "column": 31 + }, + "end": { + "line": 26, + "column": 67 + } + } + }, + { + "type": "CommentLine", + "value": " Invalid lhs value inside parentheses inside arrow function", + "start": 1136, + "end": 1197, + "loc": { + "start": { + "line": 28, + "column": 0 + }, + "end": { + "line": 28, + "column": 61 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b): c => (d => e)) : (f => g)", + "start": 1232, + "end": 1270, + "loc": { + "start": { + "line": 29, + "column": 34 + }, + "end": { + "line": 29, + "column": 72 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 1271, + "end": 1313, + "loc": { + "start": { + "line": 30, + "column": 0 + }, + "end": { + "line": 30, + "column": 42 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 1271, + "end": 1312, + "loc": { + "start": { + "line": 30, + "column": 0 + }, + "end": { + "line": 30, + "column": 41 + } + }, + "test": { + "type": "Identifier", + "start": 1271, + "end": 1272, + "loc": { + "start": { + "line": 30, + "column": 0 + }, + "end": { + "line": 30, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ConditionalExpression", + "start": 1275, + "end": 1303, + "loc": { + "start": { + "line": 30, + "column": 4 + }, + "end": { + "line": 30, + "column": 32 + } + }, + "test": { + "type": "Identifier", + "start": 1275, + "end": 1276, + "loc": { + "start": { + "line": 30, + "column": 4 + }, + "end": { + "line": 30, + "column": 5 + }, + "identifierName": "b" + }, + "name": "b" + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 1280, + "end": 1286, + "loc": { + "start": { + "line": 30, + "column": 9 + }, + "end": { + "line": 30, + "column": 15 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1280, + "end": 1281, + "loc": { + "start": { + "line": 30, + "column": 9 + }, + "end": { + "line": 30, + "column": 10 + }, + "identifierName": "c" + }, + "name": "c" + } + ], + "body": { + "type": "Identifier", + "start": 1285, + "end": 1286, + "loc": { + "start": { + "line": 30, + "column": 14 + }, + "end": { + "line": 30, + "column": 15 + }, + "identifierName": "d" + }, + "name": "d" + }, + "extra": { + "parenthesized": true, + "parenStart": 1279 + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 1290, + "end": 1303, + "loc": { + "start": { + "line": 30, + "column": 19 + }, + "end": { + "line": 30, + "column": 32 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1290, + "end": 1291, + "loc": { + "start": { + "line": 30, + "column": 19 + }, + "end": { + "line": 30, + "column": 20 + }, + "identifierName": "e" + }, + "name": "e" + } + ], + "body": { + "type": "ArrowFunctionExpression", + "start": 1296, + "end": 1302, + "loc": { + "start": { + "line": 30, + "column": 25 + }, + "end": { + "line": 30, + "column": 31 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1296, + "end": 1297, + "loc": { + "start": { + "line": 30, + "column": 25 + }, + "end": { + "line": 30, + "column": 26 + }, + "identifierName": "f" + }, + "name": "f" + } + ], + "body": { + "type": "Identifier", + "start": 1301, + "end": 1302, + "loc": { + "start": { + "line": 30, + "column": 30 + }, + "end": { + "line": 30, + "column": 31 + }, + "identifierName": "g" + }, + "name": "g" + }, + "extra": { + "parenthesized": true, + "parenStart": 1295 + } + } + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 1306, + "end": 1312, + "loc": { + "start": { + "line": 30, + "column": 35 + }, + "end": { + "line": 30, + "column": 41 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1306, + "end": 1307, + "loc": { + "start": { + "line": 30, + "column": 35 + }, + "end": { + "line": 30, + "column": 36 + }, + "identifierName": "h" + }, + "name": "h" + } + ], + "body": { + "type": "Identifier", + "start": 1311, + "end": 1312, + "loc": { + "start": { + "line": 30, + "column": 40 + }, + "end": { + "line": 30, + "column": 41 + }, + "identifierName": "i" + }, + "name": "i" + } + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b): c => (d => e)) : (f => g)", + "start": 1232, + "end": 1270, + "loc": { + "start": { + "line": 29, + "column": 34 + }, + "end": { + "line": 29, + "column": 72 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? (b ? (c => d) : (e => (f => g))) : (h => i)", + "start": 1314, + "end": 1364, + "loc": { + "start": { + "line": 30, + "column": 43 + }, + "end": { + "line": 30, + "column": 93 + } + } + }, { "type": "CommentLine", "value": " Function as type annotation", - "start": 1136, - "end": 1166, + "start": 1366, + "end": 1396, "loc": { "start": { - "line": 28, + "line": 32, "column": 0 }, "end": { - "line": 28, + "line": 32, "column": 30 } } @@ -3669,43 +4315,43 @@ }, { "type": "ExpressionStatement", - "start": 1167, - "end": 1195, + "start": 1397, + "end": 1425, "loc": { "start": { - "line": 29, + "line": 33, "column": 0 }, "end": { - "line": 29, + "line": 33, "column": 28 } }, "expression": { "type": "ConditionalExpression", - "start": 1167, - "end": 1194, + "start": 1397, + "end": 1424, "loc": { "start": { - "line": 29, + "line": 33, "column": 0 }, "end": { - "line": 29, + "line": 33, "column": 27 } }, "test": { "type": "Identifier", - "start": 1167, - "end": 1168, + "start": 1397, + "end": 1398, "loc": { "start": { - "line": 29, + "line": 33, "column": 0 }, "end": { - "line": 29, + "line": 33, "column": 1 }, "identifierName": "a" @@ -3715,59 +4361,59 @@ }, "consequent": { "type": "ArrowFunctionExpression", - "start": 1171, - "end": 1190, + "start": 1401, + "end": 1420, "loc": { "start": { - "line": 29, + "line": 33, "column": 4 }, "end": { - "line": 29, + "line": 33, "column": 23 } }, "predicate": null, "returnType": { "type": "TypeAnnotation", - "start": 1175, - "end": 1185, + "start": 1405, + "end": 1415, "loc": { "start": { - "line": 29, + "line": 33, "column": 8 }, "end": { - "line": 29, + "line": 33, "column": 18 } }, "typeAnnotation": { "type": "FunctionTypeAnnotation", - "start": 1178, - "end": 1184, + "start": 1408, + "end": 1414, "loc": { "start": { - "line": 29, + "line": 33, "column": 11 }, "end": { - "line": 29, + "line": 33, "column": 17 } }, "params": [ { "type": "FunctionTypeParam", - "start": 1178, - "end": 1182, + "start": 1408, + "end": 1412, "loc": { "start": { - "line": 29, + "line": 33, "column": 11 }, "end": { - "line": 29, + "line": 33, "column": 15 } }, @@ -3775,30 +4421,30 @@ "optional": false, "typeAnnotation": { "type": "GenericTypeAnnotation", - "start": 1178, - "end": 1179, + "start": 1408, + "end": 1409, "loc": { "start": { - "line": 29, + "line": 33, "column": 11 }, "end": { - "line": 29, + "line": 33, "column": 12 } }, "typeParameters": null, "id": { "type": "Identifier", - "start": 1178, - "end": 1179, + "start": 1408, + "end": 1409, "loc": { "start": { - "line": 29, + "line": 33, "column": 11 }, "end": { - "line": 29, + "line": 33, "column": 12 }, "identifierName": "c" @@ -3811,30 +4457,30 @@ "rest": null, "returnType": { "type": "GenericTypeAnnotation", - "start": 1183, - "end": 1184, + "start": 1413, + "end": 1414, "loc": { "start": { - "line": 29, + "line": 33, "column": 16 }, "end": { - "line": 29, + "line": 33, "column": 17 } }, "typeParameters": null, "id": { "type": "Identifier", - "start": 1183, - "end": 1184, + "start": 1413, + "end": 1414, "loc": { "start": { - "line": 29, + "line": 33, "column": 16 }, "end": { - "line": 29, + "line": 33, "column": 17 }, "identifierName": "d" @@ -3852,15 +4498,15 @@ "params": [ { "type": "Identifier", - "start": 1172, - "end": 1173, + "start": 1402, + "end": 1403, "loc": { "start": { - "line": 29, + "line": 33, "column": 5 }, "end": { - "line": 29, + "line": 33, "column": 6 }, "identifierName": "b" @@ -3870,15 +4516,15 @@ ], "body": { "type": "Identifier", - "start": 1189, - "end": 1190, + "start": 1419, + "end": 1420, "loc": { "start": { - "line": 29, + "line": 33, "column": 22 }, "end": { - "line": 29, + "line": 33, "column": 23 }, "identifierName": "e" @@ -3888,15 +4534,15 @@ }, "alternate": { "type": "Identifier", - "start": 1193, - "end": 1194, + "start": 1423, + "end": 1424, "loc": { "start": { - "line": 29, + "line": 33, "column": 26 }, "end": { - "line": 29, + "line": 33, "column": 27 }, "identifierName": "f" @@ -3908,32 +4554,32 @@ "leadingComments": [ { "type": "CommentLine", - "value": " a ? (b ? (c => d) : (e => f)) : g", - "start": 1098, - "end": 1134, + "value": " a ? (b ? (c => d) : (e => (f => g))) : (h => i)", + "start": 1314, + "end": 1364, "loc": { "start": { - "line": 26, - "column": 31 + "line": 30, + "column": 43 }, "end": { - "line": 26, - "column": 67 + "line": 30, + "column": 93 } } }, { "type": "CommentLine", "value": " Function as type annotation", - "start": 1136, - "end": 1166, + "start": 1366, + "end": 1396, "loc": { "start": { - "line": 28, + "line": 32, "column": 0 }, "end": { - "line": 28, + "line": 32, "column": 30 } } @@ -3943,15 +4589,15 @@ { "type": "CommentLine", "value": " a ? ((b): (c => d) => e) : f", - "start": 1196, - "end": 1227, + "start": 1426, + "end": 1457, "loc": { "start": { - "line": 29, + "line": 33, "column": 29 }, "end": { - "line": 29, + "line": 33, "column": 60 } } @@ -4284,9 +4930,9 @@ }, { "type": "CommentLine", - "value": " Function as type annotation", + "value": " Invalid lhs value inside parentheses inside arrow function", "start": 1136, - "end": 1166, + "end": 1197, "loc": { "start": { "line": 28, @@ -4294,6 +4940,54 @@ }, "end": { "line": 28, + "column": 61 + } + } + }, + { + "type": "CommentLine", + "value": " a ? ((b): c => (d => e)) : (f => g)", + "start": 1232, + "end": 1270, + "loc": { + "start": { + "line": 29, + "column": 34 + }, + "end": { + "line": 29, + "column": 72 + } + } + }, + { + "type": "CommentLine", + "value": " a ? (b ? (c => d) : (e => (f => g))) : (h => i)", + "start": 1314, + "end": 1364, + "loc": { + "start": { + "line": 30, + "column": 43 + }, + "end": { + "line": 30, + "column": 93 + } + } + }, + { + "type": "CommentLine", + "value": " Function as type annotation", + "start": 1366, + "end": 1396, + "loc": { + "start": { + "line": 32, + "column": 0 + }, + "end": { + "line": 32, "column": 30 } } @@ -4301,15 +4995,15 @@ { "type": "CommentLine", "value": " a ? ((b): (c => d) => e) : f", - "start": 1196, - "end": 1227, + "start": 1426, + "end": 1457, "loc": { "start": { - "line": 29, + "line": 33, "column": 29 }, "end": { - "line": 29, + "line": 33, "column": 60 } } From 1db908a7432b4472231aec05c361272f4801898b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Mon, 12 Jun 2017 14:57:38 +0200 Subject: [PATCH 3/8] Check params of arrow fns w/ type params or w/o return type --- src/parser/expression.js | 12 +++++----- src/plugins/flow.js | 22 +++++-------------- .../actual.js | 0 .../options.json | 0 .../regression/issue-58-failing-2/actual.js | 2 ++ .../issue-58-failing-2/options.json | 3 +++ .../regression/issue-58-failing-3/actual.js | 2 ++ .../issue-58-failing-3/options.json | 3 +++ 8 files changed, 21 insertions(+), 23 deletions(-) rename test/fixtures/flow/regression/{issue-58-failing => issue-58-failing-1}/actual.js (100%) rename test/fixtures/flow/regression/{issue-58-failing => issue-58-failing-1}/options.json (100%) create mode 100644 test/fixtures/flow/regression/issue-58-failing-2/actual.js create mode 100644 test/fixtures/flow/regression/issue-58-failing-2/options.json create mode 100644 test/fixtures/flow/regression/issue-58-failing-3/actual.js create mode 100644 test/fixtures/flow/regression/issue-58-failing-3/options.json diff --git a/src/parser/expression.js b/src/parser/expression.js index 1065916d0f..1f92149748 100644 --- a/src/parser/expression.js +++ b/src/parser/expression.js @@ -1106,24 +1106,24 @@ export default class ExpressionParser extends LValParser { // are not repeated, and it does not try to bind the words `eval` // or `arguments`. const isStrict = this.isStrictBody(node, isExpression); - // If the body is an expression, check LVal only if parameters are converted - // to assignable nodes. - const checkLVal = isExpression ? !noArrowParamsConversion : this.state.strict || isStrict; if (isStrict && node.id && node.id.type === "Identifier" && node.id.name === "yield") { this.raise(node.id.start, "Binding yield in strict mode"); } - if (checkLVal) { - this.checkFunctionLValues(node, isStrict); + // If this is an arrow function, check its parameter only if they are + // converted to assignable nodes + if (allowExpression ? !noArrowParamsConversion : this.state.strict || isStrict) { + this.checkFunctionLValues(node); } } // Checks that functions parameters and function name (if it exists) are valid // left hand side values. - checkFunctionLValues(node: N.Function, isStrict: boolean): void { + checkFunctionLValues(node: N.Function): void { const nameHash = Object.create(null); const oldStrict = this.state.strict; + const isStrict = this.isStrictBody(node, node.expression); if (isStrict) this.state.strict = true; if (node.id) { diff --git a/src/plugins/flow.js b/src/plugins/flow.js index 017945f6f7..d759a98785 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -1180,7 +1180,11 @@ export default (superClass: Class): Class => class extends super while (stack.length !== 0) { const node = stack.pop(); if (node.type === "ArrowFunctionExpression") { - if (node.returnType && !node.typeParameters) { + if (node.typeParameters || !node.returnType) { + // This is an arrow expression without ambiguity, so check its parameters + this.toAssignableList(node.params, true, "arrow function parameters"); + this.checkFunctionLValues(node); + } else { arrows.push(node); } stack.push(node.body); @@ -1683,22 +1687,6 @@ export default (superClass: Class): Class => class extends super return this.match(tt.colon) || super.shouldParseArrow(); } - parseArrowExpression(node: N.ArrowFunctionExpression, params: N.Expression[], isAsync?: boolean, noArrowParamsConversion?: boolean): N.ArrowFunctionExpression { - const convertParameters = noArrowParamsConversion && node.typeParameters; - if (convertParameters) { - this.toAssignableList(params, true, "arrow function parameters"); - } - - const arrowNode = super.parseArrowExpression(node, params, isAsync, noArrowParamsConversion); - - if (convertParameters) { - const isStrict = this.isStrictBody(arrowNode, arrowNode.expression); - this.checkFunctionLValues(arrowNode, isStrict); - } - - return arrowNode; - } - parseParenAndDistinguishExpression(canBeArrow: boolean, noArrowParamsConversion?: boolean): N.Expression { return super.parseParenAndDistinguishExpression( canBeArrow && this.state.noArrowAt.indexOf(this.state.start) === -1, diff --git a/test/fixtures/flow/regression/issue-58-failing/actual.js b/test/fixtures/flow/regression/issue-58-failing-1/actual.js similarity index 100% rename from test/fixtures/flow/regression/issue-58-failing/actual.js rename to test/fixtures/flow/regression/issue-58-failing-1/actual.js diff --git a/test/fixtures/flow/regression/issue-58-failing/options.json b/test/fixtures/flow/regression/issue-58-failing-1/options.json similarity index 100% rename from test/fixtures/flow/regression/issue-58-failing/options.json rename to test/fixtures/flow/regression/issue-58-failing-1/options.json diff --git a/test/fixtures/flow/regression/issue-58-failing-2/actual.js b/test/fixtures/flow/regression/issue-58-failing-2/actual.js new file mode 100644 index 0000000000..30942d27a0 --- /dev/null +++ b/test/fixtures/flow/regression/issue-58-failing-2/actual.js @@ -0,0 +1,2 @@ +// Invalid LHS parameter after type parameters +a ? (b => c) : d => (e) : f => g; \ No newline at end of file diff --git a/test/fixtures/flow/regression/issue-58-failing-2/options.json b/test/fixtures/flow/regression/issue-58-failing-2/options.json new file mode 100644 index 0000000000..26b1d53e8d --- /dev/null +++ b/test/fixtures/flow/regression/issue-58-failing-2/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Invalid left-hand side in arrow function parameters (2:8)" +} \ No newline at end of file diff --git a/test/fixtures/flow/regression/issue-58-failing-3/actual.js b/test/fixtures/flow/regression/issue-58-failing-3/actual.js new file mode 100644 index 0000000000..2dd9457fff --- /dev/null +++ b/test/fixtures/flow/regression/issue-58-failing-3/actual.js @@ -0,0 +1,2 @@ +// Invalid LHS parameter after type parameters +a ? (b => c) => (e) : f => g; \ No newline at end of file diff --git a/test/fixtures/flow/regression/issue-58-failing-3/options.json b/test/fixtures/flow/regression/issue-58-failing-3/options.json new file mode 100644 index 0000000000..830b8f6bc7 --- /dev/null +++ b/test/fixtures/flow/regression/issue-58-failing-3/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Invalid left-hand side in arrow function parameters (2:5)" +} \ No newline at end of file From 4d93bd7edd668faa281896f8b44d05e3d209cdf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Mon, 12 Jun 2017 16:42:17 +0200 Subject: [PATCH 4/8] Fix also async functions --- src/parser/expression.js | 10 +- src/plugins/flow.js | 20 +- .../regression/issue-58-failing-4/actual.js | 2 + .../issue-58-failing-4/options.json | 3 + .../flow/regression/issue-58/actual.js | 6 + .../flow/regression/issue-58/expected.json | 986 +++++++++++++++++- 6 files changed, 1014 insertions(+), 13 deletions(-) create mode 100644 test/fixtures/flow/regression/issue-58-failing-4/actual.js create mode 100644 test/fixtures/flow/regression/issue-58-failing-4/options.json diff --git a/src/parser/expression.js b/src/parser/expression.js index 1f92149748..9cee233e67 100644 --- a/src/parser/expression.js +++ b/src/parser/expression.js @@ -293,10 +293,10 @@ export default class ExpressionParser extends LValParser { return expr; } - return this.parseSubscripts(expr, startPos, startLoc); + return this.parseSubscripts(expr, startPos, startLoc, undefined, noArrowParamsConversion); } - parseSubscripts(base: N.Expression, startPos: number, startLoc: Position, noCalls?: boolean): N.Expression { + parseSubscripts(base: N.Expression, startPos: number, startLoc: Position, noCalls?: boolean, noArrowParamsConversion?: ?boolean): N.Expression { for (;;) { if (!noCalls && this.eat(tt.doubleColon)) { const node = this.startNodeAt(startPos, startLoc); @@ -374,7 +374,7 @@ export default class ExpressionParser extends LValParser { base = this.finishNode(node, "CallExpression"); if (possibleAsync && this.shouldParseAsyncArrow()) { - return this.parseAsyncArrowFromCallExpression(this.startNodeAt(startPos, startLoc), node); + return this.parseAsyncArrowFromCallExpression(this.startNodeAt(startPos, startLoc), node, noArrowParamsConversion); } else { this.toReferencedList(node.arguments); } @@ -424,9 +424,9 @@ export default class ExpressionParser extends LValParser { return this.match(tt.arrow); } - parseAsyncArrowFromCallExpression(node: N.ArrowFunctionExpression, call: N.CallExpression): N.ArrowFunctionExpression { + parseAsyncArrowFromCallExpression(node: N.ArrowFunctionExpression, call: N.CallExpression, noArrowParamsConversion?: boolean): N.ArrowFunctionExpression { this.expect(tt.arrow); - return this.parseArrowExpression(node, call.arguments, true); + return this.parseArrowExpression(node, call.arguments, true, noArrowParamsConversion); } // Parse a no-call expression (like argument of `new` or `::` operators). diff --git a/src/plugins/flow.js b/src/plugins/flow.js index d759a98785..d440dae116 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -1572,7 +1572,7 @@ export default (superClass: Class): Class => class extends super } // parse the return type of an async arrow function - let foo = (async (): number => {}); - parseAsyncArrowFromCallExpression(node: N.ArrowFunctionExpression, call: N.CallExpression): N.ArrowFunctionExpression { + parseAsyncArrowFromCallExpression(node: N.ArrowFunctionExpression, call: N.CallExpression, noArrowParamsConversion?: ?boolean): N.ArrowFunctionExpression { if (this.match(tt.colon)) { const oldNoAnonFunctionType = this.state.noAnonFunctionType; this.state.noAnonFunctionType = true; @@ -1580,7 +1580,7 @@ export default (superClass: Class): Class => class extends super this.state.noAnonFunctionType = oldNoAnonFunctionType; } - return super.parseAsyncArrowFromCallExpression(node, call); + return super.parseAsyncArrowFromCallExpression(node, call, noArrowParamsConversion); } // todo description @@ -1693,4 +1693,20 @@ export default (superClass: Class): Class => class extends super noArrowParamsConversion ); } + + parseSubscripts(base: N.Expression, startPos: number, startLoc: Position, noCalls?: boolean, noArrowParamsConversion?: ?boolean): N.Expression { + if ( + base.type === "Identifier" && base.name === "async" && + this.state.noArrowAt.indexOf(startPos) !== -1 + ) { + this.next(); + + const node = this.startNodeAt(startPos, startLoc); + node.callee = base; + node.arguments = this.parseCallExpressionArguments(tt.parenR, false); + base = this.finishNode(node, "CallExpression"); + } + + return super.parseSubscripts(base, startPos, startLoc, noCalls, noArrowParamsConversion); + } }; diff --git a/test/fixtures/flow/regression/issue-58-failing-4/actual.js b/test/fixtures/flow/regression/issue-58-failing-4/actual.js new file mode 100644 index 0000000000..597746898b --- /dev/null +++ b/test/fixtures/flow/regression/issue-58-failing-4/actual.js @@ -0,0 +1,2 @@ +// Invalid LHS parameter +a ? async (b => c) => (d) : f => g; \ No newline at end of file diff --git a/test/fixtures/flow/regression/issue-58-failing-4/options.json b/test/fixtures/flow/regression/issue-58-failing-4/options.json new file mode 100644 index 0000000000..fcc81471ca --- /dev/null +++ b/test/fixtures/flow/regression/issue-58-failing-4/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Invalid left-hand side in arrow function parameters (2:11)" +} \ No newline at end of file diff --git a/test/fixtures/flow/regression/issue-58/actual.js b/test/fixtures/flow/regression/issue-58/actual.js index 44cac8e1f6..7c886c77cb 100644 --- a/test/fixtures/flow/regression/issue-58/actual.js +++ b/test/fixtures/flow/regression/issue-58/actual.js @@ -31,3 +31,9 @@ a ? b ? (c => d) : e => (f => g) : h => i; // a ? (b ? (c => d) : (e => (f => g) // Function as type annotation a ? (b) : (c => d) => e : f; // a ? ((b): (c => d) => e) : f + +// Async functions or calls +a ? async (b) : c => d; // a ? (async(b)) : (c => d) +a ? async (b) : c => d : e; // a ? (async (b): c => d) : e +a ? async (b => c) : d => e; // a ? (async(b => c)) : (d => e) +a ? async (b) => (c => d) : e => f; // a ? (async (b) => c => d) : (e => f) diff --git a/test/fixtures/flow/regression/issue-58/expected.json b/test/fixtures/flow/regression/issue-58/expected.json index 23d799a63a..eb2be36a9a 100644 --- a/test/fixtures/flow/regression/issue-58/expected.json +++ b/test/fixtures/flow/regression/issue-58/expected.json @@ -1,29 +1,29 @@ { "type": "File", "start": 0, - "end": 1457, + "end": 1737, "loc": { "start": { "line": 1, "column": 0 }, "end": { - "line": 33, - "column": 60 + "line": 39, + "column": 75 } }, "program": { "type": "Program", "start": 0, - "end": 1457, + "end": 1737, "loc": { "start": { "line": 1, "column": 0 }, "end": { - "line": 33, - "column": 60 + "line": 39, + "column": 75 } }, "sourceType": "module", @@ -4601,6 +4601,900 @@ "column": 60 } } + }, + { + "type": "CommentLine", + "value": " Async functions or calls", + "start": 1459, + "end": 1486, + "loc": { + "start": { + "line": 35, + "column": 0 + }, + "end": { + "line": 35, + "column": 27 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 1487, + "end": 1510, + "loc": { + "start": { + "line": 36, + "column": 0 + }, + "end": { + "line": 36, + "column": 23 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 1487, + "end": 1509, + "loc": { + "start": { + "line": 36, + "column": 0 + }, + "end": { + "line": 36, + "column": 22 + } + }, + "test": { + "type": "Identifier", + "start": 1487, + "end": 1488, + "loc": { + "start": { + "line": 36, + "column": 0 + }, + "end": { + "line": 36, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "CallExpression", + "start": 1491, + "end": 1500, + "loc": { + "start": { + "line": 36, + "column": 4 + }, + "end": { + "line": 36, + "column": 13 + } + }, + "callee": { + "type": "Identifier", + "start": 1491, + "end": 1496, + "loc": { + "start": { + "line": 36, + "column": 4 + }, + "end": { + "line": 36, + "column": 9 + }, + "identifierName": "async" + }, + "name": "async" + }, + "arguments": [ + { + "type": "Identifier", + "start": 1498, + "end": 1499, + "loc": { + "start": { + "line": 36, + "column": 11 + }, + "end": { + "line": 36, + "column": 12 + }, + "identifierName": "b" + }, + "name": "b" + } + ] + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 1503, + "end": 1509, + "loc": { + "start": { + "line": 36, + "column": 16 + }, + "end": { + "line": 36, + "column": 22 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1503, + "end": 1504, + "loc": { + "start": { + "line": 36, + "column": 16 + }, + "end": { + "line": 36, + "column": 17 + }, + "identifierName": "c" + }, + "name": "c" + } + ], + "body": { + "type": "Identifier", + "start": 1508, + "end": 1509, + "loc": { + "start": { + "line": 36, + "column": 21 + }, + "end": { + "line": 36, + "column": 22 + }, + "identifierName": "d" + }, + "name": "d" + } + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? ((b): (c => d) => e) : f", + "start": 1426, + "end": 1457, + "loc": { + "start": { + "line": 33, + "column": 29 + }, + "end": { + "line": 33, + "column": 60 + } + } + }, + { + "type": "CommentLine", + "value": " Async functions or calls", + "start": 1459, + "end": 1486, + "loc": { + "start": { + "line": 35, + "column": 0 + }, + "end": { + "line": 35, + "column": 27 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? (async(b)) : (c => d)", + "start": 1511, + "end": 1539, + "loc": { + "start": { + "line": 36, + "column": 24 + }, + "end": { + "line": 36, + "column": 52 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 1540, + "end": 1567, + "loc": { + "start": { + "line": 37, + "column": 0 + }, + "end": { + "line": 37, + "column": 27 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 1540, + "end": 1566, + "loc": { + "start": { + "line": 37, + "column": 0 + }, + "end": { + "line": 37, + "column": 26 + } + }, + "test": { + "type": "Identifier", + "start": 1540, + "end": 1541, + "loc": { + "start": { + "line": 37, + "column": 0 + }, + "end": { + "line": 37, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 1544, + "end": 1562, + "loc": { + "start": { + "line": 37, + "column": 4 + }, + "end": { + "line": 37, + "column": 22 + } + }, + "returnType": { + "type": "TypeAnnotation", + "start": 1554, + "end": 1557, + "loc": { + "start": { + "line": 37, + "column": 14 + }, + "end": { + "line": 37, + "column": 17 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 1556, + "end": 1557, + "loc": { + "start": { + "line": 37, + "column": 16 + }, + "end": { + "line": 37, + "column": 17 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 1556, + "end": 1557, + "loc": { + "start": { + "line": 37, + "column": 16 + }, + "end": { + "line": 37, + "column": 17 + }, + "identifierName": "c" + }, + "name": "c" + } + } + }, + "id": null, + "generator": false, + "expression": true, + "async": true, + "params": [ + { + "type": "Identifier", + "start": 1551, + "end": 1552, + "loc": { + "start": { + "line": 37, + "column": 11 + }, + "end": { + "line": 37, + "column": 12 + }, + "identifierName": "b" + }, + "name": "b" + } + ], + "body": { + "type": "Identifier", + "start": 1561, + "end": 1562, + "loc": { + "start": { + "line": 37, + "column": 21 + }, + "end": { + "line": 37, + "column": 22 + }, + "identifierName": "d" + }, + "name": "d" + } + }, + "alternate": { + "type": "Identifier", + "start": 1565, + "end": 1566, + "loc": { + "start": { + "line": 37, + "column": 25 + }, + "end": { + "line": 37, + "column": 26 + }, + "identifierName": "e" + }, + "name": "e" + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? (async(b)) : (c => d)", + "start": 1511, + "end": 1539, + "loc": { + "start": { + "line": 36, + "column": 24 + }, + "end": { + "line": 36, + "column": 52 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? (async (b): c => d) : e", + "start": 1568, + "end": 1598, + "loc": { + "start": { + "line": 37, + "column": 28 + }, + "end": { + "line": 37, + "column": 58 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 1599, + "end": 1627, + "loc": { + "start": { + "line": 38, + "column": 0 + }, + "end": { + "line": 38, + "column": 28 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 1599, + "end": 1626, + "loc": { + "start": { + "line": 38, + "column": 0 + }, + "end": { + "line": 38, + "column": 27 + } + }, + "test": { + "type": "Identifier", + "start": 1599, + "end": 1600, + "loc": { + "start": { + "line": 38, + "column": 0 + }, + "end": { + "line": 38, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "CallExpression", + "start": 1603, + "end": 1617, + "loc": { + "start": { + "line": 38, + "column": 4 + }, + "end": { + "line": 38, + "column": 18 + } + }, + "callee": { + "type": "Identifier", + "start": 1603, + "end": 1608, + "loc": { + "start": { + "line": 38, + "column": 4 + }, + "end": { + "line": 38, + "column": 9 + }, + "identifierName": "async" + }, + "name": "async" + }, + "arguments": [ + { + "type": "ArrowFunctionExpression", + "start": 1610, + "end": 1616, + "loc": { + "start": { + "line": 38, + "column": 11 + }, + "end": { + "line": 38, + "column": 17 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1610, + "end": 1611, + "loc": { + "start": { + "line": 38, + "column": 11 + }, + "end": { + "line": 38, + "column": 12 + }, + "identifierName": "b" + }, + "name": "b" + } + ], + "body": { + "type": "Identifier", + "start": 1615, + "end": 1616, + "loc": { + "start": { + "line": 38, + "column": 16 + }, + "end": { + "line": 38, + "column": 17 + }, + "identifierName": "c" + }, + "name": "c" + } + } + ] + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 1620, + "end": 1626, + "loc": { + "start": { + "line": 38, + "column": 21 + }, + "end": { + "line": 38, + "column": 27 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1620, + "end": 1621, + "loc": { + "start": { + "line": 38, + "column": 21 + }, + "end": { + "line": 38, + "column": 22 + }, + "identifierName": "d" + }, + "name": "d" + } + ], + "body": { + "type": "Identifier", + "start": 1625, + "end": 1626, + "loc": { + "start": { + "line": 38, + "column": 26 + }, + "end": { + "line": 38, + "column": 27 + }, + "identifierName": "e" + }, + "name": "e" + } + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? (async (b): c => d) : e", + "start": 1568, + "end": 1598, + "loc": { + "start": { + "line": 37, + "column": 28 + }, + "end": { + "line": 37, + "column": 58 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? (async(b => c)) : (d => e)", + "start": 1628, + "end": 1661, + "loc": { + "start": { + "line": 38, + "column": 29 + }, + "end": { + "line": 38, + "column": 62 + } + } + } + ] + }, + { + "type": "ExpressionStatement", + "start": 1662, + "end": 1697, + "loc": { + "start": { + "line": 39, + "column": 0 + }, + "end": { + "line": 39, + "column": 35 + } + }, + "expression": { + "type": "ConditionalExpression", + "start": 1662, + "end": 1696, + "loc": { + "start": { + "line": 39, + "column": 0 + }, + "end": { + "line": 39, + "column": 34 + } + }, + "test": { + "type": "Identifier", + "start": 1662, + "end": 1663, + "loc": { + "start": { + "line": 39, + "column": 0 + }, + "end": { + "line": 39, + "column": 1 + }, + "identifierName": "a" + }, + "name": "a", + "leadingComments": null + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 1666, + "end": 1687, + "loc": { + "start": { + "line": 39, + "column": 4 + }, + "end": { + "line": 39, + "column": 25 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": true, + "params": [ + { + "type": "Identifier", + "start": 1673, + "end": 1674, + "loc": { + "start": { + "line": 39, + "column": 11 + }, + "end": { + "line": 39, + "column": 12 + }, + "identifierName": "b" + }, + "name": "b" + } + ], + "body": { + "type": "ArrowFunctionExpression", + "start": 1680, + "end": 1686, + "loc": { + "start": { + "line": 39, + "column": 18 + }, + "end": { + "line": 39, + "column": 24 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1680, + "end": 1681, + "loc": { + "start": { + "line": 39, + "column": 18 + }, + "end": { + "line": 39, + "column": 19 + }, + "identifierName": "c" + }, + "name": "c" + } + ], + "body": { + "type": "Identifier", + "start": 1685, + "end": 1686, + "loc": { + "start": { + "line": 39, + "column": 23 + }, + "end": { + "line": 39, + "column": 24 + }, + "identifierName": "d" + }, + "name": "d" + }, + "extra": { + "parenthesized": true, + "parenStart": 1679 + } + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 1690, + "end": 1696, + "loc": { + "start": { + "line": 39, + "column": 28 + }, + "end": { + "line": 39, + "column": 34 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1690, + "end": 1691, + "loc": { + "start": { + "line": 39, + "column": 28 + }, + "end": { + "line": 39, + "column": 29 + }, + "identifierName": "e" + }, + "name": "e" + } + ], + "body": { + "type": "Identifier", + "start": 1695, + "end": 1696, + "loc": { + "start": { + "line": 39, + "column": 33 + }, + "end": { + "line": 39, + "column": 34 + }, + "identifierName": "f" + }, + "name": "f" + } + }, + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? (async(b => c)) : (d => e)", + "start": 1628, + "end": 1661, + "loc": { + "start": { + "line": 38, + "column": 29 + }, + "end": { + "line": 38, + "column": 62 + } + } + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " a ? (async (b) => c => d) : (e => f)", + "start": 1698, + "end": 1737, + "loc": { + "start": { + "line": 39, + "column": 36 + }, + "end": { + "line": 39, + "column": 75 + } + } } ] } @@ -5007,6 +5901,86 @@ "column": 60 } } + }, + { + "type": "CommentLine", + "value": " Async functions or calls", + "start": 1459, + "end": 1486, + "loc": { + "start": { + "line": 35, + "column": 0 + }, + "end": { + "line": 35, + "column": 27 + } + } + }, + { + "type": "CommentLine", + "value": " a ? (async(b)) : (c => d)", + "start": 1511, + "end": 1539, + "loc": { + "start": { + "line": 36, + "column": 24 + }, + "end": { + "line": 36, + "column": 52 + } + } + }, + { + "type": "CommentLine", + "value": " a ? (async (b): c => d) : e", + "start": 1568, + "end": 1598, + "loc": { + "start": { + "line": 37, + "column": 28 + }, + "end": { + "line": 37, + "column": 58 + } + } + }, + { + "type": "CommentLine", + "value": " a ? (async(b => c)) : (d => e)", + "start": 1628, + "end": 1661, + "loc": { + "start": { + "line": 38, + "column": 29 + }, + "end": { + "line": 38, + "column": 62 + } + } + }, + { + "type": "CommentLine", + "value": " a ? (async (b) => c => d) : (e => f)", + "start": 1698, + "end": 1737, + "loc": { + "start": { + "line": 39, + "column": 36 + }, + "end": { + "line": 39, + "column": 75 + } + } } ] } \ No newline at end of file From b43240c6367d95dabde47106a3ae026e0e24fb68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Tue, 20 Jun 2017 21:08:26 +0200 Subject: [PATCH 5/8] Add test from prettier https://github.com/prettier/prettier/issues/2194 --- .../flow/regression/issue-58/actual.js | 5 + .../flow/regression/issue-58/expected.json | 577 +++++++++++++++++- 2 files changed, 576 insertions(+), 6 deletions(-) diff --git a/test/fixtures/flow/regression/issue-58/actual.js b/test/fixtures/flow/regression/issue-58/actual.js index 7c886c77cb..1442ec300f 100644 --- a/test/fixtures/flow/regression/issue-58/actual.js +++ b/test/fixtures/flow/regression/issue-58/actual.js @@ -37,3 +37,8 @@ a ? async (b) : c => d; // a ? (async(b)) : (c => d) a ? async (b) : c => d : e; // a ? (async (b): c => d) : e a ? async (b => c) : d => e; // a ? (async(b => c)) : (d => e) a ? async (b) => (c => d) : e => f; // a ? (async (b) => c => d) : (e => f) + +// https://github.com/prettier/prettier/issues/2194 +let icecream = what == "cone" + ? p => (!!p ? `here's your ${p} cone` : `just the empty cone for you`) + : p => `here's your ${p} ${what}`; diff --git a/test/fixtures/flow/regression/issue-58/expected.json b/test/fixtures/flow/regression/issue-58/expected.json index eb2be36a9a..44381d5f03 100644 --- a/test/fixtures/flow/regression/issue-58/expected.json +++ b/test/fixtures/flow/regression/issue-58/expected.json @@ -1,29 +1,29 @@ { "type": "File", "start": 0, - "end": 1737, + "end": 1930, "loc": { "start": { "line": 1, "column": 0 }, "end": { - "line": 39, - "column": 75 + "line": 44, + "column": 36 } }, "program": { "type": "Program", "start": 0, - "end": 1737, + "end": 1930, "loc": { "start": { "line": 1, "column": 0 }, "end": { - "line": 39, - "column": 75 + "line": 44, + "column": 36 } }, "sourceType": "module", @@ -5495,6 +5495,555 @@ "column": 75 } } + }, + { + "type": "CommentLine", + "value": " https://github.com/prettier/prettier/issues/2194", + "start": 1739, + "end": 1790, + "loc": { + "start": { + "line": 41, + "column": 0 + }, + "end": { + "line": 41, + "column": 51 + } + } + } + ] + }, + { + "type": "VariableDeclaration", + "start": 1791, + "end": 1930, + "loc": { + "start": { + "line": 42, + "column": 0 + }, + "end": { + "line": 44, + "column": 36 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 1795, + "end": 1929, + "loc": { + "start": { + "line": 42, + "column": 4 + }, + "end": { + "line": 44, + "column": 35 + } + }, + "id": { + "type": "Identifier", + "start": 1795, + "end": 1803, + "loc": { + "start": { + "line": 42, + "column": 4 + }, + "end": { + "line": 42, + "column": 12 + }, + "identifierName": "icecream" + }, + "name": "icecream", + "leadingComments": null + }, + "init": { + "type": "ConditionalExpression", + "start": 1806, + "end": 1929, + "loc": { + "start": { + "line": 42, + "column": 15 + }, + "end": { + "line": 44, + "column": 35 + } + }, + "test": { + "type": "BinaryExpression", + "start": 1806, + "end": 1820, + "loc": { + "start": { + "line": 42, + "column": 15 + }, + "end": { + "line": 42, + "column": 29 + } + }, + "left": { + "type": "Identifier", + "start": 1806, + "end": 1810, + "loc": { + "start": { + "line": 42, + "column": 15 + }, + "end": { + "line": 42, + "column": 19 + }, + "identifierName": "what" + }, + "name": "what" + }, + "operator": "==", + "right": { + "type": "StringLiteral", + "start": 1814, + "end": 1820, + "loc": { + "start": { + "line": 42, + "column": 23 + }, + "end": { + "line": 42, + "column": 29 + } + }, + "extra": { + "rawValue": "cone", + "raw": "\"cone\"" + }, + "value": "cone" + } + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start": 1825, + "end": 1893, + "loc": { + "start": { + "line": 43, + "column": 4 + }, + "end": { + "line": 43, + "column": 72 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1825, + "end": 1826, + "loc": { + "start": { + "line": 43, + "column": 4 + }, + "end": { + "line": 43, + "column": 5 + }, + "identifierName": "p" + }, + "name": "p" + } + ], + "body": { + "type": "ConditionalExpression", + "start": 1831, + "end": 1892, + "loc": { + "start": { + "line": 43, + "column": 10 + }, + "end": { + "line": 43, + "column": 71 + } + }, + "test": { + "type": "UnaryExpression", + "start": 1831, + "end": 1834, + "loc": { + "start": { + "line": 43, + "column": 10 + }, + "end": { + "line": 43, + "column": 13 + } + }, + "operator": "!", + "prefix": true, + "argument": { + "type": "UnaryExpression", + "start": 1832, + "end": 1834, + "loc": { + "start": { + "line": 43, + "column": 11 + }, + "end": { + "line": 43, + "column": 13 + } + }, + "operator": "!", + "prefix": true, + "argument": { + "type": "Identifier", + "start": 1833, + "end": 1834, + "loc": { + "start": { + "line": 43, + "column": 12 + }, + "end": { + "line": 43, + "column": 13 + }, + "identifierName": "p" + }, + "name": "p" + }, + "extra": { + "parenthesizedArgument": false + } + }, + "extra": { + "parenthesizedArgument": false + } + }, + "consequent": { + "type": "TemplateLiteral", + "start": 1837, + "end": 1860, + "loc": { + "start": { + "line": 43, + "column": 16 + }, + "end": { + "line": 43, + "column": 39 + } + }, + "expressions": [ + { + "type": "Identifier", + "start": 1852, + "end": 1853, + "loc": { + "start": { + "line": 43, + "column": 31 + }, + "end": { + "line": 43, + "column": 32 + }, + "identifierName": "p" + }, + "name": "p" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 1838, + "end": 1850, + "loc": { + "start": { + "line": 43, + "column": 17 + }, + "end": { + "line": 43, + "column": 29 + } + }, + "value": { + "raw": "here's your ", + "cooked": "here's your " + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 1854, + "end": 1859, + "loc": { + "start": { + "line": 43, + "column": 33 + }, + "end": { + "line": 43, + "column": 38 + } + }, + "value": { + "raw": " cone", + "cooked": " cone" + }, + "tail": true + } + ] + }, + "alternate": { + "type": "TemplateLiteral", + "start": 1863, + "end": 1892, + "loc": { + "start": { + "line": 43, + "column": 42 + }, + "end": { + "line": 43, + "column": 71 + } + }, + "expressions": [], + "quasis": [ + { + "type": "TemplateElement", + "start": 1864, + "end": 1891, + "loc": { + "start": { + "line": 43, + "column": 43 + }, + "end": { + "line": 43, + "column": 70 + } + }, + "value": { + "raw": "just the empty cone for you", + "cooked": "just the empty cone for you" + }, + "tail": true + } + ] + }, + "extra": { + "parenthesized": true, + "parenStart": 1830 + } + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start": 1898, + "end": 1929, + "loc": { + "start": { + "line": 44, + "column": 4 + }, + "end": { + "line": 44, + "column": 35 + } + }, + "id": null, + "generator": false, + "expression": true, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 1898, + "end": 1899, + "loc": { + "start": { + "line": 44, + "column": 4 + }, + "end": { + "line": 44, + "column": 5 + }, + "identifierName": "p" + }, + "name": "p" + } + ], + "body": { + "type": "TemplateLiteral", + "start": 1903, + "end": 1929, + "loc": { + "start": { + "line": 44, + "column": 9 + }, + "end": { + "line": 44, + "column": 35 + } + }, + "expressions": [ + { + "type": "Identifier", + "start": 1918, + "end": 1919, + "loc": { + "start": { + "line": 44, + "column": 24 + }, + "end": { + "line": 44, + "column": 25 + }, + "identifierName": "p" + }, + "name": "p" + }, + { + "type": "Identifier", + "start": 1923, + "end": 1927, + "loc": { + "start": { + "line": 44, + "column": 29 + }, + "end": { + "line": 44, + "column": 33 + }, + "identifierName": "what" + }, + "name": "what" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 1904, + "end": 1916, + "loc": { + "start": { + "line": 44, + "column": 10 + }, + "end": { + "line": 44, + "column": 22 + } + }, + "value": { + "raw": "here's your ", + "cooked": "here's your " + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 1920, + "end": 1921, + "loc": { + "start": { + "line": 44, + "column": 26 + }, + "end": { + "line": 44, + "column": 27 + } + }, + "value": { + "raw": " ", + "cooked": " " + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 1928, + "end": 1928, + "loc": { + "start": { + "line": 44, + "column": 34 + }, + "end": { + "line": 44, + "column": 34 + } + }, + "value": { + "raw": "", + "cooked": "" + }, + "tail": true + } + ] + } + } + }, + "leadingComments": null + } + ], + "kind": "let", + "leadingComments": [ + { + "type": "CommentLine", + "value": " a ? (async (b) => c => d) : (e => f)", + "start": 1698, + "end": 1737, + "loc": { + "start": { + "line": 39, + "column": 36 + }, + "end": { + "line": 39, + "column": 75 + } + } + }, + { + "type": "CommentLine", + "value": " https://github.com/prettier/prettier/issues/2194", + "start": 1739, + "end": 1790, + "loc": { + "start": { + "line": 41, + "column": 0 + }, + "end": { + "line": 41, + "column": 51 + } + } } ] } @@ -5981,6 +6530,22 @@ "column": 75 } } + }, + { + "type": "CommentLine", + "value": " https://github.com/prettier/prettier/issues/2194", + "start": 1739, + "end": 1790, + "loc": { + "start": { + "line": 41, + "column": 0 + }, + "end": { + "line": 41, + "column": 51 + } + } } ] } \ No newline at end of file From 2f60733fa99d0728bbc60315f8c4b1c62d20194d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Tue, 20 Jun 2017 21:12:22 +0200 Subject: [PATCH 6/8] Don't check arrow params if they are valid at the first attemp --- src/plugins/flow.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/flow.js b/src/plugins/flow.js index d440dae116..b09d147465 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -1142,9 +1142,9 @@ export default (superClass: Class): Class => class extends super this.state.noArrowAt = noArrowAt.concat(valid[0].start); ({ consequent, failed } = this.tryParseConditionalConsequent()); } - } - this.getArrowLikeExpressions(consequent, true); + this.getArrowLikeExpressions(consequent, true); + } this.state.noArrowAt = originalNoArrowAt; this.expect(tt.colon); From 74ef24e0095dccd70e32849fba5faa77b4d9f9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sun, 25 Jun 2017 23:51:07 +0200 Subject: [PATCH 7/8] Use state instead of relying on the "noArrowParamsConversion" parameter --- src/parser/expression.js | 64 +++++++++++++++++++++------------------- src/plugins/flow.js | 56 +++++++++++++++++++++++++++++++---- src/tokenizer/state.js | 9 ++++++ 3 files changed, 92 insertions(+), 37 deletions(-) diff --git a/src/parser/expression.js b/src/parser/expression.js index 9cee233e67..3188b35565 100644 --- a/src/parser/expression.js +++ b/src/parser/expression.js @@ -1057,17 +1057,20 @@ export default class ExpressionParser extends LValParser { parseArrowExpression(node: N.ArrowFunctionExpression, params: N.Expression[], isAsync?: boolean, noArrowParamsConversion?: boolean): N.ArrowFunctionExpression { this.initFunction(node, isAsync); - - if (noArrowParamsConversion) { - node.params = params; - } else { - node.params = this.toAssignableList(params, true, "arrow function parameters"); - } - + this.setArrowFunctionParameters(node, params, noArrowParamsConversion); this.parseFunctionBody(node, true, noArrowParamsConversion); return this.finishNode(node, "ArrowFunctionExpression"); } + setArrowFunctionParameters( + node: N.ArrowFunctionExpression, + params: N.Expression[], + // eslint-disable-next-line no-unused-vars + noArrowParamsConversion?: boolean + ): N.ArrowFunctionExpression { + node.params = this.toAssignableList(params, true, "arrow function parameters"); + } + isStrictBody(node: { body: N.BlockStatement }, isExpression?: boolean): boolean { if (!isExpression && node.body.directives.length) { for (const directive of node.body.directives) { @@ -1102,41 +1105,40 @@ export default class ExpressionParser extends LValParser { } this.state.inAsync = oldInAsync; + this.checkFunctionNameAndParams(node, allowExpression); + } + + checkFunctionNameAndParams( + node: N.Function, + isArrowFunction?: boolean + ): void { // If this is a strict mode function, verify that argument names // are not repeated, and it does not try to bind the words `eval` // or `arguments`. - const isStrict = this.isStrictBody(node, isExpression); + const isStrict = this.isStrictBody(node, node.expression); + const checkLVal = this.state.strict || isStrict || isArrowFunction; if (isStrict && node.id && node.id.type === "Identifier" && node.id.name === "yield") { this.raise(node.id.start, "Binding yield in strict mode"); } - // If this is an arrow function, check its parameter only if they are - // converted to assignable nodes - if (allowExpression ? !noArrowParamsConversion : this.state.strict || isStrict) { - this.checkFunctionLValues(node); - } - } - - // Checks that functions parameters and function name (if it exists) are valid - // left hand side values. - checkFunctionLValues(node: N.Function): void { - const nameHash = Object.create(null); - const oldStrict = this.state.strict; - const isStrict = this.isStrictBody(node, node.expression); + if (checkLVal) { + const nameHash = Object.create(null); + const oldStrict = this.state.strict; - if (isStrict) this.state.strict = true; - if (node.id) { - this.checkLVal(node.id, true, undefined, "function name"); - } - for (const param of node.params) { - if (isStrict && param.type !== "Identifier") { - this.raise(param.start, "Non-simple parameter in strict mode"); + if (isStrict) this.state.strict = true; + if (node.id) { + this.checkLVal(node.id, true, undefined, "function name"); + } + for (const param of node.params) { + if (isStrict && param.type !== "Identifier") { + this.raise(param.start, "Non-simple parameter in strict mode"); + } + this.checkLVal(param, true, nameHash, "function parameter list"); } - this.checkLVal(param, true, nameHash, "function parameter list"); - } - this.state.strict = oldStrict; + this.state.strict = oldStrict; + } } // Parses a comma-separated list of expressions, and returns them as diff --git a/src/plugins/flow.js b/src/plugins/flow.js index b09d147465..20cddc0508 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -1016,9 +1016,15 @@ export default (superClass: Class): Class => class extends super // Overrides // ================================== - // plain function return types: function name(): string {} parseFunctionBody(node: N.Function, allowExpression?: boolean, noArrowParamsConversion?: boolean): void { - if (this.match(tt.colon) && !allowExpression) { + if (allowExpression && this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) { + this.state.noArrowParamsConversionAt.push(this.state.start); + super.parseFunctionBody(node, allowExpression); + this.state.noArrowParamsConversionAt.pop(); + + return; + } else if (this.match(tt.colon)) { + // plain function return types: function name(): string {} // if allowExpression is true then we're parsing an arrow function and if // there's a return type then it's been handled elsewhere const typeNode = this.startNode(); @@ -1151,18 +1157,24 @@ export default (superClass: Class): Class => class extends super node.test = expr; node.consequent = consequent; - node.alternate = this.parseMaybeAssign(noIn, undefined, undefined, undefined, noArrowParamsConversion); + node.alternate = this.forwardNoArrowParamsConversionAt(node, () => ( + this.parseMaybeAssign(noIn, undefined, undefined, undefined, noArrowParamsConversion) + )); return this.finishNode(node, "ConditionalExpression"); } tryParseConditionalConsequent(): { consequent: N.Expression, failed: boolean } { + this.state.noArrowParamsConversionAt.push(this.state.start); + const consequent = this.parseMaybeAssign( undefined, undefined, undefined, undefined, /* noArrowParamsConversion */ true ); const failed = !this.match(tt.colon); + this.state.noArrowParamsConversionAt.pop(); + return { consequent, failed }; } @@ -1183,7 +1195,8 @@ export default (superClass: Class): Class => class extends super if (node.typeParameters || !node.returnType) { // This is an arrow expression without ambiguity, so check its parameters this.toAssignableList(node.params, true, "arrow function parameters"); - this.checkFunctionLValues(node); + // Use super's method to force the parameters to be checked + super.checkFunctionNameAndParams(node, true); } else { arrows.push(node); } @@ -1211,6 +1224,19 @@ export default (superClass: Class): Class => class extends super }); } + forwardNoArrowParamsConversionAt(node: N.Node, parse: () => T): T { + let result: T; + if (this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) { + this.state.noArrowParamsConversionAt.push(this.state.start); + result = parse(); + this.state.noArrowParamsConversionAt.pop(this.state.start); + } else { + result = parse(); + } + + return result; + } + parseParenItem(node: N.Expression, startPos: number, startLoc: Position): N.Expression { node = super.parseParenItem(node, startPos, startLoc); if (this.eat(tt.question)) { @@ -1626,8 +1652,9 @@ export default (superClass: Class): Class => class extends super let typeParameters; try { typeParameters = this.flowParseTypeParameterDeclaration(); - - arrowExpression = super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos, noArrowParamsConversion); + arrowExpression = this.forwardNoArrowParamsConversionAt(typeParameters, () => ( + super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos, noArrowParamsConversion) + )); arrowExpression.typeParameters = typeParameters; this.resetStartLocationFromNode(arrowExpression, typeParameters); } catch (err) { @@ -1687,6 +1714,23 @@ export default (superClass: Class): Class => class extends super return this.match(tt.colon) || super.shouldParseArrow(); } + setArrowFunctionParameters(node: N.ArrowFunctionExpression, params: N.Expression[], noArrowParamsConversion ?: boolean): N.ArrowFunctionExpression { + if (this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) { + node.params = params; + return node; + } + + return super.setArrowFunctionParameters(node, params); + } + + checkFunctionNameAndParams(node: N.Function, isArrowFunction?: boolean): void { + if (isArrowFunction && this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) { + return; + } + + return super.checkFunctionNameAndParams(node, isArrowFunction); + } + parseParenAndDistinguishExpression(canBeArrow: boolean, noArrowParamsConversion?: boolean): N.Expression { return super.parseParenAndDistinguishExpression( canBeArrow && this.state.noArrowAt.indexOf(this.state.start) === -1, diff --git a/src/tokenizer/state.js b/src/tokenizer/state.js index 790fca609b..6f031272f2 100644 --- a/src/tokenizer/state.js +++ b/src/tokenizer/state.js @@ -19,6 +19,7 @@ export default class State { this.potentialArrowAt = -1; this.noArrowAt = []; + this.noArrowParamsConversionAt = []; this.inMethod = this.inFunction = @@ -81,6 +82,14 @@ export default class State { // ^ noArrowAt: number[]; + // Used to signify the start of an expression whose params, if it looks like + // an arrow function, shouldn't be converted to assignable nodes. + // This is used to defer the validation of typed arrow functions inside + // conditional expressions. + // e.g. a ? (b) : c => d + // ^ + noArrowParamsConversionAt: number[]; + // Flags to track whether we are in a function, a generator. inFunction: boolean; inGenerator: boolean; From 8541f2c54fe0ebdecec1a48699ea0c9cd1f343e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Mon, 26 Jun 2017 00:00:41 +0200 Subject: [PATCH 8/8] Remove noArrowParamsConversion --- src/parser/expression.js | 66 ++++++++++++++++++---------------------- src/plugins/estree.js | 4 +-- src/plugins/flow.js | 36 ++++++++++------------ src/plugins/jsx/index.js | 4 +-- 4 files changed, 49 insertions(+), 61 deletions(-) diff --git a/src/parser/expression.js b/src/parser/expression.js index 3188b35565..f631999656 100644 --- a/src/parser/expression.js +++ b/src/parser/expression.js @@ -96,7 +96,7 @@ export default class ExpressionParser extends LValParser { // Parse an assignment expression. This includes applications of // operators like `+=`. - parseMaybeAssign(noIn?: ?boolean, refShorthandDefaultPos?: ?Pos, afterLeftParse?: Function, refNeedsArrowPos?: ?Pos, noArrowParamsConversion?: boolean): N.Expression { + parseMaybeAssign(noIn?: ?boolean, refShorthandDefaultPos?: ?Pos, afterLeftParse?: Function, refNeedsArrowPos?: ?Pos): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; if (this.match(tt._yield) && this.state.inGenerator) { @@ -117,7 +117,7 @@ export default class ExpressionParser extends LValParser { this.state.potentialArrowAt = this.state.start; } - let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos, refNeedsArrowPos, noArrowParamsConversion); + let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos, refNeedsArrowPos); if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc); if (this.state.type.isAssign) { const node = this.startNodeAt(startPos, startLoc); @@ -151,13 +151,13 @@ export default class ExpressionParser extends LValParser { // Parse a ternary conditional (`?:`) operator. - parseMaybeConditional(noIn: ?boolean, refShorthandDefaultPos: Pos, refNeedsArrowPos?: ?Pos, noArrowParamsConversion?: boolean): N.Expression { + parseMaybeConditional(noIn: ?boolean, refShorthandDefaultPos: Pos, refNeedsArrowPos?: ?Pos): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; - const expr = this.parseExprOps(noIn, refShorthandDefaultPos, noArrowParamsConversion); + const expr = this.parseExprOps(noIn, refShorthandDefaultPos); if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; - return this.parseConditional(expr, noIn, startPos, startLoc, refNeedsArrowPos, noArrowParamsConversion); + return this.parseConditional(expr, noIn, startPos, startLoc, refNeedsArrowPos); } parseConditional( @@ -167,9 +167,7 @@ export default class ExpressionParser extends LValParser { startLoc: Position, // FIXME: Disabling this for now since can't seem to get it to play nicely // eslint-disable-next-line no-unused-vars - refNeedsArrowPos?: ?Pos, - // eslint-disable-next-line no-unused-vars - noArrowParamsConversion?: boolean + refNeedsArrowPos?: ?Pos ): N.Expression { if (this.eat(tt.question)) { const node = this.startNodeAt(startPos, startLoc); @@ -184,10 +182,10 @@ export default class ExpressionParser extends LValParser { // Start the precedence parser. - parseExprOps(noIn: ?boolean, refShorthandDefaultPos: Pos, noArrowParamsConversion?: boolean): N.Expression { + parseExprOps(noIn: ?boolean, refShorthandDefaultPos: Pos): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; - const expr = this.parseMaybeUnary(refShorthandDefaultPos, noArrowParamsConversion); + const expr = this.parseMaybeUnary(refShorthandDefaultPos); if (refShorthandDefaultPos && refShorthandDefaultPos.start) { return expr; } else { @@ -235,7 +233,7 @@ export default class ExpressionParser extends LValParser { // Parse unary operators, both prefix and postfix. - parseMaybeUnary(refShorthandDefaultPos: ?Pos, noArrowParamsConversion?: boolean): N.Expression { + parseMaybeUnary(refShorthandDefaultPos: ?Pos): N.Expression { if (this.state.type.prefix) { const node = this.startNode(); const update = this.match(tt.incDec); @@ -263,7 +261,7 @@ export default class ExpressionParser extends LValParser { const startPos = this.state.start; const startLoc = this.state.startLoc; - let expr = this.parseExprSubscripts(refShorthandDefaultPos, noArrowParamsConversion); + let expr = this.parseExprSubscripts(refShorthandDefaultPos); if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; while (this.state.type.postfix && !this.canInsertSemicolon()) { const node = this.startNodeAt(startPos, startLoc); @@ -279,11 +277,11 @@ export default class ExpressionParser extends LValParser { // Parse call, dot, and `[]`-subscript expressions. - parseExprSubscripts(refShorthandDefaultPos: ?Pos, noArrowParamsConversion?: boolean): N.Expression { + parseExprSubscripts(refShorthandDefaultPos: ?Pos): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; const potentialArrowAt = this.state.potentialArrowAt; - const expr = this.parseExprAtom(refShorthandDefaultPos, noArrowParamsConversion); + const expr = this.parseExprAtom(refShorthandDefaultPos); if (expr.type === "ArrowFunctionExpression" && expr.start === potentialArrowAt) { return expr; @@ -293,10 +291,10 @@ export default class ExpressionParser extends LValParser { return expr; } - return this.parseSubscripts(expr, startPos, startLoc, undefined, noArrowParamsConversion); + return this.parseSubscripts(expr, startPos, startLoc); } - parseSubscripts(base: N.Expression, startPos: number, startLoc: Position, noCalls?: boolean, noArrowParamsConversion?: ?boolean): N.Expression { + parseSubscripts(base: N.Expression, startPos: number, startLoc: Position, noCalls?: boolean): N.Expression { for (;;) { if (!noCalls && this.eat(tt.doubleColon)) { const node = this.startNodeAt(startPos, startLoc); @@ -374,7 +372,7 @@ export default class ExpressionParser extends LValParser { base = this.finishNode(node, "CallExpression"); if (possibleAsync && this.shouldParseAsyncArrow()) { - return this.parseAsyncArrowFromCallExpression(this.startNodeAt(startPos, startLoc), node, noArrowParamsConversion); + return this.parseAsyncArrowFromCallExpression(this.startNodeAt(startPos, startLoc), node); } else { this.toReferencedList(node.arguments); } @@ -424,9 +422,9 @@ export default class ExpressionParser extends LValParser { return this.match(tt.arrow); } - parseAsyncArrowFromCallExpression(node: N.ArrowFunctionExpression, call: N.CallExpression, noArrowParamsConversion?: boolean): N.ArrowFunctionExpression { + parseAsyncArrowFromCallExpression(node: N.ArrowFunctionExpression, call: N.CallExpression): N.ArrowFunctionExpression { this.expect(tt.arrow); - return this.parseArrowExpression(node, call.arguments, true, noArrowParamsConversion); + return this.parseArrowExpression(node, call.arguments, true); } // Parse a no-call expression (like argument of `new` or `::` operators). @@ -442,7 +440,7 @@ export default class ExpressionParser extends LValParser { // `new`, or an expression wrapped in punctuation like `()`, `[]`, // or `{}`. - parseExprAtom(refShorthandDefaultPos?: ?Pos, noArrowParamsConversion?: boolean): N.Expression { + parseExprAtom(refShorthandDefaultPos?: ?Pos): N.Expression { const canBeArrow = this.state.potentialArrowAt === this.state.start; let node; @@ -509,7 +507,7 @@ export default class ExpressionParser extends LValParser { } if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) { - return this.parseArrowExpression(node, [id], undefined, noArrowParamsConversion); + return this.parseArrowExpression(node, [id]); } return id; @@ -553,7 +551,7 @@ export default class ExpressionParser extends LValParser { return this.finishNode(node, "BooleanLiteral"); case tt.parenL: - return this.parseParenAndDistinguishExpression(canBeArrow, noArrowParamsConversion); + return this.parseParenAndDistinguishExpression(canBeArrow); case tt.bracketL: node = this.startNode(); @@ -667,7 +665,7 @@ export default class ExpressionParser extends LValParser { return val; } - parseParenAndDistinguishExpression(canBeArrow: boolean, noArrowParamsConversion?: boolean): N.Expression { + parseParenAndDistinguishExpression(canBeArrow: boolean): N.Expression { const startPos = this.state.start; const startLoc = this.state.startLoc; @@ -715,7 +713,7 @@ export default class ExpressionParser extends LValParser { if (param.extra && param.extra.parenthesized) this.unexpected(param.extra.parenStart); } - return this.parseArrowExpression(arrowNode, exprList, false, noArrowParamsConversion); + return this.parseArrowExpression(arrowNode, exprList); } if (!exprList.length) { @@ -1055,19 +1053,14 @@ export default class ExpressionParser extends LValParser { // Parse arrow function expression with given parameters. - parseArrowExpression(node: N.ArrowFunctionExpression, params: N.Expression[], isAsync?: boolean, noArrowParamsConversion?: boolean): N.ArrowFunctionExpression { + parseArrowExpression(node: N.ArrowFunctionExpression, params: N.Expression[], isAsync?: boolean): N.ArrowFunctionExpression { this.initFunction(node, isAsync); - this.setArrowFunctionParameters(node, params, noArrowParamsConversion); - this.parseFunctionBody(node, true, noArrowParamsConversion); + this.setArrowFunctionParameters(node, params); + this.parseFunctionBody(node, true); return this.finishNode(node, "ArrowFunctionExpression"); } - setArrowFunctionParameters( - node: N.ArrowFunctionExpression, - params: N.Expression[], - // eslint-disable-next-line no-unused-vars - noArrowParamsConversion?: boolean - ): N.ArrowFunctionExpression { + setArrowFunctionParameters(node: N.ArrowFunctionExpression, params: N.Expression[]): N.ArrowFunctionExpression { node.params = this.toAssignableList(params, true, "arrow function parameters"); } @@ -1084,13 +1077,13 @@ export default class ExpressionParser extends LValParser { } // Parse function body and check parameters. - parseFunctionBody(node: N.Function, allowExpression?: boolean, noArrowParamsConversion?: boolean): void { + parseFunctionBody(node: N.Function, allowExpression?: boolean): void { const isExpression = allowExpression && !this.match(tt.braceL); const oldInAsync = this.state.inAsync; this.state.inAsync = node.async; if (isExpression) { - node.body = this.parseMaybeAssign(undefined, undefined, undefined, undefined, noArrowParamsConversion); + node.body = this.parseMaybeAssign(); node.expression = true; } else { // Start a new scope with regard to labels and the `inFunction` @@ -1116,6 +1109,7 @@ export default class ExpressionParser extends LValParser { // are not repeated, and it does not try to bind the words `eval` // or `arguments`. const isStrict = this.isStrictBody(node, node.expression); + // Also check for arrow functions const checkLVal = this.state.strict || isStrict || isArrowFunction; if (isStrict && node.id && node.id.type === "Identifier" && node.id.name === "yield") { @@ -1125,7 +1119,6 @@ export default class ExpressionParser extends LValParser { if (checkLVal) { const nameHash = Object.create(null); const oldStrict = this.state.strict; - if (isStrict) this.state.strict = true; if (node.id) { this.checkLVal(node.id, true, undefined, "function name"); @@ -1136,7 +1129,6 @@ export default class ExpressionParser extends LValParser { } this.checkLVal(param, true, nameHash, "function parameter list"); } - this.state.strict = oldStrict; } } diff --git a/src/plugins/estree.js b/src/plugins/estree.js index 3988c0c32c..61e2a8e69c 100644 --- a/src/plugins/estree.js +++ b/src/plugins/estree.js @@ -164,7 +164,7 @@ export default (superClass: Class): Class => class extends super classBody.body.push(this.finishNode(method, "MethodDefinition")); } - parseExprAtom(refShorthandDefaultPos?: ?Pos, noArrowParamsConversion?: boolean): N.Expression { + parseExprAtom(refShorthandDefaultPos?: ?Pos): N.Expression { switch (this.state.type) { case tt.regexp: return this.estreeParseRegExpLiteral(this.state.value); @@ -183,7 +183,7 @@ export default (superClass: Class): Class => class extends super return this.estreeParseLiteral(false); default: - return super.parseExprAtom(refShorthandDefaultPos, noArrowParamsConversion); + return super.parseExprAtom(refShorthandDefaultPos); } } diff --git a/src/plugins/flow.js b/src/plugins/flow.js index 20cddc0508..d8f30d3d20 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -1016,7 +1016,7 @@ export default (superClass: Class): Class => class extends super // Overrides // ================================== - parseFunctionBody(node: N.Function, allowExpression?: boolean, noArrowParamsConversion?: boolean): void { + parseFunctionBody(node: N.Function, allowExpression?: boolean): void { if (allowExpression && this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) { this.state.noArrowParamsConversionAt.push(this.state.start); super.parseFunctionBody(node, allowExpression); @@ -1036,7 +1036,7 @@ export default (superClass: Class): Class => class extends super : null; } - return super.parseFunctionBody(node, allowExpression, noArrowParamsConversion); + return super.parseFunctionBody(node, allowExpression); } // interfaces @@ -1088,7 +1088,7 @@ export default (superClass: Class): Class => class extends super return super.isExportDefaultSpecifier(); } - parseConditional(expr: N.Expression, noIn: ?boolean, startPos: number, startLoc: Position, refNeedsArrowPos?: ?Pos, noArrowParamsConversion?: boolean): N.Expression { + parseConditional(expr: N.Expression, noIn: ?boolean, startPos: number, startLoc: Position, refNeedsArrowPos?: ?Pos): N.Expression { if (!this.match(tt.question)) return expr; // only do the expensive clone if there is a question mark @@ -1158,7 +1158,7 @@ export default (superClass: Class): Class => class extends super node.test = expr; node.consequent = consequent; node.alternate = this.forwardNoArrowParamsConversionAt(node, () => ( - this.parseMaybeAssign(noIn, undefined, undefined, undefined, noArrowParamsConversion) + this.parseMaybeAssign(noIn, undefined, undefined, undefined) )); return this.finishNode(node, "ConditionalExpression"); @@ -1167,10 +1167,7 @@ export default (superClass: Class): Class => class extends super tryParseConditionalConsequent(): { consequent: N.Expression, failed: boolean } { this.state.noArrowParamsConversionAt.push(this.state.start); - const consequent = this.parseMaybeAssign( - undefined, undefined, undefined, undefined, - /* noArrowParamsConversion */ true - ); + const consequent = this.parseMaybeAssign(); const failed = !this.match(tt.colon); this.state.noArrowParamsConversionAt.pop(); @@ -1598,7 +1595,7 @@ export default (superClass: Class): Class => class extends super } // parse the return type of an async arrow function - let foo = (async (): number => {}); - parseAsyncArrowFromCallExpression(node: N.ArrowFunctionExpression, call: N.CallExpression, noArrowParamsConversion?: ?boolean): N.ArrowFunctionExpression { + parseAsyncArrowFromCallExpression(node: N.ArrowFunctionExpression, call: N.CallExpression): N.ArrowFunctionExpression { if (this.match(tt.colon)) { const oldNoAnonFunctionType = this.state.noAnonFunctionType; this.state.noAnonFunctionType = true; @@ -1606,7 +1603,7 @@ export default (superClass: Class): Class => class extends super this.state.noAnonFunctionType = oldNoAnonFunctionType; } - return super.parseAsyncArrowFromCallExpression(node, call, noArrowParamsConversion); + return super.parseAsyncArrowFromCallExpression(node, call); } // todo description @@ -1624,12 +1621,12 @@ export default (superClass: Class): Class => class extends super // parse the rest, make sure the rest is an arrow function, and go from // there // 3. This is neither. Just call the super method - parseMaybeAssign(noIn?: ?boolean, refShorthandDefaultPos?: ?Pos, afterLeftParse?: Function, refNeedsArrowPos?: ?Pos, noArrowParamsConversion?: boolean): N.Expression { + parseMaybeAssign(noIn?: ?boolean, refShorthandDefaultPos?: ?Pos, afterLeftParse?: Function, refNeedsArrowPos?: ?Pos): N.Expression { let jsxError = null; if (tt.jsxTagStart && this.match(tt.jsxTagStart)) { const state = this.state.clone(); try { - return super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos, noArrowParamsConversion); + return super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos); } catch (err) { if (err instanceof SyntaxError) { this.state = state; @@ -1653,7 +1650,7 @@ export default (superClass: Class): Class => class extends super try { typeParameters = this.flowParseTypeParameterDeclaration(); arrowExpression = this.forwardNoArrowParamsConversionAt(typeParameters, () => ( - super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos, noArrowParamsConversion) + super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos) )); arrowExpression.typeParameters = typeParameters; this.resetStartLocationFromNode(arrowExpression, typeParameters); @@ -1673,7 +1670,7 @@ export default (superClass: Class): Class => class extends super } } - return super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos, noArrowParamsConversion); + return super.parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos); } // handle return types for arrow functions @@ -1714,7 +1711,7 @@ export default (superClass: Class): Class => class extends super return this.match(tt.colon) || super.shouldParseArrow(); } - setArrowFunctionParameters(node: N.ArrowFunctionExpression, params: N.Expression[], noArrowParamsConversion ?: boolean): N.ArrowFunctionExpression { + setArrowFunctionParameters(node: N.ArrowFunctionExpression, params: N.Expression[]): N.ArrowFunctionExpression { if (this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) { node.params = params; return node; @@ -1731,14 +1728,13 @@ export default (superClass: Class): Class => class extends super return super.checkFunctionNameAndParams(node, isArrowFunction); } - parseParenAndDistinguishExpression(canBeArrow: boolean, noArrowParamsConversion?: boolean): N.Expression { + parseParenAndDistinguishExpression(canBeArrow: boolean): N.Expression { return super.parseParenAndDistinguishExpression( - canBeArrow && this.state.noArrowAt.indexOf(this.state.start) === -1, - noArrowParamsConversion + canBeArrow && this.state.noArrowAt.indexOf(this.state.start) === -1 ); } - parseSubscripts(base: N.Expression, startPos: number, startLoc: Position, noCalls?: boolean, noArrowParamsConversion?: ?boolean): N.Expression { + parseSubscripts(base: N.Expression, startPos: number, startLoc: Position, noCalls?: boolean): N.Expression { if ( base.type === "Identifier" && base.name === "async" && this.state.noArrowAt.indexOf(startPos) !== -1 @@ -1751,6 +1747,6 @@ export default (superClass: Class): Class => class extends super base = this.finishNode(node, "CallExpression"); } - return super.parseSubscripts(base, startPos, startLoc, noCalls, noArrowParamsConversion); + return super.parseSubscripts(base, startPos, startLoc, noCalls); } }; diff --git a/src/plugins/jsx/index.js b/src/plugins/jsx/index.js index f1e3fb0424..0d8e6b91a5 100644 --- a/src/plugins/jsx/index.js +++ b/src/plugins/jsx/index.js @@ -407,13 +407,13 @@ export default (superClass: Class): Class => class extends super // Overrides // ================================== - parseExprAtom(refShortHandDefaultPos: ?Pos, noArrowParamsConversion?: boolean): N.Expression { + parseExprAtom(refShortHandDefaultPos: ?Pos): N.Expression { if (this.match(tt.jsxText)) { return this.parseLiteral(this.state.value, "JSXText"); } else if (this.match(tt.jsxTagStart)) { return this.jsxParseElement(); } else { - return super.parseExprAtom(refShortHandDefaultPos, noArrowParamsConversion); + return super.parseExprAtom(refShortHandDefaultPos); } }