Fix flow plugin when flow+arrow+spread used together
The fix includes creating a new method on the parser called `parseArrow`. This new function by default only checks if current position matches an arrow. If it does returns the `node` otherwise `undefined`. The flow plugin can then extend this function and correctly parse the typeAnnotation and add it to the node. With this change, in the flow plugin there is no need anymore to extend `parseParenAndDistinguishExpression` and the arrow handling in `parseParenItem` could also be removed, because it is all handled now in `parseArrow`. Some existing tests were failing, because `extra->parentesized` is now missing, but this is correct as it is now inline with parsing without flow annotation. No extra is added for arrow function without type annotations. In the expression-parser `this.next()` was replaced by a more specific `this.expect(tt.parenL)`.
This commit is contained in:
@@ -531,12 +531,12 @@ pp.parseParenExpression = function () {
|
||||
return val;
|
||||
};
|
||||
|
||||
pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow, isAsync, allowOptionalCommaStart) {
|
||||
pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow, isAsync) {
|
||||
startPos = startPos || this.state.start;
|
||||
startLoc = startLoc || this.state.startLoc;
|
||||
|
||||
let val;
|
||||
this.next();
|
||||
this.expect(tt.parenL);
|
||||
|
||||
let innerStartPos = this.state.start, innerStartLoc = this.state.startLoc;
|
||||
let exprList = [], first = true;
|
||||
@@ -566,12 +566,13 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow
|
||||
let innerEndLoc = this.state.startLoc;
|
||||
this.expect(tt.parenR);
|
||||
|
||||
if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) {
|
||||
let arrowNode = this.startNodeAt(startPos, startLoc);
|
||||
if (canBeArrow && !this.canInsertSemicolon() && (arrowNode = this.parseArrow(arrowNode))) {
|
||||
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);
|
||||
return this.parseArrowExpression(arrowNode, exprList, isAsync);
|
||||
}
|
||||
|
||||
if (!exprList.length) {
|
||||
@@ -581,7 +582,7 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow
|
||||
this.unexpected(this.state.lastTokStart);
|
||||
}
|
||||
}
|
||||
if (optionalCommaStart && !allowOptionalCommaStart) this.unexpected(optionalCommaStart);
|
||||
if (optionalCommaStart) this.unexpected(optionalCommaStart);
|
||||
if (spreadStart) this.unexpected(spreadStart);
|
||||
if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start);
|
||||
|
||||
@@ -601,6 +602,12 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow
|
||||
return val;
|
||||
};
|
||||
|
||||
pp.parseArrow = function (node) {
|
||||
if (this.eat(tt.arrow)) {
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
||||
pp.parseParenItem = function (node) {
|
||||
return node;
|
||||
};
|
||||
|
||||
@@ -717,30 +717,19 @@ export default function (instance) {
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("parseParenItem", function () {
|
||||
return function (node, startLoc, startPos, forceArrow?) {
|
||||
let canBeArrow = this.state.potentialArrowAt = startPos;
|
||||
instance.extend("parseParenItem", function (inner) {
|
||||
return function (node, startLoc, startPos) {
|
||||
node = inner.call(this, node, startLoc, startPos);
|
||||
|
||||
if (this.match(tt.colon)) {
|
||||
let typeCastNode = this.startNodeAt(startLoc, startPos);
|
||||
typeCastNode.expression = node;
|
||||
typeCastNode.typeAnnotation = this.flowParseTypeAnnotation();
|
||||
|
||||
if (forceArrow && !this.match(tt.arrow)) {
|
||||
this.unexpected();
|
||||
}
|
||||
|
||||
if (canBeArrow && this.eat(tt.arrow)) {
|
||||
// ((lol): number => {});
|
||||
let params = node.type === "SequenceExpression" ? node.expressions : [node];
|
||||
let func = this.parseArrowExpression(this.startNodeAt(startLoc, startPos), params);
|
||||
func.returnType = typeCastNode.typeAnnotation;
|
||||
return func;
|
||||
} else {
|
||||
return this.finishNode(typeCastNode, "TypeCastExpression");
|
||||
}
|
||||
} else {
|
||||
return node;
|
||||
return this.finishNode(typeCastNode, "TypeCastExpression");
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1038,40 +1027,25 @@ export default function (instance) {
|
||||
});
|
||||
|
||||
// handle return types for arrow functions
|
||||
instance.extend("parseParenAndDistinguishExpression", function (inner) {
|
||||
return function (startPos, startLoc, canBeArrow, isAsync) {
|
||||
startPos = startPos || this.state.start;
|
||||
startLoc = startLoc || this.state.startLoc;
|
||||
|
||||
if (canBeArrow && this.lookahead().type === tt.parenR) {
|
||||
// let foo = (): number => {};
|
||||
this.expect(tt.parenL);
|
||||
this.expect(tt.parenR);
|
||||
|
||||
let node = this.startNodeAt(startPos, startLoc);
|
||||
if (this.match(tt.colon)) node.returnType = this.flowParseTypeAnnotation();
|
||||
this.expect(tt.arrow);
|
||||
return this.parseArrowExpression(node, [], isAsync);
|
||||
} else {
|
||||
// let foo = (foo): number => {};
|
||||
let node = inner.call(this, startPos, startLoc, canBeArrow, isAsync, this.hasPlugin("trailingFunctionCommas"));
|
||||
|
||||
if (this.match(tt.colon)) {
|
||||
let state = this.state.clone();
|
||||
try {
|
||||
return this.parseParenItem(node, startPos, startLoc, true);
|
||||
} catch (err) {
|
||||
if (err instanceof SyntaxError) {
|
||||
this.state = state;
|
||||
return node;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
instance.extend("parseArrow", function (inner) {
|
||||
return function (node) {
|
||||
if (this.match(tt.colon)) {
|
||||
let state = this.state.clone();
|
||||
try {
|
||||
let returnType = this.flowParseTypeAnnotation();
|
||||
if (!this.match(tt.arrow)) this.unexpected();
|
||||
// assign after it is clear it is an arrow
|
||||
node.returnType = returnType;
|
||||
} catch (err) {
|
||||
if (err instanceof SyntaxError) {
|
||||
this.state = state;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return inner.call(this, node);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user