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:
Daniel Tschinder 2016-03-12 14:04:53 +01:00
parent 8b150813f5
commit d15a2310cd
13 changed files with 793 additions and 74 deletions

View File

@ -531,12 +531,12 @@ pp.parseParenExpression = function () {
return val; return val;
}; };
pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow, isAsync, allowOptionalCommaStart) { pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow, isAsync) {
startPos = startPos || this.state.start; startPos = startPos || this.state.start;
startLoc = startLoc || this.state.startLoc; startLoc = startLoc || this.state.startLoc;
let val; let val;
this.next(); this.expect(tt.parenL);
let innerStartPos = this.state.start, innerStartLoc = this.state.startLoc; let innerStartPos = this.state.start, innerStartLoc = this.state.startLoc;
let exprList = [], first = true; let exprList = [], first = true;
@ -566,12 +566,13 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow
let innerEndLoc = this.state.startLoc; let innerEndLoc = this.state.startLoc;
this.expect(tt.parenR); 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) { for (let param of exprList) {
if (param.extra && param.extra.parenthesized) this.unexpected(param.extra.parenStart); 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) { if (!exprList.length) {
@ -581,7 +582,7 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow
this.unexpected(this.state.lastTokStart); this.unexpected(this.state.lastTokStart);
} }
} }
if (optionalCommaStart && !allowOptionalCommaStart) this.unexpected(optionalCommaStart); if (optionalCommaStart) this.unexpected(optionalCommaStart);
if (spreadStart) this.unexpected(spreadStart); if (spreadStart) this.unexpected(spreadStart);
if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start); if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start);
@ -601,6 +602,12 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow
return val; return val;
}; };
pp.parseArrow = function (node) {
if (this.eat(tt.arrow)) {
return node;
}
};
pp.parseParenItem = function (node) { pp.parseParenItem = function (node) {
return node; return node;
}; };

View File

@ -717,30 +717,19 @@ export default function (instance) {
}; };
}); });
instance.extend("parseParenItem", function () { instance.extend("parseParenItem", function (inner) {
return function (node, startLoc, startPos, forceArrow?) { return function (node, startLoc, startPos) {
let canBeArrow = this.state.potentialArrowAt = startPos; node = inner.call(this, node, startLoc, startPos);
if (this.match(tt.colon)) { if (this.match(tt.colon)) {
let typeCastNode = this.startNodeAt(startLoc, startPos); let typeCastNode = this.startNodeAt(startLoc, startPos);
typeCastNode.expression = node; typeCastNode.expression = node;
typeCastNode.typeAnnotation = this.flowParseTypeAnnotation(); typeCastNode.typeAnnotation = this.flowParseTypeAnnotation();
if (forceArrow && !this.match(tt.arrow)) { return this.finishNode(typeCastNode, "TypeCastExpression");
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 node;
}; };
}); });
@ -1038,40 +1027,25 @@ export default function (instance) {
}); });
// handle return types for arrow functions // handle return types for arrow functions
instance.extend("parseParenAndDistinguishExpression", function (inner) { instance.extend("parseArrow", function (inner) {
return function (startPos, startLoc, canBeArrow, isAsync) { return function (node) {
startPos = startPos || this.state.start; if (this.match(tt.colon)) {
startLoc = startLoc || this.state.startLoc; let state = this.state.clone();
try {
if (canBeArrow && this.lookahead().type === tt.parenR) { let returnType = this.flowParseTypeAnnotation();
// let foo = (): number => {}; if (!this.match(tt.arrow)) this.unexpected();
this.expect(tt.parenL); // assign after it is clear it is an arrow
this.expect(tt.parenR); node.returnType = returnType;
} catch (err) {
let node = this.startNodeAt(startPos, startLoc); if (err instanceof SyntaxError) {
if (this.match(tt.colon)) node.returnType = this.flowParseTypeAnnotation(); this.state = state;
this.expect(tt.arrow); } else {
return this.parseArrowExpression(node, [], isAsync); throw err;
} 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;
}
} }
} else {
return node;
} }
} }
return inner.call(this, node);
}; };
}); });
} }

View File

@ -171,10 +171,6 @@
"raw": "' world'" "raw": "' world'"
}, },
"value": " world" "value": " world"
},
"extra": {
"parenthesized": true,
"parenStart": 12
} }
} }
], ],

View File

@ -0,0 +1 @@
( ...props: SomeType ) : ?ReturnType => ( 3 );

View File

