-
-
Notifications
You must be signed in to change notification settings - Fork 258
[Flow] Function predicate declaration #103
Changes from 1 commit
a411e1e
502b996
d5782a9
7c81009
f972e80
e42d60c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,45 @@ pp.flowParseTypeInitialiser = function (tok, allowLeadingPipeOrAnd) { | |
return type; | ||
}; | ||
|
||
pp.flowParsePredicate = function() { | ||
let node = this.startNode(); | ||
let moduloLoc = this.state.startLoc; | ||
let moduloPos = this.state.start; | ||
this.expect(tt.modulo); | ||
let checksLoc = this.state.startLoc; | ||
this.expectContextual("checks"); | ||
// Force '%' and 'checks' to be adjacent | ||
if (moduloLoc.line !== checksLoc.line || moduloLoc.column !== checksLoc.column - 1) { | ||
this.raise(moduloPos, "Unexpected token"); | ||
} | ||
if (this.match(tt.parenL)) { | ||
this.next(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of if (this.match(tt.parenL)) {
this.next(); you can do if (this.eat(tt.parenL)) { Which is doing the exact same thing but is shorter. :) |
||
node.expression = this.parseExpression(); | ||
this.expect(tt.parenR); | ||
return this.finishNode(node, "DeclaredPredicate"); | ||
} else { | ||
return this.finishNode(node, "InferredPredicate"); | ||
} | ||
}; | ||
|
||
pp.flowParseTypeAndPredicateInitialiser = function () { | ||
let oldInType = this.state.inType; | ||
this.state.inType = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like Bottom line is I wasn't able to perform the optimization you suggested. |
||
this.expect(tt.colon); | ||
let type = null; | ||
let predicate = null; | ||
if (this.match(tt.modulo)) { | ||
predicate = this.flowParsePredicate(); | ||
} else { | ||
type = this.flowParseType(); | ||
if (this.match(tt.modulo)) { | ||
predicate = this.flowParsePredicate(); | ||
} | ||
} | ||
this.state.inType = oldInType; | ||
return [type, predicate]; | ||
}; | ||
|
||
pp.flowParseDeclareClass = function (node) { | ||
this.next(); | ||
this.flowParseInterfaceish(node, true); | ||
|
@@ -46,7 +85,7 @@ pp.flowParseDeclareFunction = function (node) { | |
typeNode.params = tmp.params; | ||
typeNode.rest = tmp.rest; | ||
this.expect(tt.parenR); | ||
typeNode.returnType = this.flowParseTypeInitialiser(); | ||
[typeNode.returnType, node.predicate] = this.flowParseTypeAndPredicateInitialiser(); | ||
|
||
typeContainer.typeAnnotation = this.finishNode(typeNode, "FunctionTypeAnnotation"); | ||
id.typeAnnotation = this.finishNode(typeContainer, "TypeAnnotation"); | ||
|
@@ -674,6 +713,13 @@ pp.flowParseTypeAnnotation = function () { | |
return this.finishNode(node, "TypeAnnotation"); | ||
}; | ||
|
||
pp.flowParseTypeAndPredicateAnnotation = function () { | ||
let node = this.startNode(); | ||
let predicate; | ||
[node.typeAnnotation, predicate] = this.flowParseTypeAndPredicateInitialiser(); | ||
return [this.finishNode(node, "TypeAnnotation"), predicate]; | ||
}; | ||
|
||
pp.flowParseTypeAnnotatableIdentifier = function (requireTypeAnnotation, canBeOptionalParam) { | ||
|
||
let ident = this.parseIdentifier(); | ||
|
@@ -715,7 +761,7 @@ export default function (instance) { | |
if (this.match(tt.colon) && !allowExpression) { | ||
// if allowExpression is true then we're parsing an arrow function and if | ||
// there's a return type then it's been handled elsewhere | ||
node.returnType = this.flowParseTypeAnnotation(); | ||
[node.returnType, node.predicate] = this.flowParseTypeAndPredicateAnnotation(); | ||
} | ||
|
||
return inner.call(this, node, allowExpression); | ||
|
@@ -1161,10 +1207,11 @@ export default function (instance) { | |
if (this.match(tt.colon)) { | ||
let state = this.state.clone(); | ||
try { | ||
let returnType = this.flowParseTypeAnnotation(); | ||
let [returnType, predicate] = this.flowParseTypeAndPredicateAnnotation(); | ||
if (!this.match(tt.arrow)) this.unexpected(); | ||
// assign after it is clear it is an arrow | ||
node.returnType = returnType; | ||
node.predicate = predicate; | ||
} catch (err) { | ||
if (err instanceof SyntaxError) { | ||
this.state = state; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
declare function foo(x: mixed): boolean %checks(x !== null); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
{ | ||
"type": "File", | ||
"start": 0, | ||
"end": 60, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 0 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 60 | ||
} | ||
}, | ||
"program": { | ||
"type": "Program", | ||
"start": 0, | ||
"end": 60, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 0 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 60 | ||
} | ||
}, | ||
"sourceType": "module", | ||
"body": [ | ||
{ | ||
"type": "DeclareFunction", | ||
"start": 0, | ||
"end": 60, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 0 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 60 | ||
} | ||
}, | ||
"id": { | ||
"type": "Identifier", | ||
"start": 17, | ||
"end": 59, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 17 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 59 | ||
}, | ||
"identifierName": "foo" | ||
}, | ||
"name": "foo", | ||
"typeAnnotation": { | ||
"type": "TypeAnnotation", | ||
"start": 20, | ||
"end": 59, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 20 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 59 | ||
} | ||
}, | ||
"typeAnnotation": { | ||
"type": "FunctionTypeAnnotation", | ||
"start": 20, | ||
"end": 59, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 20 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 59 | ||
} | ||
}, | ||
"typeParameters": null, | ||
"params": [ | ||
{ | ||
"type": "FunctionTypeParam", | ||
"start": 21, | ||
"end": 29, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 21 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 29 | ||
} | ||
}, | ||
"name": { | ||
"type": "Identifier", | ||
"start": 21, | ||
"end": 22, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 21 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 22 | ||
}, | ||
"identifierName": "x" | ||
}, | ||
"name": "x" | ||
}, | ||
"optional": false, | ||
"typeAnnotation": { | ||
"type": "MixedTypeAnnotation", | ||
"start": 24, | ||
"end": 29, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 24 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 29 | ||
} | ||
} | ||
} | ||
} | ||
], | ||
"rest": null, | ||
"returnType": { | ||
"type": "BooleanTypeAnnotation", | ||
"start": 32, | ||
"end": 39, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 32 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 39 | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
"predicate": { | ||
"type": "DeclaredPredicate", | ||
"start": 40, | ||
"end": 59, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 40 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 59 | ||
} | ||
}, | ||
"expression": { | ||
"type": "BinaryExpression", | ||
"start": 48, | ||
"end": 58, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 48 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 58 | ||
} | ||
}, | ||
"left": { | ||
"type": "Identifier", | ||
"start": 48, | ||
"end": 49, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 48 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 49 | ||
}, | ||
"identifierName": "x" | ||
}, | ||
"name": "x" | ||
}, | ||
"operator": "!==", | ||
"right": { | ||
"type": "NullLiteral", | ||
"start": 54, | ||
"end": 58, | ||
"loc": { | ||
"start": { | ||
"line": 1, | ||
"column": 54 | ||
}, | ||
"end": { | ||
"line": 1, | ||
"column": 58 | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
], | ||
"directives": [] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
var f = (x: mixed): %checks => typeof x === "string"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is not covered. Do you think we can have two tests that check if there are parse errors in these cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also use
this.unexpected(moduloPos);
here. It's a little bit shorter but otherwise isn't really important if not changed.