From a38a58bad8b1482eff52df2235779b95421947c5 Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Mon, 29 Feb 2016 17:20:55 -0800 Subject: [PATCH] Add support for Flow def-site variance syntax This syntax allows you to specify whether a type variable can appear in a covariant or contravariant position, and is super useful for, say, Promise. Right now this is hacked in jankily, but in the next major release we should stop using Identifier nodes for type parameters. --- .../babel-generator/src/generators/types.js | 10 + .../fixtures/flow/def-site-variance/actual.js | 3 + .../flow/def-site-variance/expected.js | 3 + .../flow-comments/def-site-variance/actual.js | 3 + .../def-site-variance/expected.js | 3 + .../strip-types/def-site-variance/actual.js | 3 + .../strip-types/def-site-variance/expected.js | 2 + packages/babylon/src/plugins/flow.js | 14 + .../flow/def-site-variance/1/actual.js | 3 + .../flow/def-site-variance/1/expected.json | 336 ++++++++++++++++++ 10 files changed, 380 insertions(+) create mode 100644 packages/babel-generator/test/fixtures/flow/def-site-variance/actual.js create mode 100644 packages/babel-generator/test/fixtures/flow/def-site-variance/expected.js create mode 100644 packages/babel-plugin-transform-flow-comments/test/fixtures/flow-comments/def-site-variance/actual.js create mode 100644 packages/babel-plugin-transform-flow-comments/test/fixtures/flow-comments/def-site-variance/expected.js create mode 100644 packages/babel-plugin-transform-flow-strip-types/test/fixtures/strip-types/def-site-variance/actual.js create mode 100644 packages/babel-plugin-transform-flow-strip-types/test/fixtures/strip-types/def-site-variance/expected.js create mode 100644 packages/babylon/test/fixtures/flow/def-site-variance/1/actual.js create mode 100644 packages/babylon/test/fixtures/flow/def-site-variance/1/expected.json diff --git a/packages/babel-generator/src/generators/types.js b/packages/babel-generator/src/generators/types.js index aed1c3f35c..6ca86a74e1 100644 --- a/packages/babel-generator/src/generators/types.js +++ b/packages/babel-generator/src/generators/types.js @@ -4,6 +4,16 @@ import * as t from "babel-types"; export function Identifier(node: Object) { + // FIXME: We hang variance off Identifer to support Flow's def-site variance. + // This is a terrible hack, but changing type annotations to use a new, + // dedicated node would be a breaking change. This should be cleaned up in + // the next major. + if (node.variance === "plus") { + this.push("+"); + } else if (node.variance === "minus") { + this.push("-"); + } + this.push(node.name); } diff --git a/packages/babel-generator/test/fixtures/flow/def-site-variance/actual.js b/packages/babel-generator/test/fixtures/flow/def-site-variance/actual.js new file mode 100644 index 0000000000..f046505722 --- /dev/null +++ b/packages/babel-generator/test/fixtures/flow/def-site-variance/actual.js @@ -0,0 +1,3 @@ +class C<+T, -U> {} +function f<+T, -U>() {} +type T<+T, -U> = {}; diff --git a/packages/babel-generator/test/fixtures/flow/def-site-variance/expected.js b/packages/babel-generator/test/fixtures/flow/def-site-variance/expected.js new file mode 100644 index 0000000000..f046505722 --- /dev/null +++ b/packages/babel-generator/test/fixtures/flow/def-site-variance/expected.js @@ -0,0 +1,3 @@ +class C<+T, -U> {} +function f<+T, -U>() {} +type T<+T, -U> = {}; diff --git a/packages/babel-plugin-transform-flow-comments/test/fixtures/flow-comments/def-site-variance/actual.js b/packages/babel-plugin-transform-flow-comments/test/fixtures/flow-comments/def-site-variance/actual.js new file mode 100644 index 0000000000..f046505722 --- /dev/null +++ b/packages/babel-plugin-transform-flow-comments/test/fixtures/flow-comments/def-site-variance/actual.js @@ -0,0 +1,3 @@ +class C<+T, -U> {} +function f<+T, -U>() {} +type T<+T, -U> = {}; diff --git a/packages/babel-plugin-transform-flow-comments/test/fixtures/flow-comments/def-site-variance/expected.js b/packages/babel-plugin-transform-flow-comments/test/fixtures/flow-comments/def-site-variance/expected.js new file mode 100644 index 0000000000..195209568b --- /dev/null +++ b/packages/babel-plugin-transform-flow-comments/test/fixtures/flow-comments/def-site-variance/expected.js @@ -0,0 +1,3 @@ +class C /*:: <+T, -U>*/ {} +function f /*:: <+T, -U>*/() {} +/*:: type T<+T, -U> = {};*/ diff --git a/packages/babel-plugin-transform-flow-strip-types/test/fixtures/strip-types/def-site-variance/actual.js b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/strip-types/def-site-variance/actual.js new file mode 100644 index 0000000000..f046505722 --- /dev/null +++ b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/strip-types/def-site-variance/actual.js @@ -0,0 +1,3 @@ +class C<+T, -U> {} +function f<+T, -U>() {} +type T<+T, -U> = {}; diff --git a/packages/babel-plugin-transform-flow-strip-types/test/fixtures/strip-types/def-site-variance/expected.js b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/strip-types/def-site-variance/expected.js new file mode 100644 index 0000000000..9eb99a782f --- /dev/null +++ b/packages/babel-plugin-transform-flow-strip-types/test/fixtures/strip-types/def-site-variance/expected.js @@ -0,0 +1,2 @@ +class C {} +function f() {} diff --git a/packages/babylon/src/plugins/flow.js b/packages/babylon/src/plugins/flow.js index f856955176..3ecaeb84e2 100644 --- a/packages/babylon/src/plugins/flow.js +++ b/packages/babylon/src/plugins/flow.js @@ -624,9 +624,23 @@ pp.flowParseTypeAnnotation = function () { }; pp.flowParseTypeAnnotatableIdentifier = function (requireTypeAnnotation, canBeOptionalParam) { + 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 ident = this.parseIdentifier(); let isOptionalParam = false; + if (variance) { + ident.variance = variance; + } + if (canBeOptionalParam && this.eat(tt.question)) { this.expect(tt.question); isOptionalParam = true; diff --git a/packages/babylon/test/fixtures/flow/def-site-variance/1/actual.js b/packages/babylon/test/fixtures/flow/def-site-variance/1/actual.js new file mode 100644 index 0000000000..5eb19f458a --- /dev/null +++ b/packages/babylon/test/fixtures/flow/def-site-variance/1/actual.js @@ -0,0 +1,3 @@ +class C<+T,-U> {} +function f<+T,-U>() {} +type T<+T,-U> = {} diff --git a/packages/babylon/test/fixtures/flow/def-site-variance/1/expected.json b/packages/babylon/test/fixtures/flow/def-site-variance/1/expected.json new file mode 100644 index 0000000000..b42edfb108 --- /dev/null +++ b/packages/babylon/test/fixtures/flow/def-site-variance/1/expected.json @@ -0,0 +1,336 @@ +{ + "type": "File", + "start": 0, + "end": 59, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 18 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 59, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 18 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ClassDeclaration", + "start": 0, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "id": { + "type": "Identifier", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "name": "C" + }, + "typeParameters": { + "type": "TypeParameterDeclaration", + "start": 7, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 14 + } + }, + "params": [ + { + "type": "Identifier", + "start": 9, + "end": 10, + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "name": "T", + "variance": "plus" + }, + { + "type": "Identifier", + "start": 12, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 12 + }, + "end": { + "line": 1, + "column": 13 + } + }, + "name": "U", + "variance": "minus" + } + ] + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 15, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "body": [] + } + }, + { + "type": "FunctionDeclaration", + "start": 18, + "end": 40, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 22 + } + }, + "id": { + "type": "Identifier", + "start": 27, + "end": 28, + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "name": "f" + }, + "generator": false, + "expression": false, + "async": false, + "typeParameters": { + "type": "TypeParameterDeclaration", + "start": 28, + "end": 35, + "loc": { + "start": { + "line": 2, + "column": 10 + }, + "end": { + "line": 2, + "column": 17 + } + }, + "params": [ + { + "type": "Identifier", + "start": 30, + "end": 31, + "loc": { + "start": { + "line": 2, + "column": 12 + }, + "end": { + "line": 2, + "column": 13 + } + }, + "name": "T", + "variance": "plus" + }, + { + "type": "Identifier", + "start": 33, + "end": 34, + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 16 + } + }, + "name": "U", + "variance": "minus" + } + ] + }, + "params": [], + "body": { + "type": "BlockStatement", + "start": 38, + "end": 40, + "loc": { + "start": { + "line": 2, + "column": 20 + }, + "end": { + "line": 2, + "column": 22 + } + }, + "body": [], + "directives": [] + } + }, + { + "type": "TypeAlias", + "start": 41, + "end": 59, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 18 + } + }, + "id": { + "type": "Identifier", + "start": 46, + "end": 47, + "loc": { + "start": { + "line": 3, + "column": 5 + }, + "end": { + "line": 3, + "column": 6 + } + }, + "name": "T" + }, + "typeParameters": { + "type": "TypeParameterDeclaration", + "start": 47, + "end": 54, + "loc": { + "start": { + "line": 3, + "column": 6 + }, + "end": { + "line": 3, + "column": 13 + } + }, + "params": [ + { + "type": "Identifier", + "start": 49, + "end": 50, + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 9 + } + }, + "name": "T", + "variance": "plus" + }, + { + "type": "Identifier", + "start": 52, + "end": 53, + "loc": { + "start": { + "line": 3, + "column": 11 + }, + "end": { + "line": 3, + "column": 12 + } + }, + "name": "U", + "variance": "minus" + } + ] + }, + "right": { + "type": "ObjectTypeAnnotation", + "start": 57, + "end": 59, + "loc": { + "start": { + "line": 3, + "column": 16 + }, + "end": { + "line": 3, + "column": 18 + } + }, + "callProperties": [], + "properties": [], + "indexers": [] + } + } + ], + "directives": [] + } +} \ No newline at end of file