@ -0,0 +1,232 @@
{
"type": "File",
"start": 0,
"end": 46,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 46
}
},
"program": {
"type": "Program",
"start": 0,
"end": 46,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 46
}
},
"sourceType": "module",
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 46,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 46
}
},
"expression": {
"type": "ArrowFunctionExpression",
"start": 0,
"end": 45,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 45
}
},
"returnType": {
"type": "TypeAnnotation",
"start": 23,
"end": 36,
"loc": {
"start": {
"line": 1,
"column": 23
},
"end": {
"line": 1,
"column": 36
}
},
"typeAnnotation": {
"type": "NullableTypeAnnotation",
"start": 25,
"end": 36,
"loc": {
"start": {
"line": 1,
"column": 25
},
"end": {
"line": 1,
"column": 36
}
},
"typeAnnotation": {
"type": "GenericTypeAnnotation",
"start": 26,
"end": 36,
"loc": {
"start": {
"line": 1,
"column": 26
},
"end": {
"line": 1,
"column": 36
}
},
"typeParameters": null,
"id": {
"type": "Identifier",
"start": 26,
"end": 36,
"loc": {
"start": {
"line": 1,
"column": 26
},
"end": {
"line": 1,
"column": 36
}
},
"name": "ReturnType"
}
}
}
},
"id": null,
"generator": false,
"expression": true,
"async": false,
"params": [
{
"type": "RestElement",
"start": 2,
"end": 10,
"loc": {
"start": {
"line": 1,
"column": 2
},
"end": {
"line": 1,
"column": 10
}
},
"argument": {
"type": "Identifier",
"start": 5,
"end": 10,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 10
}
},
"name": "props"
},
"typeAnnotation": {
"type": "TypeAnnotation",
"start": 10,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 10
},
"end": {
"line": 1,
"column": 20
}
},
"typeAnnotation": {
"type": "GenericTypeAnnotation",
"start": 12,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 12
},
"end": {
"line": 1,
"column": 20
}
},
"typeParameters": null,
"id": {
"type": "Identifier",
"start": 12,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 12
},
"end": {
"line": 1,
"column": 20
}
},
"name": "SomeType"
}
}
}
}
],
"body": {
"type": "NumericLiteral",
"start": 42,
"end": 43,
"loc": {
"start": {
"line": 1,
"column": 42
},
"end": {
"line": 1,
"column": 43
}
},
"extra": {
"rawValue": 3,
"raw": "3",
"parenthesized": true,
"parenStart": 40
},
"value": 3
}
}
}
],
"directives": []
}
}

View File

@ -0,0 +1 @@
export default (...modifiers): Array<string> => {};

View File

@ -0,0 +1,196 @@
{
"type": "File",
"start": 0,
"end": 51,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 51
}
},
"program": {
"type": "Program",
"start": 0,
"end": 51,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 51
}
},
"sourceType": "module",
"body": [
{
"type": "ExportDefaultDeclaration",
"start": 0,
"end": 51,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 51
}
},
"declaration": {
"type": "ArrowFunctionExpression",
"start": 15,
"end": 50,
"loc": {
"start": {
"line": 1,
"column": 15
},
"end": {
"line": 1,
"column": 50
}
},
"returnType": {
"type": "TypeAnnotation",
"start": 29,
"end": 44,
"loc": {
"start": {
"line": 1,
"column": 29
},
"end": {
"line": 1,
"column": 44
}
},
"typeAnnotation": {
"type": "GenericTypeAnnotation",
"start": 31,
"end": 44,
"loc": {
"start": {
"line": 1,
"column": 31
},
"end": {
"line": 1,
"column": 44
}
},
"typeParameters": {
"type": "TypeParameterInstantiation",
"start": 36,
"end": 44,
"loc": {
"start": {
"line": 1,
"column": 36
},
"end": {
"line": 1,
"column": 44
}
},
"params": [
{
"type": "StringTypeAnnotation",
"start": 37,
"end": 43,
"loc": {
"start": {
"line": 1,
"column": 37
},
"end": {
"line": 1,
"column": 43
}
}
}
]
},
"id": {
"type": "Identifier",
"start": 31,
"end": 36,
"loc": {
"start": {
"line": 1,
"column": 31
},
"end": {
"line": 1,
"column": 36
}
},
"name": "Array"
}
}
},
"id": null,
"generator": false,
"expression": false,
"async": false,
"params": [
{
"type": "RestElement",
"start": 16,
"end": 28,
"loc": {
"start": {
"line": 1,
"column": 16
},
"end": {
"line": 1,
"column": 28
}
},
"argument": {
"type": "Identifier",
"start": 19,
"end": 28,
"loc": {
"start": {
"line": 1,
"column": 19
},
"end": {
"line": 1,
"column": 28
}
},
"name": "modifiers"
}
}
],
"body": {
"type": "BlockStatement",
"start": 48,
"end": 50,
"loc": {
"start": {
"line": 1,
"column": 48
},
"end": {
"line": 1,
"column": 50
}
},
"body": [],
"directives": []
}
}
}
],
"directives": []
}
}

View File

@ -0,0 +1 @@
const parser = (rootPath: string, ...filesToParse: Array<string>):a => {}

View File

