diff --git a/plugins/flow.js b/plugins/flow.js index bb54b9c44a..2021a7a4d9 100644 --- a/plugins/flow.js +++ b/plugins/flow.js @@ -710,27 +710,29 @@ acorn.plugins.flow = function (instance) { } }) + instance.extend("parseClassProperty", function (inner) { + return function (node) { + if (this.type === tt.colon) { + node.typeAnnotation = this.flow_parseTypeAnnotation() + } + return inner.call(this, node) + } + }) + instance.extend("isClassProperty", function (inner) { + return function () { + return this.type === tt.colon || inner.call(this) + } + }) + instance.extend("parseClassMethod", function (inner) { return function (classBody, method, isGenerator, isAsync) { - var classProperty = false - - if (this.type === tt.colon) { - method.typeAnnotation = this.flow_parseTypeAnnotation() - classProperty = true - } - - if (classProperty) { - this.semicolon() - classBody.body.push(this.finishNode(method, "ClassProperty")) - } else { - var typeParameters - if (this.isRelational("<")) { - typeParameters = this.flow_parseTypeParameterDeclaration() - } - method.value = this.parseMethod(isGenerator, isAsync) - method.value.typeParameters = typeParameters - classBody.body.push(this.finishNode(method, "MethodDefinition")) + var typeParameters + if (this.isRelational("<")) { + typeParameters = this.flow_parseTypeParameterDeclaration() } + method.value = this.parseMethod(isGenerator, isAsync) + method.value.typeParameters = typeParameters + classBody.body.push(this.finishNode(method, "MethodDefinition")) } }) diff --git a/src/expression.js b/src/expression.js index a0dbb57831..871403e355 100755 --- a/src/expression.js +++ b/src/expression.js @@ -418,6 +418,7 @@ pp.parseParenAndDistinguishExpression = function(start, isAsync) { par.expression = val return this.finishNode(par, "ParenthesizedExpression") } else { + val.parenthesizedExpression = true return val } } diff --git a/src/lval.js b/src/lval.js index b7c4f2707d..3f5eff3532 100755 --- a/src/lval.js +++ b/src/lval.js @@ -9,6 +9,14 @@ const pp = Parser.prototype // if possible. pp.toAssignable = function(node, isBinding) { + if (node.parenthesizedExpression) { + if (node.type === "ObjectExpression") { + this.raise(node.start, "You're trying to assign to a parenthesized expression, instead of `({ foo }) = {}` use `({ foo } = {})`"); + } else { + this.raise(node.start, "Parenthesized left hand expressions are illegal"); + } + } + if (this.options.ecmaVersion >= 6 && node) { switch (node.type) { case "Identifier": diff --git a/src/statement.js b/src/statement.js index 0e77cdc308..e6e8cee49f 100755 --- a/src/statement.js +++ b/src/statement.js @@ -150,10 +150,17 @@ pp.parseDebuggerStatement = function(node) { } pp.parseDoStatement = function(node) { + let start = this.markPosition() this.next() this.labels.push(loopLabel) node.body = this.parseStatement(false) this.labels.pop() + if (this.options.features["es7.doExpressions"] && this.type !== tt._while) { + let container = this.startNodeAt(start) + container.expression = this.finishNode(node, "DoExpression") + this.semicolon() + return this.finishNode(container, "ExpressionStatement") + } this.expect(tt._while) node.test = this.parseParenExpression() if (this.options.ecmaVersion >= 6) @@ -455,13 +462,17 @@ pp.parseClass = function(node, isStatement) { while (!this.eat(tt.braceR)) { if (this.eat(tt.semi)) continue if (this.options.features["es7.decorators"] && this.type === tt.at) { - decorators.push(this.parseDecorator()); + decorators.push(this.parseDecorator()) continue; } var method = this.startNode() + if (this.options.features["es7.decorators"] && 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" && + if (this.options.features["es7.classProperties"] && this.type !== tt.parenL && !method.computed && method.key.type === "Identifier" && method.key.name === "static") { if (isGenerator) this.unexpected() method['static'] = true @@ -470,6 +481,10 @@ pp.parseClass = function(node, isStatement) { } else { method['static'] = false } + if (!isGenerator && method.key.type === "Identifier" && !method.computed && this.isClassProperty()) { + classBody.body.push(this.parseClassProperty(method)) + continue + } if (this.options.features["es7.asyncFunctions"] && this.type !== tt.parenL && !method.computed && method.key.type === "Identifier" && method.key.name === "async") { isAsync = true @@ -488,9 +503,8 @@ pp.parseClass = function(node, isStatement) { method.kind = "constructor" } } - if (this.options.features["es7.decorators"] && decorators.length) { - method.decorators = decorators - decorators = [] + if (method.kind === "constructor" && method.decorators) { + this.raise(method.start, "You can't attach decorators to a class constructor") } this.parseClassMethod(classBody, method, isGenerator, isAsync) } @@ -501,16 +515,32 @@ pp.parseClass = function(node, isStatement) { return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression") } -pp.parseClassMethod = function (classBody, method, isGenerator, isAsync) { +pp.isClassProperty = function() { + return this.type === tt.eq || (this.type === tt.semi || this.canInsertSemicolon()) +} + +pp.parseClassProperty = function(node) { + if (this.type === tt.eq) { + if (!this.options.features["es7.classProperties"]) this.unexpected() + this.next() + node.value = this.parseMaybeAssign(); + } else { + node.value = null; + } + this.semicolon() + return this.finishNode(node, "ClassProperty") +} + +pp.parseClassMethod = function(classBody, method, isGenerator, isAsync) { method.value = this.parseMethod(isGenerator, isAsync) classBody.body.push(this.finishNode(method, "MethodDefinition")) } -pp.parseClassId = function (node, isStatement) { +pp.parseClassId = function(node, isStatement) { node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null } -pp.parseClassSuper = function (node) { +pp.parseClassSuper = function(node) { node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null }