diff --git a/src/acorn/src/expression.js b/src/acorn/src/expression.js index b84117e756..8eaeab610f 100755 --- a/src/acorn/src/expression.js +++ b/src/acorn/src/expression.js @@ -509,22 +509,24 @@ pp.parseTemplate = function() { pp.parseObj = function(isPattern, refShorthandDefaultPos) { let node = this.startNode(), first = true, propHash = {} node.properties = [] + let decorators = [] this.next() while (!this.eat(tt.braceR)) { if (!first) { this.expect(tt.comma) if (this.afterTrailingComma(tt.braceR)) break } else first = false - while (this.type === tt.at) { - this.decorators.push(this.parseDecorator()) + decorators.push(this.parseDecorator()) } - let prop = this.startNode(), isGenerator = false, isAsync = false, start + if (decorators.length) { + prop.decorators = decorators + decorators = [] + } if (this.options.features["es7.objectRestSpread"] && this.type === tt.ellipsis) { prop = this.parseSpread() prop.type = "SpreadProperty" - this.takeDecorators(prop) node.properties.push(prop) continue } @@ -550,10 +552,9 @@ pp.parseObj = function(isPattern, refShorthandDefaultPos) { } this.parseObjPropValue(prop, start, isGenerator, isAsync, isPattern, refShorthandDefaultPos); this.checkPropClash(prop, propHash) - this.takeDecorators(prop) node.properties.push(this.finishNode(prop, "Property")) } - if (this.decorators.length) { + if (decorators.length) { this.raise(this.start, "You have trailing decorators with no property"); } return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression") diff --git a/src/acorn/src/statement.js b/src/acorn/src/statement.js index 9490e25e3c..cf351718f3 100755 --- a/src/acorn/src/statement.js +++ b/src/acorn/src/statement.js @@ -471,14 +471,18 @@ pp.parseClass = function(node, isStatement) { var classBody = this.startNode() classBody.body = [] this.expect(tt.braceL) + let decorators = [] while (!this.eat(tt.braceR)) { if (this.eat(tt.semi)) continue if (this.type === tt.at) { - this.decorators.push(this.parseDecorator()) + decorators.push(this.parseDecorator()) continue } var method = this.startNode() - this.takeDecorators(method) + if (decorators.length) { + method.decorators = decorators + decorators = [] + } var isGenerator = this.eat(tt.star), isAsync = false this.parsePropertyName(method) if (this.type !== tt.parenL && !method.computed && method.key.type === "Identifier" && @@ -517,7 +521,7 @@ pp.parseClass = function(node, isStatement) { } this.parseClassMethod(classBody, method, isGenerator, isAsync) } - if (this.decorators.length) { + if (decorators.length) { this.raise(this.start, "You have trailing decorators with no method"); } node.body = this.finishNode(classBody, "ClassBody") diff --git a/test/acorn/tests-babel.js b/test/acorn/tests-babel.js index 5ebfbb622e..8c889508c7 100644 --- a/test/acorn/tests-babel.js +++ b/test/acorn/tests-babel.js @@ -2461,6 +2461,166 @@ test("class Foo { @foo @bar bar() {} }", { features: { "es7.decorators": true } }); +test('@foo({ @bar foo: "bar" }) @bar class Foo {}', { + "start": 0, + "body": [{ + "start": 31, + "decorators": [{ + "start": 0, + "expression": { + "start": 1, + "callee": { + "start": 1, + "name": "foo", + "type": "Identifier", + "end": 4 + }, + "arguments": [{ + "start": 5, + "properties": [{ + "start": 12, + "decorators": [{ + "start": 7, + "expression": { + "start": 8, + "name": "bar", + "type": "Identifier", + "end": 11 + }, + "type": "Decorator", + "end": 11 + }], + "method": false, + "shorthand": false, + "computed": false, + "key": { + "start": 12, + "name": "foo", + "type": "Identifier", + "end": 15 + }, + "value": { + "start": 17, + "value": "bar", + "raw": "\"bar\"", + "type": "Literal", + "end": 22 + }, + "kind": "init", + "type": "Property", + "end": 22 + }], + "type": "ObjectExpression", + "end": 24 + }], + "type": "CallExpression", + "end": 25 + }, + "type": "Decorator", + "end": 25 + }, + { + "start": 26, + "expression": { + "start": 27, + "name": "bar", + "type": "Identifier", + "end": 30 + }, + "type": "Decorator", + "end": 30 + }], + "id": { + "start": 37, + "name": "Foo", + "type": "Identifier", + "end": 40 + }, + "superClass": null, + "body": { + "start": 41, + "body": [], + "type": "ClassBody", + "end": 43 + }, + "type": "ClassDeclaration", + "end": 43 + }], + "sourceType": "script", + "type": "Program", + "end": 43 +}, { + ecmaVersion: 6, + features: { "es7.decorators": true } +}); + +test('@bar class Foo extends @foo class Bar {} {}', { + "start": 0, + "body": [{ + "start": 5, + "decorators": [{ + "start": 0, + "expression": { + "start": 1, + "name": "bar", + "type": "Identifier", + "end": 4 + }, + "type": "Decorator", + "end": 4 + }], + "id": { + "start": 11, + "name": "Foo", + "type": "Identifier", + "end": 14 + }, + "superClass": { + "start": 28, + "decorators": [{ + "start": 23, + "expression": { + "start": 24, + "name": "foo", + "type": "Identifier", + "end": 27 + }, + "type": "Decorator", + "end": 27 + }], + "id": { + "start": 34, + "name": "Bar", + "type": "Identifier", + "end": 37 + }, + "superClass": null, + "body": { + "start": 38, + "body": [], + "type": "ClassBody", + "end": 40 + }, + "type": "ClassExpression", + "end": 40 + }, + "body": { + "start": 41, + "body": [], + "type": "ClassBody", + "end": 43 + }, + "type": "ClassDeclaration", + "end": 43 + }], + "sourceType": "script", + "type": "Program", + "end": 43 +}, { + ecmaVersion: 6, + features: { "es7.decorators": true } +}); + testFail("@foo function bar() {}", "Leading decorators must be attached to a class declaration (1:5)", { ecmaVersion: 6, features: { "es7.decorators": true }