From 7877829fcb4d4ed50cb2832019947cfc2eb55b9c Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Fri, 30 Sep 2016 17:37:55 +0200 Subject: [PATCH] Add static errors for object rest (#149) * Fix parsing object rest This makes object-rest-spread behave according to spec and only allow one rest operator and enforces it to be the last param in the object. Also move all object-rest-spread tests to a own folder. * Show nicer error messages --- src/parser/expression.js | 22 +- .../9 => object-rest-spread/1}/actual.js | 0 .../9 => object-rest-spread/1}/expected.json | 0 .../object-rest-spread/10/actual.js | 1 + .../object-rest-spread/10/expected.json | 211 ++++++++++++++++++ .../10 => object-rest-spread/2}/actual.js | 0 .../10 => object-rest-spread/2}/expected.json | 0 .../11 => object-rest-spread/3}/actual.js | 0 .../11 => object-rest-spread/3}/expected.json | 0 .../12 => object-rest-spread/4}/actual.js | 0 .../12 => object-rest-spread/4}/expected.json | 0 .../13 => object-rest-spread/5}/actual.js | 0 .../13 => object-rest-spread/5}/expected.json | 0 .../object-rest-spread/6/actual.js | 1 + .../14 => object-rest-spread/6}/expected.json | 52 +++-- .../object-rest-spread/7/actual.js | 1 + .../object-rest-spread/7/options.json | 3 + .../object-rest-spread/8/actual.js | 1 + .../object-rest-spread/8/options.json | 3 + .../object-rest-spread/9/actual.js | 1 + .../object-rest-spread/9/options.json | 3 + .../10 => object-rest-spread}/options.json | 0 .../uncategorised/11/options.json | 3 - .../uncategorised/12/options.json | 3 - .../uncategorised/13/options.json | 3 - .../experimental/uncategorised/14/actual.js | 1 - .../uncategorised/14/options.json | 3 - .../experimental/uncategorised/9/options.json | 3 - 28 files changed, 281 insertions(+), 34 deletions(-) rename test/fixtures/experimental/{uncategorised/9 => object-rest-spread/1}/actual.js (100%) rename test/fixtures/experimental/{uncategorised/9 => object-rest-spread/1}/expected.json (100%) create mode 100644 test/fixtures/experimental/object-rest-spread/10/actual.js create mode 100644 test/fixtures/experimental/object-rest-spread/10/expected.json rename test/fixtures/experimental/{uncategorised/10 => object-rest-spread/2}/actual.js (100%) rename test/fixtures/experimental/{uncategorised/10 => object-rest-spread/2}/expected.json (100%) rename test/fixtures/experimental/{uncategorised/11 => object-rest-spread/3}/actual.js (100%) rename test/fixtures/experimental/{uncategorised/11 => object-rest-spread/3}/expected.json (100%) rename test/fixtures/experimental/{uncategorised/12 => object-rest-spread/4}/actual.js (100%) rename test/fixtures/experimental/{uncategorised/12 => object-rest-spread/4}/expected.json (100%) rename test/fixtures/experimental/{uncategorised/13 => object-rest-spread/5}/actual.js (100%) rename test/fixtures/experimental/{uncategorised/13 => object-rest-spread/5}/expected.json (100%) create mode 100644 test/fixtures/experimental/object-rest-spread/6/actual.js rename test/fixtures/experimental/{uncategorised/14 => object-rest-spread/6}/expected.json (87%) create mode 100644 test/fixtures/experimental/object-rest-spread/7/actual.js create mode 100644 test/fixtures/experimental/object-rest-spread/7/options.json create mode 100644 test/fixtures/experimental/object-rest-spread/8/actual.js create mode 100644 test/fixtures/experimental/object-rest-spread/8/options.json create mode 100644 test/fixtures/experimental/object-rest-spread/9/actual.js create mode 100644 test/fixtures/experimental/object-rest-spread/9/options.json rename test/fixtures/experimental/{uncategorised/10 => object-rest-spread}/options.json (100%) delete mode 100644 test/fixtures/experimental/uncategorised/11/options.json delete mode 100644 test/fixtures/experimental/uncategorised/12/options.json delete mode 100644 test/fixtures/experimental/uncategorised/13/options.json delete mode 100644 test/fixtures/experimental/uncategorised/14/actual.js delete mode 100644 test/fixtures/experimental/uncategorised/14/options.json delete mode 100644 test/fixtures/experimental/uncategorised/9/options.json diff --git a/src/parser/expression.js b/src/parser/expression.js index 0d4c371938..f8a50c7d84 100644 --- a/src/parser/expression.js +++ b/src/parser/expression.js @@ -691,6 +691,8 @@ pp.parseObj = function (isPattern, refShorthandDefaultPos) { node.properties = []; this.next(); + let firstRestLocation = null; + while (!this.eat(tt.braceR)) { if (first) { first = false; @@ -713,7 +715,21 @@ pp.parseObj = function (isPattern, refShorthandDefaultPos) { prop = this.parseSpread(); prop.type = isPattern ? "RestProperty" : "SpreadProperty"; node.properties.push(prop); - continue; + if (isPattern) { + const position = this.state.start; + if (firstRestLocation !== null) { + this.unexpected(firstRestLocation, "Cannot have multiple rest elements when destructuring"); + } else if (this.eat(tt.braceR)) { + break; + } else if (this.match(tt.comma) && this.lookahead().type === tt.braceR) { + this.unexpected(position, "A trailing comma is not permitted after the rest element"); + } else { + firstRestLocation = position; + continue; + } + } else { + continue; + } } prop.method = false; @@ -753,6 +769,10 @@ pp.parseObj = function (isPattern, refShorthandDefaultPos) { node.properties.push(prop); } + if (firstRestLocation !== null) { + this.unexpected(firstRestLocation, "The rest element has to be the last element when destructuring"); + } + if (decorators.length) { this.raise(this.state.start, "You have trailing decorators with no property"); } diff --git a/test/fixtures/experimental/uncategorised/9/actual.js b/test/fixtures/experimental/object-rest-spread/1/actual.js similarity index 100% rename from test/fixtures/experimental/uncategorised/9/actual.js rename to test/fixtures/experimental/object-rest-spread/1/actual.js diff --git a/test/fixtures/experimental/uncategorised/9/expected.json b/test/fixtures/experimental/object-rest-spread/1/expected.json similarity index 100% rename from test/fixtures/experimental/uncategorised/9/expected.json rename to test/fixtures/experimental/object-rest-spread/1/expected.json diff --git a/test/fixtures/experimental/object-rest-spread/10/actual.js b/test/fixtures/experimental/object-rest-spread/10/actual.js new file mode 100644 index 0000000000..ae868e52ac --- /dev/null +++ b/test/fixtures/experimental/object-rest-spread/10/actual.js @@ -0,0 +1 @@ +let { x, y, } = obj; diff --git a/test/fixtures/experimental/object-rest-spread/10/expected.json b/test/fixtures/experimental/object-rest-spread/10/expected.json new file mode 100644 index 0000000000..bce210e65b --- /dev/null +++ b/test/fixtures/experimental/object-rest-spread/10/expected.json @@ -0,0 +1,211 @@ +{ + "type": "File", + "start": 0, + "end": 20, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 20 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 20, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 20 + } + }, + "sourceType": "script", + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 20, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 20 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 19 + } + }, + "id": { + "type": "ObjectPattern", + "start": 4, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 13 + } + }, + "properties": [ + { + "type": "ObjectProperty", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Identifier", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + }, + "identifierName": "x" + }, + "name": "x" + }, + "value": { + "type": "Identifier", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + }, + "identifierName": "x" + }, + "name": "x" + }, + "extra": { + "shorthand": true + } + }, + { + "type": "ObjectProperty", + "start": 9, + "end": 10, + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Identifier", + "start": 9, + "end": 10, + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 10 + }, + "identifierName": "y" + }, + "name": "y" + }, + "value": { + "type": "Identifier", + "start": 9, + "end": 10, + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 10 + }, + "identifierName": "y" + }, + "name": "y" + }, + "extra": { + "shorthand": true + } + } + ] + }, + "init": { + "type": "Identifier", + "start": 16, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 19 + }, + "identifierName": "obj" + }, + "name": "obj" + } + } + ], + "kind": "let" + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/test/fixtures/experimental/uncategorised/10/actual.js b/test/fixtures/experimental/object-rest-spread/2/actual.js similarity index 100% rename from test/fixtures/experimental/uncategorised/10/actual.js rename to test/fixtures/experimental/object-rest-spread/2/actual.js diff --git a/test/fixtures/experimental/uncategorised/10/expected.json b/test/fixtures/experimental/object-rest-spread/2/expected.json similarity index 100% rename from test/fixtures/experimental/uncategorised/10/expected.json rename to test/fixtures/experimental/object-rest-spread/2/expected.json diff --git a/test/fixtures/experimental/uncategorised/11/actual.js b/test/fixtures/experimental/object-rest-spread/3/actual.js similarity index 100% rename from test/fixtures/experimental/uncategorised/11/actual.js rename to test/fixtures/experimental/object-rest-spread/3/actual.js diff --git a/test/fixtures/experimental/uncategorised/11/expected.json b/test/fixtures/experimental/object-rest-spread/3/expected.json similarity index 100% rename from test/fixtures/experimental/uncategorised/11/expected.json rename to test/fixtures/experimental/object-rest-spread/3/expected.json diff --git a/test/fixtures/experimental/uncategorised/12/actual.js b/test/fixtures/experimental/object-rest-spread/4/actual.js similarity index 100% rename from test/fixtures/experimental/uncategorised/12/actual.js rename to test/fixtures/experimental/object-rest-spread/4/actual.js diff --git a/test/fixtures/experimental/uncategorised/12/expected.json b/test/fixtures/experimental/object-rest-spread/4/expected.json similarity index 100% rename from test/fixtures/experimental/uncategorised/12/expected.json rename to test/fixtures/experimental/object-rest-spread/4/expected.json diff --git a/test/fixtures/experimental/uncategorised/13/actual.js b/test/fixtures/experimental/object-rest-spread/5/actual.js similarity index 100% rename from test/fixtures/experimental/uncategorised/13/actual.js rename to test/fixtures/experimental/object-rest-spread/5/actual.js diff --git a/test/fixtures/experimental/uncategorised/13/expected.json b/test/fixtures/experimental/object-rest-spread/5/expected.json similarity index 100% rename from test/fixtures/experimental/uncategorised/13/expected.json rename to test/fixtures/experimental/object-rest-spread/5/expected.json diff --git a/test/fixtures/experimental/object-rest-spread/6/actual.js b/test/fixtures/experimental/object-rest-spread/6/actual.js new file mode 100644 index 0000000000..0d8a75b880 --- /dev/null +++ b/test/fixtures/experimental/object-rest-spread/6/actual.js @@ -0,0 +1 @@ +({x, ...y, a, ...b, c, }) diff --git a/test/fixtures/experimental/uncategorised/14/expected.json b/test/fixtures/experimental/object-rest-spread/6/expected.json similarity index 87% rename from test/fixtures/experimental/uncategorised/14/expected.json rename to test/fixtures/experimental/object-rest-spread/6/expected.json index 761b028be8..748d406185 100644 --- a/test/fixtures/experimental/uncategorised/14/expected.json +++ b/test/fixtures/experimental/object-rest-spread/6/expected.json @@ -1,7 +1,7 @@ { "type": "File", "start": 0, - "end": 23, + "end": 25, "loc": { "start": { "line": 1, @@ -9,13 +9,13 @@ }, "end": { "line": 1, - "column": 23 + "column": 25 } }, "program": { "type": "Program", "start": 0, - "end": 23, + "end": 25, "loc": { "start": { "line": 1, @@ -23,7 +23,7 @@ }, "end": { "line": 1, - "column": 23 + "column": 25 } }, "sourceType": "script", @@ -31,7 +31,7 @@ { "type": "ExpressionStatement", "start": 0, - "end": 23, + "end": 25, "loc": { "start": { "line": 1, @@ -39,13 +39,13 @@ }, "end": { "line": 1, - "column": 23 + "column": 25 } }, "expression": { "type": "ObjectExpression", "start": 1, - "end": 22, + "end": 24, "loc": { "start": { "line": 1, @@ -53,7 +53,7 @@ }, "end": { "line": 1, - "column": 22 + "column": 24 } }, "properties": [ @@ -86,7 +86,8 @@ "end": { "line": 1, "column": 3 - } + }, + "identifierName": "x" }, "name": "x" }, @@ -102,9 +103,13 @@ "end": { "line": 1, "column": 3 - } + }, + "identifierName": "x" }, "name": "x" + }, + "extra": { + "shorthand": true } }, { @@ -133,7 +138,8 @@ "end": { "line": 1, "column": 9 - } + }, + "identifierName": "y" }, "name": "y" } @@ -167,7 +173,8 @@ "end": { "line": 1, "column": 12 - } + }, + "identifierName": "a" }, "name": "a" }, @@ -183,9 +190,13 @@ "end": { "line": 1, "column": 12 - } + }, + "identifierName": "a" }, "name": "a" + }, + "extra": { + "shorthand": true } }, { @@ -214,7 +225,8 @@ "end": { "line": 1, "column": 18 - } + }, + "identifierName": "b" }, "name": "b" } @@ -248,7 +260,8 @@ "end": { "line": 1, "column": 21 - } + }, + "identifierName": "c" }, "name": "c" }, @@ -264,14 +277,19 @@ "end": { "line": 1, "column": 21 - } + }, + "identifierName": "c" }, "name": "c" + }, + "extra": { + "shorthand": true } } ], "extra": { - "parenthesized": true + "parenthesized": true, + "parenStart": 0 } } } diff --git a/test/fixtures/experimental/object-rest-spread/7/actual.js b/test/fixtures/experimental/object-rest-spread/7/actual.js new file mode 100644 index 0000000000..6a338741f5 --- /dev/null +++ b/test/fixtures/experimental/object-rest-spread/7/actual.js @@ -0,0 +1 @@ +let { ...x, y, z } = obj; diff --git a/test/fixtures/experimental/object-rest-spread/7/options.json b/test/fixtures/experimental/object-rest-spread/7/options.json new file mode 100644 index 0000000000..09b3275eae --- /dev/null +++ b/test/fixtures/experimental/object-rest-spread/7/options.json @@ -0,0 +1,3 @@ +{ + "throws": "The rest element has to be the last element when destructuring (1:10)" +} diff --git a/test/fixtures/experimental/object-rest-spread/8/actual.js b/test/fixtures/experimental/object-rest-spread/8/actual.js new file mode 100644 index 0000000000..62308375b6 --- /dev/null +++ b/test/fixtures/experimental/object-rest-spread/8/actual.js @@ -0,0 +1 @@ +let { x, y, ...z, } = obj; diff --git a/test/fixtures/experimental/object-rest-spread/8/options.json b/test/fixtures/experimental/object-rest-spread/8/options.json new file mode 100644 index 0000000000..4a97ac85ea --- /dev/null +++ b/test/fixtures/experimental/object-rest-spread/8/options.json @@ -0,0 +1,3 @@ +{ + "throws": "A trailing comma is not permitted after the rest element (1:16)" +} diff --git a/test/fixtures/experimental/object-rest-spread/9/actual.js b/test/fixtures/experimental/object-rest-spread/9/actual.js new file mode 100644 index 0000000000..df7243be62 --- /dev/null +++ b/test/fixtures/experimental/object-rest-spread/9/actual.js @@ -0,0 +1 @@ +let { x, ...y, ...z } = obj; diff --git a/test/fixtures/experimental/object-rest-spread/9/options.json b/test/fixtures/experimental/object-rest-spread/9/options.json new file mode 100644 index 0000000000..03ba1e37e8 --- /dev/null +++ b/test/fixtures/experimental/object-rest-spread/9/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Cannot have multiple rest elements when destructuring (1:13)" +} diff --git a/test/fixtures/experimental/uncategorised/10/options.json b/test/fixtures/experimental/object-rest-spread/options.json similarity index 100% rename from test/fixtures/experimental/uncategorised/10/options.json rename to test/fixtures/experimental/object-rest-spread/options.json diff --git a/test/fixtures/experimental/uncategorised/11/options.json b/test/fixtures/experimental/uncategorised/11/options.json deleted file mode 100644 index 4de042a697..0000000000 --- a/test/fixtures/experimental/uncategorised/11/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "plugins": ["objectRestSpread"] -} diff --git a/test/fixtures/experimental/uncategorised/12/options.json b/test/fixtures/experimental/uncategorised/12/options.json deleted file mode 100644 index 4de042a697..0000000000 --- a/test/fixtures/experimental/uncategorised/12/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "plugins": ["objectRestSpread"] -} diff --git a/test/fixtures/experimental/uncategorised/13/options.json b/test/fixtures/experimental/uncategorised/13/options.json deleted file mode 100644 index 4de042a697..0000000000 --- a/test/fixtures/experimental/uncategorised/13/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "plugins": ["objectRestSpread"] -} diff --git a/test/fixtures/experimental/uncategorised/14/actual.js b/test/fixtures/experimental/uncategorised/14/actual.js deleted file mode 100644 index 3a17541c45..0000000000 --- a/test/fixtures/experimental/uncategorised/14/actual.js +++ /dev/null @@ -1 +0,0 @@ -({x, ...y, a, ...b, c}) \ No newline at end of file diff --git a/test/fixtures/experimental/uncategorised/14/options.json b/test/fixtures/experimental/uncategorised/14/options.json deleted file mode 100644 index 4de042a697..0000000000 --- a/test/fixtures/experimental/uncategorised/14/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "plugins": ["objectRestSpread"] -} diff --git a/test/fixtures/experimental/uncategorised/9/options.json b/test/fixtures/experimental/uncategorised/9/options.json deleted file mode 100644 index 4de042a697..0000000000 --- a/test/fixtures/experimental/uncategorised/9/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "plugins": ["objectRestSpread"] -}