From a4ca1cc645e02fb0d2965a63391e99fb7f2979d7 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Thu, 20 Apr 2017 16:40:55 +0100 Subject: [PATCH] Cherry-pick #418 to 6.x (#476) * Add support for flow type spread (#418) * Add support for flow type spread * Broaden spreadable types from primary to all, more tests * Eliminate variance sigil for type spreads, better errors, fix tests # Conflicts: # src/plugins/flow.js * Fix tests --- src/plugins/flow.js | 54 ++-- .../flow/type-annotations/112/options.json | 2 +- .../flow/type-annotations/113/options.json | 2 +- .../flow/type-annotations/125/options.json | 2 +- .../flow/type-annotations/135/actual.js | 3 + .../flow/type-annotations/135/expected.json | 117 ++++++++ .../flow/type-annotations/136/actual.js | 4 + .../flow/type-annotations/136/expected.json | 175 +++++++++++ .../flow/type-annotations/137/actual.js | 3 + .../flow/type-annotations/137/options.json | 3 + .../flow/type-annotations/138/actual.js | 5 + .../flow/type-annotations/138/expected.json | 283 ++++++++++++++++++ .../flow/type-annotations/139/actual.js | 4 + .../flow/type-annotations/139/options.json | 3 + 14 files changed, 638 insertions(+), 22 deletions(-) create mode 100644 test/fixtures/flow/type-annotations/135/actual.js create mode 100644 test/fixtures/flow/type-annotations/135/expected.json create mode 100644 test/fixtures/flow/type-annotations/136/actual.js create mode 100644 test/fixtures/flow/type-annotations/136/expected.json create mode 100644 test/fixtures/flow/type-annotations/137/actual.js create mode 100644 test/fixtures/flow/type-annotations/137/options.json create mode 100644 test/fixtures/flow/type-annotations/138/actual.js create mode 100644 test/fixtures/flow/type-annotations/138/expected.json create mode 100644 test/fixtures/flow/type-annotations/139/actual.js create mode 100644 test/fixtures/flow/type-annotations/139/options.json diff --git a/src/plugins/flow.js b/src/plugins/flow.js index d30545b113..ab6003aa2a 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -194,7 +194,7 @@ pp.flowParseDeclareInterface = function (node) { // Interfaces -pp.flowParseInterfaceish = function (node, allowStatic) { +pp.flowParseInterfaceish = function (node) { node.id = this.parseIdentifier(); if (this.isRelational("<")) { @@ -219,7 +219,7 @@ pp.flowParseInterfaceish = function (node, allowStatic) { } while (this.eat(tt.comma)); } - node.body = this.flowParseObjectType(allowStatic); + node.body = this.flowParseObjectType(true, false, false); }; pp.flowParseInterfaceExtends = function () { @@ -400,7 +400,7 @@ pp.flowParseObjectTypeCallProperty = function (node, isStatic) { return this.finishNode(node, "ObjectTypeCallProperty"); }; -pp.flowParseObjectType = function (allowStatic, allowExact) { +pp.flowParseObjectType = function (allowStatic, allowExact, allowSpread) { const oldInType = this.state.inType; this.state.inType = true; @@ -448,24 +448,40 @@ pp.flowParseObjectType = function (allowStatic, allowExact) { } nodeStart.callProperties.push(this.flowParseObjectTypeCallProperty(node, isStatic)); } else { - propertyKey = this.flowParseObjectPropertyKey(); - if (this.isRelational("<") || this.match(tt.parenL)) { - // This is a method property + if (this.match(tt.ellipsis)) { + if (!allowSpread) { + this.unexpected( + null, + "Spread operator cannnot appear in class or interface definitions" + ); + } if (variance) { - this.unexpected(variancePos); + this.unexpected(variance.start, "Spread properties cannot have variance"); } - nodeStart.properties.push(this.flowParseObjectTypeMethod(startPos, startLoc, isStatic, propertyKey)); + this.expect(tt.ellipsis); + node.argument = this.flowParseType(); + this.flowObjectTypeSemicolon(); + nodeStart.properties.push(this.finishNode(node, "ObjectTypeSpreadProperty")); } else { - if (this.eat(tt.question)) { - optional = true; + propertyKey = this.flowParseObjectPropertyKey(); + if (this.isRelational("<") || this.match(tt.parenL)) { + // This is a method property + if (variance) { + this.unexpected(variance.start); + } + nodeStart.properties.push(this.flowParseObjectTypeMethod(startPos, startLoc, isStatic, propertyKey)); + } else { + if (this.eat(tt.question)) { + optional = true; + } + node.key = propertyKey; + node.value = this.flowParseTypeInitialiser(); + node.optional = optional; + node.static = isStatic; + node.variance = variance; + this.flowObjectTypeSemicolon(); + nodeStart.properties.push(this.finishNode(node, "ObjectTypeProperty")); } - node.key = propertyKey; - node.value = this.flowParseTypeInitialiser(); - node.optional = optional; - node.static = isStatic; - node.variance = variance; - this.flowObjectTypeSemicolon(); - nodeStart.properties.push(this.finishNode(node, "ObjectTypeProperty")); } } @@ -627,10 +643,10 @@ pp.flowParsePrimaryType = function () { return this.flowIdentToTypeAnnotation(startPos, startLoc, node, this.parseIdentifier()); case tt.braceL: - return this.flowParseObjectType(false, false); + return this.flowParseObjectType(false, false, true); case tt.braceBarL: - return this.flowParseObjectType(false, true); + return this.flowParseObjectType(false, true, true); case tt.bracketL: return this.flowParseTupleType(); diff --git a/test/fixtures/flow/type-annotations/112/options.json b/test/fixtures/flow/type-annotations/112/options.json index 89bfc2d73f..9e093bfdcd 100644 --- a/test/fixtures/flow/type-annotations/112/options.json +++ b/test/fixtures/flow/type-annotations/112/options.json @@ -1,3 +1,3 @@ { - "throws": "Unexpected token (1:10)" + "throws": "Unexpected token (1:12)" } diff --git a/test/fixtures/flow/type-annotations/113/options.json b/test/fixtures/flow/type-annotations/113/options.json index 89bfc2d73f..9e093bfdcd 100644 --- a/test/fixtures/flow/type-annotations/113/options.json +++ b/test/fixtures/flow/type-annotations/113/options.json @@ -1,3 +1,3 @@ { - "throws": "Unexpected token (1:10)" + "throws": "Unexpected token (1:12)" } diff --git a/test/fixtures/flow/type-annotations/125/options.json b/test/fixtures/flow/type-annotations/125/options.json index 2e4647b080..6079f138a6 100644 --- a/test/fixtures/flow/type-annotations/125/options.json +++ b/test/fixtures/flow/type-annotations/125/options.json @@ -1,3 +1,3 @@ { - "throws": "Unexpected token (1:26)" + "throws": "Unexpected token (1:29)" } diff --git a/test/fixtures/flow/type-annotations/135/actual.js b/test/fixtures/flow/type-annotations/135/actual.js new file mode 100644 index 0000000000..b79dc870ee --- /dev/null +++ b/test/fixtures/flow/type-annotations/135/actual.js @@ -0,0 +1,3 @@ +type A = { + ...any, +}; diff --git a/test/fixtures/flow/type-annotations/135/expected.json b/test/fixtures/flow/type-annotations/135/expected.json new file mode 100644 index 0000000000..6f973de0f6 --- /dev/null +++ b/test/fixtures/flow/type-annotations/135/expected.json @@ -0,0 +1,117 @@ +{ + "type": "File", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 2 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 2 + } + }, + "sourceType": "module", + "body": [ + { + "type": "TypeAlias", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 5, + "end": 6, + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 6 + }, + "identifierName": "A" + }, + "name": "A" + }, + "typeParameters": null, + "right": { + "type": "ObjectTypeAnnotation", + "start": 9, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 3, + "column": 1 + } + }, + "callProperties": [], + "properties": [ + { + "type": "ObjectTypeSpreadProperty", + "start": 12, + "end": 19, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 8 + } + }, + "argument": { + "type": "AnyTypeAnnotation", + "start": 15, + "end": 18, + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 7 + } + } + } + } + ], + "indexers": [], + "exact": false + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/test/fixtures/flow/type-annotations/136/actual.js b/test/fixtures/flow/type-annotations/136/actual.js new file mode 100644 index 0000000000..09c0a04398 --- /dev/null +++ b/test/fixtures/flow/type-annotations/136/actual.js @@ -0,0 +1,4 @@ +type A = { + p: {}, + ...{}, +}; diff --git a/test/fixtures/flow/type-annotations/136/expected.json b/test/fixtures/flow/type-annotations/136/expected.json new file mode 100644 index 0000000000..18d746c433 --- /dev/null +++ b/test/fixtures/flow/type-annotations/136/expected.json @@ -0,0 +1,175 @@ +{ + "type": "File", + "start": 0, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 2 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 2 + } + }, + "sourceType": "module", + "body": [ + { + "type": "TypeAlias", + "start": 0, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 5, + "end": 6, + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 6 + }, + "identifierName": "A" + }, + "name": "A" + }, + "typeParameters": null, + "right": { + "type": "ObjectTypeAnnotation", + "start": 9, + "end": 28, + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "callProperties": [], + "properties": [ + { + "type": "ObjectTypeProperty", + "start": 12, + "end": 18, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 7 + } + }, + "key": { + "type": "Identifier", + "start": 12, + "end": 13, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 2 + }, + "identifierName": "p" + }, + "name": "p" + }, + "value": { + "type": "ObjectTypeAnnotation", + "start": 15, + "end": 17, + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "callProperties": [], + "properties": [], + "indexers": [], + "exact": false + }, + "optional": false, + "static": false, + "variance": null + }, + { + "type": "ObjectTypeSpreadProperty", + "start": 20, + "end": 26, + "loc": { + "start": { + "line": 3, + "column": 1 + }, + "end": { + "line": 3, + "column": 7 + } + }, + "argument": { + "type": "ObjectTypeAnnotation", + "start": 23, + "end": 25, + "loc": { + "start": { + "line": 3, + "column": 4 + }, + "end": { + "line": 3, + "column": 6 + } + }, + "callProperties": [], + "properties": [], + "indexers": [], + "exact": false + } + } + ], + "indexers": [], + "exact": false + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/test/fixtures/flow/type-annotations/137/actual.js b/test/fixtures/flow/type-annotations/137/actual.js new file mode 100644 index 0000000000..b039a41ed2 --- /dev/null +++ b/test/fixtures/flow/type-annotations/137/actual.js @@ -0,0 +1,3 @@ +interface A { + ...any, +}; diff --git a/test/fixtures/flow/type-annotations/137/options.json b/test/fixtures/flow/type-annotations/137/options.json new file mode 100644 index 0000000000..79756f8c33 --- /dev/null +++ b/test/fixtures/flow/type-annotations/137/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Spread operator cannnot appear in class or interface definitions (2:1)" +} diff --git a/test/fixtures/flow/type-annotations/138/actual.js b/test/fixtures/flow/type-annotations/138/actual.js new file mode 100644 index 0000000000..9a607c669f --- /dev/null +++ b/test/fixtures/flow/type-annotations/138/actual.js @@ -0,0 +1,5 @@ +class A {} +class B {} +type C = { + ...A&B +}; diff --git a/test/fixtures/flow/type-annotations/138/expected.json b/test/fixtures/flow/type-annotations/138/expected.json new file mode 100644 index 0000000000..3aabc9dfd3 --- /dev/null +++ b/test/fixtures/flow/type-annotations/138/expected.json @@ -0,0 +1,283 @@ +{ + "type": "File", + "start": 0, + "end": 43, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 2 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 43, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 2 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ClassDeclaration", + "start": 0, + "end": 10, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "id": { + "type": "Identifier", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + }, + "identifierName": "A" + }, + "name": "A" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 8, + "end": 10, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "body": [] + } + }, + { + "type": "ClassDeclaration", + "start": 11, + "end": 21, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "id": { + "type": "Identifier", + "start": 17, + "end": 18, + "loc": { + "start": { + "line": 2, + "column": 6 + }, + "end": { + "line": 2, + "column": 7 + }, + "identifierName": "B" + }, + "name": "B" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 19, + "end": 21, + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "body": [] + } + }, + { + "type": "TypeAlias", + "start": 22, + "end": 43, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 5, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 27, + "end": 28, + "loc": { + "start": { + "line": 3, + "column": 5 + }, + "end": { + "line": 3, + "column": 6 + }, + "identifierName": "C" + }, + "name": "C" + }, + "typeParameters": null, + "right": { + "type": "ObjectTypeAnnotation", + "start": 31, + "end": 42, + "loc": { + "start": { + "line": 3, + "column": 9 + }, + "end": { + "line": 5, + "column": 1 + } + }, + "callProperties": [], + "properties": [ + { + "type": "ObjectTypeSpreadProperty", + "start": 34, + "end": 40, + "loc": { + "start": { + "line": 4, + "column": 1 + }, + "end": { + "line": 4, + "column": 7 + } + }, + "argument": { + "type": "IntersectionTypeAnnotation", + "start": 37, + "end": 40, + "loc": { + "start": { + "line": 4, + "column": 4 + }, + "end": { + "line": 4, + "column": 7 + } + }, + "types": [ + { + "type": "GenericTypeAnnotation", + "start": 37, + "end": 38, + "loc": { + "start": { + "line": 4, + "column": 4 + }, + "end": { + "line": 4, + "column": 5 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 37, + "end": 38, + "loc": { + "start": { + "line": 4, + "column": 4 + }, + "end": { + "line": 4, + "column": 5 + }, + "identifierName": "A" + }, + "name": "A" + } + }, + { + "type": "GenericTypeAnnotation", + "start": 39, + "end": 40, + "loc": { + "start": { + "line": 4, + "column": 6 + }, + "end": { + "line": 4, + "column": 7 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 39, + "end": 40, + "loc": { + "start": { + "line": 4, + "column": 6 + }, + "end": { + "line": 4, + "column": 7 + }, + "identifierName": "B" + }, + "name": "B" + } + } + ] + } + } + ], + "indexers": [], + "exact": false + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/test/fixtures/flow/type-annotations/139/actual.js b/test/fixtures/flow/type-annotations/139/actual.js new file mode 100644 index 0000000000..fbc1330612 --- /dev/null +++ b/test/fixtures/flow/type-annotations/139/actual.js @@ -0,0 +1,4 @@ +class A {} +type C = { + -...A +}; diff --git a/test/fixtures/flow/type-annotations/139/options.json b/test/fixtures/flow/type-annotations/139/options.json new file mode 100644 index 0000000000..fe5ad00277 --- /dev/null +++ b/test/fixtures/flow/type-annotations/139/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Spread properties cannot have variance (3:2)" +}