diff --git a/CHANGELOG.md b/CHANGELOG.md index 5360d62ec1..2bbbb154f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ See [CHANGELOG - 6to5](CHANGELOG-6to5.md) for the pre-4.0.0 version changelog. * Ensure that invalid identifier JSX attribute keys are quoted when transforming to calls. * Fix ES3 property literal plugin. * Fix parameters after defaults in arrow functions refering to the wrong `arguments`. + * Fix parser bug where arrow functions have a higher precedence than they should. ## 6.0.13 diff --git a/packages/babylon/src/parser/expression.js b/packages/babylon/src/parser/expression.js index b27bf3abd2..677e78a1ed 100644 --- a/packages/babylon/src/parser/expression.js +++ b/packages/babylon/src/parser/expression.js @@ -221,12 +221,18 @@ pp.parseMaybeUnary = function (refShorthandDefaultPos) { pp.parseExprSubscripts = function (refShorthandDefaultPos) { let startPos = this.state.start, startLoc = this.state.startLoc; + let potentialArrowAt = this.state.potentialArrowAt; let expr = this.parseExprAtom(refShorthandDefaultPos); + + if (expr.type === "ArrowFunctionExpression" && expr.start === potentialArrowAt) { + return expr; + } + if (refShorthandDefaultPos && refShorthandDefaultPos.start) { return expr; - } else { - return this.parseSubscripts(expr, startPos, startLoc); } + + return this.parseSubscripts(expr, startPos, startLoc); }; pp.parseSubscripts = function (base, startPos, startLoc, noCalls) { @@ -250,7 +256,7 @@ pp.parseSubscripts = function (base, startPos, startLoc, noCalls) { this.expect(tt.bracketR); base = this.finishNode(node, "MemberExpression"); } else if (!noCalls && this.match(tt.parenL)) { - let possibleAsync = base.type === "Identifier" && base.name === "async" && !this.canInsertSemicolon(); + let possibleAsync = this.state.potentialArrowAt === base.start && base.type === "Identifier" && base.name === "async" && !this.canInsertSemicolon(); this.next(); let node = this.startNodeAt(startPos, startLoc); @@ -677,7 +683,7 @@ pp.parseObj = function (isPattern, refShorthandDefaultPos) { if (prop.shorthand) { this.addExtra(prop, "shorthand", true); } - + node.properties.push(prop); } @@ -915,6 +921,9 @@ pp.parseIdentifier = function (liberal) { // Parses await expression inside async function. pp.parseAwait = function (node) { + if (!this.state.inAsync) { + this.unexpected(); + } if (this.isLineTerminator()) { this.unexpected(); } diff --git a/packages/babylon/src/parser/statement.js b/packages/babylon/src/parser/statement.js index ef7649dc4b..710850b555 100644 --- a/packages/babylon/src/parser/statement.js +++ b/packages/babylon/src/parser/statement.js @@ -128,7 +128,8 @@ pp.parseStatement = function (declaration, topLevel) { // simply start parsing an expression, and afterwards, if the // next token is a colon and the expression was a simple // Identifier node, we switch to interpreting it as a label. - let maybeName = this.state.value, expr = this.parseExpression(); + let maybeName = this.state.value; + let expr = this.parseExpression(); if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon)) { return this.parseLabeledStatement(node, maybeName, expr); diff --git a/packages/babylon/src/plugins/flow.js b/packages/babylon/src/plugins/flow.js index 25e0ced340..994d744dfb 100644 --- a/packages/babylon/src/plugins/flow.js +++ b/packages/babylon/src/plugins/flow.js @@ -653,6 +653,7 @@ export default function (instance) { instance.extend("parseParenItem", function () { return function (node, startLoc, startPos, forceArrow?) { + let canBeArrow = this.state.potentialArrowAt = startPos; if (this.match(tt.colon)) { let typeCastNode = this.startNodeAt(startLoc, startPos); typeCastNode.expression = node; @@ -662,7 +663,7 @@ export default function (instance) { this.unexpected(); } - if (this.eat(tt.arrow)) { + if (canBeArrow && this.eat(tt.arrow)) { // ((lol): number => {}); let func = this.parseArrowExpression(this.startNodeAt(startLoc, startPos), [node]); func.returnType = typeCastNode.typeAnnotation; @@ -952,7 +953,7 @@ export default function (instance) { startPos = startPos || this.state.start; startLoc = startLoc || this.state.startLoc; - if (this.lookahead().type === tt.parenR) { + if (canBeArrow && this.lookahead().type === tt.parenR) { // let foo = (): number => {}; this.expect(tt.parenL); this.expect(tt.parenR); diff --git a/packages/babylon/test/fixtures/experimental/async-functions/expression-statement/actual.js b/packages/babylon/test/fixtures/experimental/async-functions/expression-statement/actual.js new file mode 100644 index 0000000000..2847ccae29 --- /dev/null +++ b/packages/babylon/test/fixtures/experimental/async-functions/expression-statement/actual.js @@ -0,0 +1 @@ +async () => {} diff --git a/packages/babylon/test/fixtures/experimental/async-functions/expression-statement/expected.json b/packages/babylon/test/fixtures/experimental/async-functions/expression-statement/expected.json new file mode 100644 index 0000000000..b2dfa5cf58 --- /dev/null +++ b/packages/babylon/test/fixtures/experimental/async-functions/expression-statement/expected.json @@ -0,0 +1,86 @@ +{ + "type": "File", + "start": 0, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 14 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 14 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 14 + } + }, + "expression": { + "type": "ArrowFunctionExpression", + "start": 0, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 14 + } + }, + "id": null, + "generator": false, + "expression": false, + "async": true, + "params": [], + "body": { + "type": "BlockStatement", + "start": 12, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 12 + }, + "end": { + "line": 1, + "column": 14 + } + }, + "body": [], + "directives": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babylon/test/fixtures/experimental/async-functions/no-callee/actual.js b/packages/babylon/test/fixtures/experimental/async-functions/no-callee/actual.js new file mode 100644 index 0000000000..968eff9fb2 --- /dev/null +++ b/packages/babylon/test/fixtures/experimental/async-functions/no-callee/actual.js @@ -0,0 +1 @@ +async () => {}() diff --git a/packages/babylon/test/fixtures/experimental/async-functions/no-callee/options.json b/packages/babylon/test/fixtures/experimental/async-functions/no-callee/options.json new file mode 100644 index 0000000000..d50e8469b8 --- /dev/null +++ b/packages/babylon/test/fixtures/experimental/async-functions/no-callee/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Unexpected token (1:14)" +} diff --git a/packages/babylon/test/fixtures/flow/regression/issue-2154/actual.js b/packages/babylon/test/fixtures/flow/regression/issue-2154/actual.js new file mode 100644 index 0000000000..a71a0f34e2 --- /dev/null +++ b/packages/babylon/test/fixtures/flow/regression/issue-2154/actual.js @@ -0,0 +1 @@ +!()=>0 diff --git a/packages/babylon/test/fixtures/flow/regression/issue-2154/options.json b/packages/babylon/test/fixtures/flow/regression/issue-2154/options.json new file mode 100644 index 0000000000..10bd5624f0 --- /dev/null +++ b/packages/babylon/test/fixtures/flow/regression/issue-2154/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Unexpected token (1:2)" +} diff --git a/packages/babylon/test/fixtures/flow/type-annotations/79/actual.js b/packages/babylon/test/fixtures/flow/type-annotations/79/actual.js index 72d2f265fd..f77d0b5e7f 100644 --- a/packages/babylon/test/fixtures/flow/type-annotations/79/actual.js +++ b/packages/babylon/test/fixtures/flow/type-annotations/79/actual.js @@ -1 +1 @@ -var foo = bar ? (foo) : number => {} : baz; \ No newline at end of file +var foo = bar ? (foo) : number => {} : baz; diff --git a/packages/babylon/test/fixtures/harmony/arrow-functions/no-callee/actual.js b/packages/babylon/test/fixtures/harmony/arrow-functions/no-callee/actual.js new file mode 100644 index 0000000000..7737f0162c --- /dev/null +++ b/packages/babylon/test/fixtures/harmony/arrow-functions/no-callee/actual.js @@ -0,0 +1 @@ +() => {}() diff --git a/packages/babylon/test/fixtures/harmony/arrow-functions/no-callee/options.json b/packages/babylon/test/fixtures/harmony/arrow-functions/no-callee/options.json new file mode 100644 index 0000000000..9ce9658f7d --- /dev/null +++ b/packages/babylon/test/fixtures/harmony/arrow-functions/no-callee/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Unexpected token (1:8)" +}