From 7f34827a725b64caf9ebee30bdc10e41cb1d207c Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Tue, 21 Jul 2015 01:13:35 +0100 Subject: [PATCH] reimplement async function type parameters backfix - fixes #2028 --- src/expression.js | 46 ++++++++++++++----------- src/lval.js | 6 ++++ src/plugins/flow.js | 66 +++++++++++++++++++++++++----------- test/tests-flow.js | 81 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+), 39 deletions(-) diff --git a/src/expression.js b/src/expression.js index be9a3cd9e1..d1f926d836 100755 --- a/src/expression.js +++ b/src/expression.js @@ -68,6 +68,7 @@ pp.parseExpression = function (noIn, refShorthandDefaultPos) { while (this.eat(tt.comma)) { node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos)); } + this.toReferencedList(node.expressions); return this.finishNode(node, "SequenceExpression"); } return expr; @@ -89,8 +90,9 @@ pp.parseMaybeAssign = function (noIn, refShorthandDefaultPos, afterLeftParse) { failOnShorthandAssign = false; } let startPos = this.start, startLoc = this.startLoc; - if (this.type === tt.parenL || this.type === tt.name) + if (this.type === tt.parenL || this.type === tt.name) { this.potentialArrowAt = this.start; + } let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos); if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc); if (this.type.isAssign) { @@ -245,6 +247,8 @@ pp.parseSubscripts = function(base, startPos, startLoc, noCalls) { if (possibleAsync && (this.type === tt.colon || this.type === tt.arrow)) { base = this.parseAsyncArrowFromCallExpression(this.startNodeAt(startPos, startLoc), node); + } else { + this.toReferencedList(node.arguments); } } else if (this.type === tt.backQuote) { let node = this.startNodeAt(startPos, startLoc); @@ -323,8 +327,9 @@ pp.parseExprAtom = function (refShorthandDefaultPos) { } } - if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) + if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) { return this.parseArrowExpression(node, [id]); + } return id; @@ -339,7 +344,7 @@ pp.parseExprAtom = function (refShorthandDefaultPos) { case tt._null: case tt._true: case tt._false: node = this.startNode(); - node.value = this.type === tt._null ? null : this.type === tt._true; + node.rawValue = node.value = this.type === tt._null ? null : this.type === tt._true; node.raw = this.type.keyword; this.next(); return this.finishNode(node, "Literal"); @@ -355,6 +360,7 @@ pp.parseExprAtom = function (refShorthandDefaultPos) { return this.parseComprehension(node, false); } node.elements = this.parseExprList(tt.bracketR, true, true, refShorthandDefaultPos); + this.toReferencedList(node.elements); return this.finishNode(node, "ArrayExpression"); case tt.braceL: @@ -446,12 +452,13 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem)); } } - let innerEndPos = this.start, innerEndLoc = this.startLoc; + let innerEndPos = this.start; + let innerEndLoc = this.startLoc; this.expect(tt.parenR); if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) { if (innerParenStart) this.unexpected(innerParenStart); - return this.parseParenArrowList(startPos, startLoc, exprList, isAsync); + return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, isAsync); } if (!exprList.length) { @@ -468,6 +475,7 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow if (exprList.length > 1) { val = this.startNodeAt(innerStartPos, innerStartLoc); val.expressions = exprList; + this.toReferencedList(val.expressions); this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc); } else { val = exprList[0]; @@ -477,10 +485,6 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow return val; }; -pp.parseParenArrowList = function (startPos, startLoc, exprList, isAsync) { - return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, isAsync); -}; - pp.parseParenItem = function (node) { return node; }; @@ -508,6 +512,7 @@ pp.parseNew = function () { if (this.eat(tt.parenL)) { node.arguments = this.parseExprList(tt.parenR, this.options.features["es7.trailingFunctionCommas"]); + this.toReferencedList(node.arguments); } else { node.arguments = []; } @@ -742,26 +747,29 @@ pp.parseExprList = function (close, allowTrailingComma, allowEmpty, refShorthand if (allowTrailingComma && this.afterTrailingComma(close)) break; } - let elt; - if (allowEmpty && this.type === tt.comma) { - elt = null; - } else if (this.type === tt.ellipsis) { - elt = this.parseSpread(refShorthandDefaultPos); - } else { - elt = this.parseMaybeAssign(false, refShorthandDefaultPos); - } - elts.push(elt); + elts.push(this.parseExprListItem(allowEmpty, refShorthandDefaultPos)); } return elts; }; +pp.parseExprListItem = function (allowEmpty, refShorthandDefaultPos) { + let elt; + if (allowEmpty && this.type === tt.comma) { + elt = null; + } else if (this.type === tt.ellipsis) { + elt = this.parseSpread(refShorthandDefaultPos); + } else { + elt = this.parseMaybeAssign(false, refShorthandDefaultPos); + } + return elt; +}; + // Parse the next token as an identifier. If `liberal` is true (used // when parsing properties), it will also convert keywords into // identifiers. pp.parseIdent = function (liberal) { let node = this.startNode(); - if (liberal && this.options.allowReserved === "never") liberal = false; if (this.type === tt.name) { if (!liberal && ((!this.options.allowReserved && this.isReservedWord(this.value)) || diff --git a/src/lval.js b/src/lval.js index 58027e08a8..abd6793a79 100755 --- a/src/lval.js +++ b/src/lval.js @@ -75,6 +75,12 @@ pp.toAssignableList = function (exprList, isBinding) { return exprList; }; +// Convert list of expression atoms to a list of + +pp.toReferencedList = function (exprList) { + return exprList; +}; + // Parses spread element. pp.parseSpread = function (refShorthandDefaultPos) { diff --git a/src/plugins/flow.js b/src/plugins/flow.js index c85d4b3330..dd5635700e 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -3,18 +3,6 @@ import { Parser } from "../state"; var pp = Parser.prototype; -pp.isRelational = function (op) { - return this.type === tt.relational && this.value === op; -}; - -pp.expectRelational = function (op) { - if (this.isRelational(op)) { - this.next(); - } else { - this.unexpected(); - } -}; - pp.flowParseTypeInitialiser = function (tok) { var oldInType = this.inType; this.inType = true; @@ -707,17 +695,55 @@ export default function (instance) { }; }); - instance.extend("parseParenArrowList", function (inner) { - return function (startPos, startLoc, exprList, isAsync) { + function typeCastToParameter(node) { + node.expression.typeAnnotation = node.typeAnnotation; + return node.expression; + } + + instance.extend("toAssignableList", function (inner) { + return function (exprList, isBinding) { for (var i = 0; i < exprList.length; i++) { - var listItem = exprList[i]; - if (listItem.type === "TypeCastExpression") { - var expr = listItem.expression; - expr.typeAnnotation = listItem.typeAnnotation; - exprList[i] = expr; + var expr = exprList[i]; + if (expr && expr.type === "TypeCastExpression") { + exprList[i] = typeCastToParameter(expr); } } - return inner.call(this, startPos, startLoc, exprList, isAsync); + return inner.call(this, exprList, isBinding); + }; + }); + + instance.extend("toReferencedList", function () { + return function (exprList) { + var foundTypeCast = false; + + for (var i = 0; i < exprList.length; i++) { + var expr = exprList[i]; + if (expr && expr._exprListItem && expr.type === "TypeCastExpression") { + if (foundTypeCast) { + this.unexpected(expr.start, "Unexpected type cast"); + } else { + foundTypeCast = true; + } + } + } + + return exprList; + }; + }); + + instance.extend("parseExprListItem", function (inner) { + return function (allowEmpty, refShorthandDefaultPos) { + var node = inner.call(this, allowEmpty, refShorthandDefaultPos); + if (this.type === tt.colon) { + return { + type: "TypeCastExpression", + _exprListItem: true, + expression: node, + typeAnnotation: this.flowParseTypeAnnotation() + }; + } else { + return node; + } }; }); diff --git a/test/tests-flow.js b/test/tests-flow.js index f65101449a..92fa91a17b 100644 --- a/test/tests-flow.js +++ b/test/tests-flow.js @@ -5971,6 +5971,86 @@ var fbTestFixture = { } }] }, + "var foo = async (foo: bar, bar: foo) => {}": { + type: "VariableDeclaration", + kind: "var", + start: 0, + end: 42, + declarations: [{ + type: "VariableDeclarator", + start: 4, + end: 42, + id: { + type: "Identifier", + start: 4, + end: 7, + name: "foo" + }, + init: { + type: "ArrowFunctionExpression", + start: 10, + end: 42, + id: null, + generator: false, + expression: false, + async: true, + params: [ + { + type: "Identifier", + start: 17, + end: 20, + name: "foo", + typeAnnotation: { + type: "TypeAnnotation", + start: 20, + end: 25, + typeAnnotation: { + type: "GenericTypeAnnotation", + start: 22, + end: 25, + typeParameters: null, + id: { + type: "Identifier", + start: 22, + end: 25, + name: "bar" + } + } + } + }, + { + type: "Identifier", + start: 27, + end: 30, + name: "bar", + typeAnnotation: { + type: "TypeAnnotation", + start: 30, + end: 35, + typeAnnotation: { + type: "GenericTypeAnnotation", + start: 32, + end: 35, + typeParameters: null, + id: { + type: "Identifier", + start: 32, + end: 35, + name: "foo" + } + } + } + } + ], + body: { + type: "BlockStatement", + start: 40, + end: 42, + body: [] + } + } + }] + }, "var foo = async (): number => bar;": { type: "VariableDeclaration", kind: "var", @@ -11978,6 +12058,7 @@ var fbTestFixture = { if (typeof exports !== "undefined") { var test = require("./driver.js").test; + var testFail = require("./driver.js").testFail; } for (var ns in fbTestFixture) {