diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 8aaa10d67c7d..57ba5d25a35e 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -241,15 +241,15 @@ export default class ExpressionParser extends LValParser { const startLoc = this.state.startLoc; if (this.isContextual("yield")) { if (this.prodParam.hasYield) { + // If we have [Yield] production, `yield` will start a YieldExpression thus + // regex is allowed following. Otherwise `yield` is an identifier and regex + // is disallowed in tt.name.updateContext + this.state.exprAllowed = true; let left = this.parseYield(); if (afterLeftParse) { left = afterLeftParse.call(this, left, startPos, startLoc); } return left; - } else { - // The tokenizer will assume an expression is allowed after - // `yield`, but this isn't that kind of yield - this.state.exprAllowed = false; } } @@ -1603,14 +1603,13 @@ export default class ExpressionParser extends LValParser { node.properties = []; this.next(); - while (!this.eat(close)) { + while (!this.match(close)) { if (first) { first = false; } else { this.expect(tt.comma); if (this.match(close)) { this.addExtra(node, "trailingComma", this.state.lastTokStart); - this.next(); break; } } @@ -1637,6 +1636,13 @@ export default class ExpressionParser extends LValParser { node.properties.push(prop); } + // The tokenizer uses `braceIsBlock` to detect whether `{` starts a block statement. + // If `{` is a block statement, `exprAllowed` will be `true`. + // However the tokenizer can not handle edge cases like `0 ? a : { a : 1 } / 2`, here + // we update `exprAllowed` when an object-like is parsed. + this.state.exprAllowed = false; + this.next(); + this.state.inFSharpPipelineDirectBody = oldInFSharpPipelineDirectBody; let type = "ObjectExpression"; if (isPattern) { diff --git a/packages/babel-parser/src/tokenizer/context.js b/packages/babel-parser/src/tokenizer/context.js index f93c19971efb..5581e6b05e2c 100644 --- a/packages/babel-parser/src/tokenizer/context.js +++ b/packages/babel-parser/src/tokenizer/context.js @@ -40,6 +40,14 @@ export const types: { }; // Token-specific context update code +// Note that we should avoid accessing `this.prodParam` in context update, +// because it is executed immediately when last token is consumed, which may be +// before `this.prodParam` is updated. e.g. +// ``` +// function *g() { () => yield / 2 } +// ``` +// When `=>` is eaten, the context update of `yield` is executed, however, +// `this.prodParam` still has `[Yield]` production because it is not yet updated tt.parenR.updateContext = tt.braceR.updateContext = function () { if (this.state.context.length === 1) { @@ -59,11 +67,10 @@ tt.name.updateContext = function (prevType) { let allowed = false; if (prevType !== tt.dot) { if ( - (this.state.value === "of" && - !this.state.exprAllowed && - prevType !== tt._function && - prevType !== tt._class) || - (this.state.value === "yield" && this.prodParam.hasYield) + this.state.value === "of" && + !this.state.exprAllowed && + prevType !== tt._function && + prevType !== tt._class ) { allowed = true; } diff --git a/packages/babel-parser/test/fixtures/core/categorized/labeled-block-statement-regex/input.js b/packages/babel-parser/test/fixtures/core/categorized/labeled-block-statement-regex/input.js new file mode 100644 index 000000000000..b69bf681e7d9 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/labeled-block-statement-regex/input.js @@ -0,0 +1 @@ +a : { b : 1 }/2/; diff --git a/packages/babel-parser/test/fixtures/core/categorized/labeled-block-statement-regex/output.json b/packages/babel-parser/test/fixtures/core/categorized/labeled-block-statement-regex/output.json new file mode 100644 index 000000000000..bfdc01603a79 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/labeled-block-statement-regex/output.json @@ -0,0 +1,64 @@ +{ + "type": "File", + "start":0,"end":17,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":17}}, + "program": { + "type": "Program", + "start":0,"end":17,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":17}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "LabeledStatement", + "start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}}, + "body": { + "type": "BlockStatement", + "start":4,"end":13,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":13}}, + "body": [ + { + "type": "LabeledStatement", + "start":6,"end":11,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":11}}, + "body": { + "type": "ExpressionStatement", + "start":10,"end":11,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":11}}, + "expression": { + "type": "NumericLiteral", + "start":10,"end":11,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":11}}, + "extra": { + "rawValue": 1, + "raw": "1" + }, + "value": 1 + } + }, + "label": { + "type": "Identifier", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7},"identifierName":"b"}, + "name": "b" + } + } + ], + "directives": [] + }, + "label": { + "type": "Identifier", + "start":0,"end":1,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":1},"identifierName":"a"}, + "name": "a" + } + }, + { + "type": "ExpressionStatement", + "start":13,"end":17,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":17}}, + "expression": { + "type": "RegExpLiteral", + "start":13,"end":16,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":16}}, + "extra": { + "raw": "/2/" + }, + "pattern": "2", + "flags": "" + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/core/categorized/ternary-object-literal-divide/input.js b/packages/babel-parser/test/fixtures/core/categorized/ternary-object-literal-divide/input.js new file mode 100644 index 000000000000..b424d657d3b4 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/ternary-object-literal-divide/input.js @@ -0,0 +1 @@ +0 ? a : { b : 1 }/2; diff --git a/packages/babel-parser/test/fixtures/core/categorized/ternary-object-literal-divide/output.json b/packages/babel-parser/test/fixtures/core/categorized/ternary-object-literal-divide/output.json new file mode 100644 index 000000000000..8007fd4ae432 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/ternary-object-literal-divide/output.json @@ -0,0 +1,76 @@ +{ + "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", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}}, + "expression": { + "type": "ConditionalExpression", + "start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}}, + "test": { + "type": "NumericLiteral", + "start":0,"end":1,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":1}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + }, + "consequent": { + "type": "Identifier", + "start":4,"end":5,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":5},"identifierName":"a"}, + "name": "a" + }, + "alternate": { + "type": "BinaryExpression", + "start":8,"end":19,"loc":{"start":{"line":1,"column":8},"end":{"line":1,"column":19}}, + "left": { + "type": "ObjectExpression", + "start":8,"end":17,"loc":{"start":{"line":1,"column":8},"end":{"line":1,"column":17}}, + "properties": [ + { + "type": "ObjectProperty", + "start":10,"end":15,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":15}}, + "method": false, + "key": { + "type": "Identifier", + "start":10,"end":11,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":11},"identifierName":"b"}, + "name": "b" + }, + "computed": false, + "shorthand": false, + "value": { + "type": "NumericLiteral", + "start":14,"end":15,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":15}}, + "extra": { + "rawValue": 1, + "raw": "1" + }, + "value": 1 + } + } + ] + }, + "operator": "/", + "right": { + "type": "NumericLiteral", + "start":18,"end":19,"loc":{"start":{"line":1,"column":18},"end":{"line":1,"column":19}}, + "extra": { + "rawValue": 2, + "raw": "2" + }, + "value": 2 + } + } + } + } + ], + "directives": [] + } +} \ No newline at end of file