Skip to content

Commit

Permalink
Flow typecasts require parentheses
Browse files Browse the repository at this point in the history
  • Loading branch information
wcjohnson committed Oct 9, 2017
1 parent 5e5a50e commit 322842a
Show file tree
Hide file tree
Showing 12 changed files with 356 additions and 24 deletions.
5 changes: 4 additions & 1 deletion src/parser/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,10 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow
if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start);
if (refNeedsArrowPos.start) this.unexpected(refNeedsArrowPos.start);

if (this.hasPlugin("flow")) {
this.flowExprListToCast(exprList);
}

if (exprList.length > 1) {
val = this.startNodeAt(innerStartPos, innerStartLoc);
val.expressions = exprList;
Expand All @@ -953,7 +957,6 @@ pp.parseParenAndDistinguishExpression = function (startPos, startLoc, canBeArrow
val = exprList[0];
}


this.addExtra(val, "parenthesized", true);
this.addExtra(val, "parenStart", startPos);

Expand Down
44 changes: 21 additions & 23 deletions src/plugins/flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,21 @@ pp.flowParseVariance = function() {
return variance;
};

pp.flowExprListToCast = function(exprList) {
// A flow typecast is a parenthesized expr list of exactly length 1 with
// a TypeCastish in the first slot; anything else involving TypeCastish
// is illegal.
if (exprList.length === 1 && exprList[0].type === "TypeCastish") {
exprList[0].type = "TypeCastExpression";
} else {
exprList.forEach((expr) => {
if (expr.type === "TypeCastish") {
this.unexpected(expr.typeAnnotation.pos);
}
});
}
};

export default function (instance) {
// plain function return types: function name(): string {}
instance.extend("parseFunctionBody", function (inner) {
Expand Down Expand Up @@ -962,7 +977,7 @@ export default function (instance) {
typeCastNode.expression = node;
typeCastNode.typeAnnotation = this.flowParseTypeAnnotation();

return this.finishNode(typeCastNode, "TypeCastExpression");
return this.finishNode(typeCastNode, "TypeCastish");
}

return node;
Expand Down Expand Up @@ -1058,7 +1073,7 @@ export default function (instance) {

instance.extend("toAssignable", function (inner) {
return function (node, isBinding, contextDescription) {
if (node.type === "TypeCastExpression") {
if (node.type === "TypeCastish") {
return inner.call(this, this.typeCastToParameter(node), isBinding, contextDescription);
} else if (this.hasPlugin("existentialExpression") && node.type === "ExistentialExpression") {
// Existential expression looks like an optional flow parameter
Expand All @@ -1074,7 +1089,7 @@ export default function (instance) {
return function (exprList, isBinding, contextDescription) {
for (let i = 0; i < exprList.length; i++) {
const expr = exprList[i];
if (expr && expr.type === "TypeCastExpression") {
if (expr && expr.type === "TypeCastish") {
exprList[i] = this.typeCastToParameter(expr);
} else if (this.hasPlugin("existentialExpression") && expr && expr.type === "ExistentialExpression") {
// Existential expression looks like an optional flow parameter
Expand All @@ -1091,35 +1106,18 @@ export default function (instance) {
return function (exprList) {
for (let i = 0; i < exprList.length; i++) {
const expr = exprList[i];
if (expr && expr._exprListItem && expr.type === "TypeCastExpression") {
this.raise(expr.start, "Unexpected type cast");
if (expr && expr.type === "TypeCastish") {
this.raise(expr.typeAnnotation.start, "Unexpected type cast");
}
}

return exprList;
};
});

// parse an item inside a expression list eg. `(NODE, NODE)` where NODE represents
// the position where this function is called
instance.extend("parseExprListItem", function (inner) {
return function (...args) {
const container = this.startNode();
const node = inner.call(this, ...args);
if (this.match(tt.colon)) {
container._exprListItem = true;
container.expression = node;
container.typeAnnotation = this.flowParseTypeAnnotation();
return this.finishNode(container, "TypeCastExpression");
} else {
return node;
}
};
});

instance.extend("checkLVal", function (inner) {
return function (node) {
if (node.type !== "TypeCastExpression") {
if (node.type !== "TypeCastish") {
return inner.apply(this, arguments);
}
};
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/flow/typecasts/array/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[(castee: CastTo)]
150 changes: 150 additions & 0 deletions test/fixtures/flow/typecasts/array/expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
{
"type": "File",
"start": 0,
"end": 18,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 18
}
},
"program": {
"type": "Program",
"start": 0,
"end": 18,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 18
}
},
"sourceType": "module",
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 18,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 18
}
},
"expression": {
"type": "ArrayExpression",
"start": 0,
"end": 18,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 18
}
},
"elements": [
{
"type": "TypeCastExpression",
"start": 2,
"end": 16,
"loc": {
"start": {
"line": 1,
"column": 2
},
"end": {
"line": 1,
"column": 16
}
},
"expression": {
"type": "Identifier",
"start": 2,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 2
},
"end": {
"line": 1,
"column": 8
},
"identifierName": "castee"
},
"name": "castee"
},
"typeAnnotation": {
"type": "TypeAnnotation",
"start": 8,
"end": 16,
"loc": {
"start": {
"line": 1,
"column": 8
},
"end": {
"line": 1,
"column": 16
}
},
"typeAnnotation": {
"type": "GenericTypeAnnotation",
"start": 10,
"end": 16,
"loc": {
"start": {
"line": 1,
"column": 10
},
"end": {
"line": 1,
"column": 16
}
},
"typeParameters": null,
"id": {
"type": "Identifier",
"start": 10,
"end": 16,
"loc": {
"start": {
"line": 1,
"column": 10
},
"end": {
"line": 1,
"column": 16
},
"identifierName": "CastTo"
},
"name": "CastTo"
}
}
},
"extra": {
"parenthesized": true,
"parenStart": 1
}
}
]
}
}
],
"directives": []
}
}
1 change: 1 addition & 0 deletions test/fixtures/flow/typecasts/call/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
callee((castee: CastTo))
Loading

0 comments on commit 322842a

Please sign in to comment.