@ -0,0 +1,323 @@
{
"type": "File",
"start": 0,
"end": 73,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 73
}
},
"program": {
"type": "Program",
"start": 0,
"end": 73,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 73
}
},
"sourceType": "module",
"body": [
{
"type": "VariableDeclaration",
"start": 0,
"end": 73,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 73
}
},
"declarations": [
{
"type": "VariableDeclarator",
"start": 6,
"end": 73,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 73
}
},
"id": {
"type": "Identifier",
"start": 6,
"end": 12,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 12
}
},
"name": "parser"
},
"init": {
"type": "ArrowFunctionExpression",
"start": 15,
"end": 73,
"loc": {
"start": {
"line": 1,
"column": 15
},
"end": {
"line": 1,
"column": 73
}
},
"returnType": {
"type": "TypeAnnotation",
"start": 65,
"end": 67,
"loc": {
"start": {
"line": 1,
"column": 65
},
"end": {
"line": 1,
"column": 67
}
},
"typeAnnotation": {
"type": "GenericTypeAnnotation",
"start": 66,
"end": 67,
"loc": {
"start": {
"line": 1,
"column": 66
},
"end": {
"line": 1,
"column": 67
}
},
"typeParameters": null,
"id": {
"type": "Identifier",
"start": 66,
"end": 67,
"loc": {
"start": {
"line": 1,
"column": 66
},
"end": {
"line": 1,
"column": 67
}
},
"name": "a"
}
}
},
"id": null,
"generator": false,
"expression": false,
"async": false,
"params": [
{
"type": "Identifier",
"start": 16,
"end": 24,
"loc": {
"start": {
"line": 1,
"column": 16
},
"end": {
"line": 1,
"column": 24
}
},
"name": "rootPath",
"typeAnnotation": {
"type": "TypeAnnotation",
"start": 24,
"end": 32,
"loc": {
"start": {
"line": 1,
"column": 24
},
"end": {
"line": 1,
"column": 32
}
},
"typeAnnotation": {
"type": "StringTypeAnnotation",
"start": 26,
"end": 32,
"loc": {
"start": {
"line": 1,
"column": 26
},
"end": {
"line": 1,
"column": 32
}
}
}
}
},
{
"type": "RestElement",
"start": 34,
"end": 49,
"loc": {
"start": {
"line": 1,
"column": 34
},
"end": {
"line": 1,
"column": 49
}
},
"argument": {
"type": "Identifier",
"start": 37,
"end": 49,
"loc": {
"start": {
"line": 1,
"column": 37
},
"end": {
"line": 1,
"column": 49
}
},
"name": "filesToParse"
},
"typeAnnotation": {
"type": "TypeAnnotation",
"start": 49,
"end": 64,
"loc": {
"start": {
"line": 1,
"column": 49
},
"end": {
"line": 1,
"column": 64
}
},
"typeAnnotation": {
"type": "GenericTypeAnnotation",
"start": 51,
"end": 64,
"loc": {
"start": {
"line": 1,
"column": 51
},
"end": {
"line": 1,
"column": 64
}
},
"typeParameters": {
"type": "TypeParameterInstantiation",
"start": 56,
"end": 64,
"loc": {
"start": {
"line": 1,
"column": 56
},
"end": {
"line": 1,
"column": 64
}
},
"params": [
{
"type": "StringTypeAnnotation",
"start": 57,
"end": 63,
"loc": {
"start": {
"line": 1,
"column": 57
},
"end": {
"line": 1,
"column": 63
}
}
}
]
},
"id": {
"type": "Identifier",
"start": 51,
"end": 56,
"loc": {
"start": {
"line": 1,
"column": 51
},
"end": {
"line": 1,
"column": 56
}
},
"name": "Array"
}
}
}
}
],
"body": {
"type": "BlockStatement",
"start": 71,
"end": 73,
"loc": {
"start": {
"line": 1,
"column": 71
},
"end": {
"line": 1,
"column": 73
}
},
"body": [],
"directives": []
}
}
}
],
"kind": "const"
}
],
"directives": []
}
}

View File

@ -105,10 +105,7 @@
"column": 14 "column": 14
} }
}, },
"name": "bar", "name": "bar"
"extra": {
"parenthesized": true
}
} }
], ],
"body": { "body": {

View File

@ -105,10 +105,7 @@
"column": 15 "column": 15
} }
}, },
"name": "bar", "name": "bar"
"extra": {
"parenthesized": true
}
} }
], ],
"body": { "body": {

View File

@ -119,10 +119,7 @@
"column": 16 "column": 16
} }
}, },
"name": "bar", "name": "bar"
"extra": {
"parenthesized": true
}
} }
], ],
"body": { "body": {

View File

@ -135,10 +135,7 @@
"column": 20 "column": 20
} }
}, },
"name": "foo", "name": "foo"
"extra": {
"parenthesized": true
}
} }
], ],
"body": { "body": {