diff --git a/src/parser/expression.js b/src/parser/expression.js index e987a78465..2e9847dd19 100644 --- a/src/parser/expression.js +++ b/src/parser/expression.js @@ -93,16 +93,20 @@ pp.parseMaybeAssign = function (noIn, refShorthandDefaultPos, afterLeftParse) { } let failOnShorthandAssign; - if (!refShorthandDefaultPos) { - refShorthandDefaultPos = {start: 0}; - failOnShorthandAssign = true; - } else { + if (refShorthandDefaultPos) { failOnShorthandAssign = false; + } else { + refShorthandDefaultPos = { start: 0 }; + failOnShorthandAssign = true; } - let startPos = this.state.start, startLoc = this.state.startLoc; + + let startPos = this.state.start; + let startLoc = this.state.startLoc; + if (this.match(tt.parenL) || this.match(tt.name)) { this.state.potentialArrowAt = this.state.start; } + let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos); if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc); if (this.state.type.isAssign) { @@ -110,7 +114,9 @@ pp.parseMaybeAssign = function (noIn, refShorthandDefaultPos, afterLeftParse) { node.operator = this.state.value; node.left = this.match(tt.eq) ? this.toAssignable(left) : left; refShorthandDefaultPos.start = 0; // reset because shorthand default was used correctly + this.checkLVal(left); + if (left.extra && left.extra.parenthesized) { let errorMsg; if (left.type === "ObjectPattern") { @@ -122,12 +128,14 @@ pp.parseMaybeAssign = function (noIn, refShorthandDefaultPos, afterLeftParse) { this.raise(left.start, `You're trying to assign to a parenthesized expression, eg. instead of ${errorMsg}`); } } + this.next(); node.right = this.parseMaybeAssign(noIn); return this.finishNode(node, "AssignmentExpression"); } else if (failOnShorthandAssign && refShorthandDefaultPos.start) { this.unexpected(refShorthandDefaultPos.start); } + return left; }; @@ -506,12 +514,13 @@ pp.parseParenExpression = function () { pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow, isAsync) { startPos = startPos || this.state.start; startLoc = startLoc || this.state.startLoc; + let val; this.next(); let innerStartPos = this.state.start, innerStartLoc = this.state.startLoc; let exprList = [], first = true; - let refShorthandDefaultPos = { start: 0 }, spreadStart, innerParenStart, optionalCommaStart; + let refShorthandDefaultPos = { start: 0 }, spreadStart, optionalCommaStart; while (!this.match(tt.parenR)) { if (first) { first = false; @@ -529,9 +538,6 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow exprList.push(this.parseParenItem(this.parseRest(), spreadNodeStartLoc, spreadNodeStartPos)); break; } else { - if (this.match(tt.parenL) && !innerParenStart) { - innerParenStart = this.state.start; - } exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem)); } } @@ -541,7 +547,10 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow this.expect(tt.parenR); if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) { - if (innerParenStart) this.unexpected(innerParenStart); + for (let param of exprList) { + if (param.extra && param.extra.parenthesized) this.unexpected(param.extra.parenStart); + } + return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, isAsync); } @@ -564,7 +573,11 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow } else { val = exprList[0]; } + + this.addExtra(val, "parenthesized", true); + this.addExtra(val, "parenStart", startPos); + return val; }; diff --git a/src/plugins/flow.js b/src/plugins/flow.js index b4630f67b4..ea0c82e080 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -762,6 +762,16 @@ export default function (instance) { return node.expression; } + instance.extend("toAssignable", function (inner) { + return function (node) { + if (node.type === "TypeCastExpression") { + return typeCastToParameter(node); + } else { + return inner.apply(this, arguments); + } + }; + }); + // turn type casts that we found in function parameter head into type annotated params instance.extend("toAssignableList", function (inner) { return function (exprList, isBinding) { @@ -807,6 +817,14 @@ export default function (instance) { }; }); + instance.extend("checkLVal", function (inner) { + return function (node) { + if (node.type !== "TypeCastExpression") { + return inner.apply(this, arguments); + } + }; + }); + // parse class property type annotations instance.extend("parseClassProperty", function (inner) { return function (node) { diff --git a/test/fixtures/flow/regression/issue-2493/actual.js b/test/fixtures/flow/regression/issue-2493/actual.js new file mode 100644 index 0000000000..7563bf2270 --- /dev/null +++ b/test/fixtures/flow/regression/issue-2493/actual.js @@ -0,0 +1,5 @@ +let hello = (greeting:string = ' world') : string => { + console.log('hello' + greeting); +}; + +hello(); diff --git a/test/fixtures/flow/regression/issue-2493/expected.json b/test/fixtures/flow/regression/issue-2493/expected.json new file mode 100644 index 0000000000..cba658c02f --- /dev/null +++ b/test/fixtures/flow/regression/issue-2493/expected.json @@ -0,0 +1,416 @@ +{ + "type": "File", + "start": 0, + "end": 102, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 8 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 102, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 8 + } + }, + "sourceType": "module", + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 92, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 2 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 91, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 3, + "column": 1 + } + }, + "id": { + "type": "Identifier", + "start": 4, + "end": 9, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 9 + } + }, + "name": "hello" + }, + "init": { + "type": "ArrowFunctionExpression", + "start": 12, + "end": 91, + "loc": { + "start": { + "line": 1, + "column": 12 + }, + "end": { + "line": 3, + "column": 1 + } + }, + "id": null, + "generator": false, + "expression": false, + "async": false, + "params": [ + { + "type": "AssignmentPattern", + "start": 13, + "end": 39, + "loc": { + "start": { + "line": 1, + "column": 13 + }, + "end": { + "line": 1, + "column": 39 + } + }, + "left": { + "type": "Identifier", + "start": 13, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 13 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "name": "greeting", + "typeAnnotation": { + "type": "TypeAnnotation", + "start": 21, + "end": 28, + "loc": { + "start": { + "line": 1, + "column": 21 + }, + "end": { + "line": 1, + "column": 28 + } + }, + "typeAnnotation": { + "type": "StringTypeAnnotation", + "start": 22, + "end": 28, + "loc": { + "start": { + "line": 1, + "column": 22 + }, + "end": { + "line": 1, + "column": 28 + } + } + } + } + }, + "right": { + "type": "StringLiteral", + "start": 31, + "end": 39, + "loc": { + "start": { + "line": 1, + "column": 31 + }, + "end": { + "line": 1, + "column": 39 + } + }, + "extra": { + "rawValue": " world", + "raw": "' world'" + }, + "value": " world" + }, + "extra": { + "parenthesized": true, + "parenStart": 12 + } + } + ], + "body": { + "type": "BlockStatement", + "start": 53, + "end": 91, + "loc": { + "start": { + "line": 1, + "column": 53 + }, + "end": { + "line": 3, + "column": 1 + } + }, + "body": [ + { + "type": "ExpressionStatement", + "start": 57, + "end": 89, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 34 + } + }, + "expression": { + "type": "CallExpression", + "start": 57, + "end": 88, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 33 + } + }, + "callee": { + "type": "MemberExpression", + "start": 57, + "end": 68, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 13 + } + }, + "object": { + "type": "Identifier", + "start": 57, + "end": 64, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 9 + } + }, + "name": "console" + }, + "property": { + "type": "Identifier", + "start": 65, + "end": 68, + "loc": { + "start": { + "line": 2, + "column": 10 + }, + "end": { + "line": 2, + "column": 13 + } + }, + "name": "log" + }, + "computed": false + }, + "arguments": [ + { + "type": "BinaryExpression", + "start": 69, + "end": 87, + "loc": { + "start": { + "line": 2, + "column": 14 + }, + "end": { + "line": 2, + "column": 32 + } + }, + "left": { + "type": "StringLiteral", + "start": 69, + "end": 76, + "loc": { + "start": { + "line": 2, + "column": 14 + }, + "end": { + "line": 2, + "column": 21 + } + }, + "extra": { + "rawValue": "hello", + "raw": "'hello'" + }, + "value": "hello" + }, + "operator": "+", + "right": { + "type": "Identifier", + "start": 79, + "end": 87, + "loc": { + "start": { + "line": 2, + "column": 24 + }, + "end": { + "line": 2, + "column": 32 + } + }, + "name": "greeting" + } + } + ] + } + } + ], + "directives": [] + }, + "returnType": { + "type": "TypeAnnotation", + "start": 41, + "end": 49, + "loc": { + "start": { + "line": 1, + "column": 41 + }, + "end": { + "line": 1, + "column": 49 + } + }, + "typeAnnotation": { + "type": "StringTypeAnnotation", + "start": 43, + "end": 49, + "loc": { + "start": { + "line": 1, + "column": 43 + }, + "end": { + "line": 1, + "column": 49 + } + } + } + } + } + } + ], + "kind": "let" + }, + { + "type": "ExpressionStatement", + "start": 94, + "end": 102, + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 5, + "column": 8 + } + }, + "expression": { + "type": "CallExpression", + "start": 94, + "end": 101, + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 5, + "column": 7 + } + }, + "callee": { + "type": "Identifier", + "start": 94, + "end": 99, + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 5, + "column": 5 + } + }, + "name": "hello" + }, + "arguments": [] + } + } + ], + "directives": [] + } +} \ No newline at end of file