Skip to content
This repository has been archived by the owner on May 19, 2018. It is now read-only.

Commit

Permalink
Property variance type annotations for Flow plugin (#161)
Browse files Browse the repository at this point in the history
* Property variance type annotations for Flow plugin

Non-method properties and indexers of object types, declare class, and
interfaces can be "positive" or "negative." Class fields, but again not
methods, can also have variance.

This PR generalizes the variance annotations for type parameters into a
new node type, and reuses that node for those properties.

The code for object types is reused for interfaces and declare classes.
The changes there are straightfoward.

The code for class fields is reused for object literals, which do not
support variance annotations (currently). This code is a bit sketchy,
because we always parse variance annotations in the `parsePropertyName`
extension, then error in a the subsequent parse phase for object
literals (`parseObjPropValue`) or class methods (`parseClassMethod`).

* Remove bogus unreachable code, clarify variance parsing conditional

* Don't use a new node type for variance annotations

Adding a new node type, specifically changing the TypeParameter node's
variance property to be node-valued, is a breaking change. We might
choose to make this breaking change in a later version.

* s/start/variancePos
  • Loading branch information
samwgoldman authored and danez committed Oct 14, 2016
1 parent b5877f0 commit 26809e8
Show file tree
Hide file tree
Showing 63 changed files with 1,228 additions and 65 deletions.
61 changes: 50 additions & 11 deletions src/plugins/flow.js
Expand Up @@ -205,15 +205,7 @@ pp.flowParseTypeAlias = function (node) {
pp.flowParseTypeParameter = function () {
let node = this.startNode();

let variance;
if (this.match(tt.plusMin)) {
if (this.state.value === "+") {
variance = "plus";
} else if (this.state.value === "-") {
variance = "minus";
}
this.eat(tt.plusMin);
}
let variance = this.flowParseVariance();

let ident = this.flowParseTypeAnnotatableIdentifier(false, false);
node.name = ident.name;
Expand Down Expand Up @@ -278,14 +270,15 @@ pp.flowParseObjectPropertyKey = function () {
return (this.match(tt.num) || this.match(tt.string)) ? this.parseExprAtom() : this.parseIdentifier(true);
};

pp.flowParseObjectTypeIndexer = function (node, isStatic) {
pp.flowParseObjectTypeIndexer = function (node, isStatic, variance) {
node.static = isStatic;

this.expect(tt.bracketL);
node.id = this.flowParseObjectPropertyKey();
node.key = this.flowParseTypeInitialiser();
this.expect(tt.bracketR);
node.value = this.flowParseTypeInitialiser();
node.variance = variance;

this.flowObjectTypeSemicolon();
return this.finishNode(node, "ObjectTypeIndexer");
Expand Down Expand Up @@ -371,9 +364,15 @@ pp.flowParseObjectType = function (allowStatic, allowExact) {
isStatic = true;
}

let variancePos = this.state.start;
let variance = this.flowParseVariance();

if (this.match(tt.bracketL)) {
nodeStart.indexers.push(this.flowParseObjectTypeIndexer(node, isStatic));
nodeStart.indexers.push(this.flowParseObjectTypeIndexer(node, isStatic, variance));
} else if (this.match(tt.parenL) || this.isRelational("<")) {
if (variance) {
this.unexpected(variancePos);
}
nodeStart.callProperties.push(this.flowParseObjectTypeCallProperty(node, allowStatic));
} else {
if (isStatic && this.match(tt.colon)) {
Expand All @@ -383,6 +382,9 @@ pp.flowParseObjectType = function (allowStatic, allowExact) {
}
if (this.isRelational("<") || this.match(tt.parenL)) {
// This is a method property
if (variance) {
this.unexpected(variancePos);
}
nodeStart.properties.push(this.flowParseObjectTypeMethod(startPos, startLoc, isStatic, propertyKey));
} else {
if (this.eat(tt.question)) {
Expand All @@ -392,6 +394,7 @@ pp.flowParseObjectType = function (allowStatic, allowExact) {
node.value = this.flowParseTypeInitialiser();
node.optional = optional;
node.static = isStatic;
node.variance = variance;
this.flowObjectTypeSemicolon();
nodeStart.properties.push(this.finishNode(node, "ObjectTypeProperty"));
}
Expand Down Expand Up @@ -736,6 +739,19 @@ pp.typeCastToParameter = function (node) {
);
};

pp.flowParseVariance = function() {
let variance = null;
if (this.match(tt.plusMin)) {
if (this.state.value === "+") {
variance = "plus";
} else if (this.state.value === "-") {
variance = "minus";
}
this.next();
}
return variance;
};

export default function (instance) {
// plain function return types: function name(): string {}
instance.extend("parseFunctionBody", function (inner) {
Expand Down Expand Up @@ -990,6 +1006,7 @@ export default function (instance) {
// parse class property type annotations
instance.extend("parseClassProperty", function (inner) {
return function (node) {
delete node.variancePos;
if (this.match(tt.colon)) {
node.typeAnnotation = this.flowParseTypeAnnotation();
}
Expand All @@ -1007,6 +1024,11 @@ export default function (instance) {
// parse type parameters for class methods
instance.extend("parseClassMethod", function () {
return function (classBody, method, isGenerator, isAsync) {
if (method.variance) {
this.unexpected(method.variancePos);
}
delete method.variance;
delete method.variancePos;
if (this.isRelational("<")) {
method.typeParameters = this.flowParseTypeParameterDeclaration();
}
Expand Down Expand Up @@ -1039,9 +1061,26 @@ export default function (instance) {
};
});

instance.extend("parsePropertyName", function (inner) {
return function (node) {
let variancePos = this.state.start;
let variance = this.flowParseVariance();
let key = inner.call(this, node);
node.variance = variance;
node.variancePos = variancePos;
return key;
};
});

// parse type parameters for object method shorthand
instance.extend("parseObjPropValue", function (inner) {
return function (prop) {
if (prop.variance) {
this.unexpected(prop.variancePos);
}
delete prop.variance;
delete prop.variancePos;

let typeParameters;

// method shorthand
Expand Down
3 changes: 2 additions & 1 deletion test/fixtures/flow/call-properties/3/expected.json
Expand Up @@ -292,7 +292,8 @@
}
}
},
"optional": false
"optional": false,
"variance": null
}
],
"indexers": []
Expand Down
3 changes: 2 additions & 1 deletion test/fixtures/flow/declare-statements/10/expected.json
Expand Up @@ -188,7 +188,8 @@
}
},
"optional": false,
"static": true
"static": true,
"variance": null
}
],
"indexers": []
Expand Down
3 changes: 2 additions & 1 deletion test/fixtures/flow/declare-statements/11/expected.json
Expand Up @@ -137,7 +137,8 @@
"column": 51
}
}
}
},
"variance": null
}
]
}
Expand Down
3 changes: 2 additions & 1 deletion test/fixtures/flow/declare-statements/14/expected.json
Expand Up @@ -231,7 +231,8 @@
},
"name": "U"
}
}
},
"variance": null
}
]
}
Expand Down
6 changes: 4 additions & 2 deletions test/fixtures/flow/declare-statements/15/expected.json
Expand Up @@ -122,7 +122,8 @@
}
}
},
"optional": false
"optional": false,
"variance": null
}
],
"indexers": []
Expand Down Expand Up @@ -271,7 +272,8 @@
"name": "T"
}
},
"optional": false
"optional": false,
"variance": null
}
],
"indexers": []
Expand Down
9 changes: 6 additions & 3 deletions test/fixtures/flow/declare-statements/17/expected.json
Expand Up @@ -125,7 +125,8 @@
}
},
"optional": false,
"static": false
"static": false,
"variance": null
},
{
"type": "ObjectTypeProperty",
Expand Down Expand Up @@ -174,7 +175,8 @@
}
},
"optional": false,
"static": true
"static": true,
"variance": null
},
{
"type": "ObjectTypeProperty",
Expand Down Expand Up @@ -223,7 +225,8 @@
}
},
"optional": false,
"static": false
"static": false,
"variance": null
}
],
"indexers": [],
Expand Down
3 changes: 2 additions & 1 deletion test/fixtures/flow/declare-statements/9/expected.json
Expand Up @@ -235,7 +235,8 @@
}
}
},
"optional": false
"optional": false,
"variance": null
}
],
"indexers": []
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/flow/def-site-variance/1/expected.json
Expand Up @@ -333,4 +333,4 @@
],
"directives": []
}
}
}
Expand Up @@ -139,7 +139,8 @@
},
"typeParameters": null
},
"optional": false
"optional": false,
"variance": null
}
],
"indexers": []
Expand Down
Expand Up @@ -121,7 +121,8 @@
}
}
},
"optional": false
"optional": false,
"variance": null
}
],
"indexers": [
Expand Down Expand Up @@ -184,7 +185,8 @@
"column": 46
}
}
}
},
"variance": null
}
]
}
Expand Down
6 changes: 4 additions & 2 deletions test/fixtures/flow/type-alias/4/expected.json
Expand Up @@ -140,7 +140,8 @@
"raw": "\"A\""
}
},
"optional": false
"optional": false,
"variance": null
}
],
"indexers": []
Expand Down Expand Up @@ -211,7 +212,8 @@
"raw": "\"B\""
}
},
"optional": false
"optional": false,
"variance": null
}
],
"indexers": []
Expand Down

0 comments on commit 26809e8

Please sign in to comment.