diff --git a/packages/babel-parser/src/util/expression-scope.js b/packages/babel-parser/src/util/expression-scope.js index 374a0a974501..815506691203 100644 --- a/packages/babel-parser/src/util/expression-scope.js +++ b/packages/babel-parser/src/util/expression-scope.js @@ -140,15 +140,30 @@ export default class ExpressionScopeHandler { * For example, in `([(a) = []] = []) => {}`, we think `(a) = []` is an LHS in `[(a) = []]`, * an LHS within `[(a) = []] = []`. However the LHS chain is then transformed by toAssignable, * and we should throw assignment `(a)`, which is only valid in LHS. Hence we record the - * location of parenthesized `(a)` to any ancestry MaybeArrowParameterDeclaration - * and MaybeAsyncArrowParameterDeclaration scope until an Expression scope is seen + * location of parenthesized `(a)` to current scope if it is one of MaybeArrowParameterDeclaration + * and MaybeAsyncArrowParameterDeclaration + * + * Unlike `recordParameterInitializerError`, we don't record to ancestry scope because we + * validate arrow head parsing scope before exit, and then the LHS will be unambiguous: + * For example, in `( x = ( [(a) = []] = [] ) ) => {}`, we should not record `(a)` in `( x = ... ) =>` + * arrow scope because when we finish parsing `( [(a) = []] = [] )`, it is an umbiguous assignment + * expression and can not be cast to pattern * @param {number} pos * @param {string} message * @returns {void} * @memberof ExpressionScopeHandler */ recordParenthesizedIdentifierError(pos: number, message: string): void { - return this.recordParameterInitializerError(pos, message); + const { stack } = this; + const scope: ExpressionScope = stack[stack.length - 1]; + if (scope.isCertainlyParameterDeclaration()) { + this.raise(pos, message); + } else if (scope.canBeArrowParameterDeclaration()) { + /*:: invariant(scope instanceof ArrowHeadParsingScope) */ + scope.recordDeclarationError(pos, message); + } else { + return; + } } /** diff --git a/packages/babel-parser/test/fixtures/es2015/arrow-functions/parenthesized-lhs-in-parethesis-in-param/input.js b/packages/babel-parser/test/fixtures/es2015/arrow-functions/parenthesized-lhs-in-parethesis-in-param/input.js new file mode 100644 index 000000000000..7892d319adb0 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/arrow-functions/parenthesized-lhs-in-parethesis-in-param/input.js @@ -0,0 +1 @@ +(x = ([(f) = []] = [])) => {}; diff --git a/packages/babel-parser/test/fixtures/es2015/arrow-functions/parenthesized-lhs-in-parethesis-in-param/output.json b/packages/babel-parser/test/fixtures/es2015/arrow-functions/parenthesized-lhs-in-parethesis-in-param/output.json new file mode 100644 index 000000000000..6626adc706ca --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/arrow-functions/parenthesized-lhs-in-parethesis-in-param/output.json @@ -0,0 +1,79 @@ +{ + "type": "File", + "start":0,"end":30,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":30}}, + "program": { + "type": "Program", + "start":0,"end":30,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":30}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":30,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":30}}, + "expression": { + "type": "ArrowFunctionExpression", + "start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":29}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "AssignmentPattern", + "start":1,"end":22,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":22}}, + "left": { + "type": "Identifier", + "start":1,"end":2,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":2},"identifierName":"x"}, + "name": "x" + }, + "right": { + "type": "AssignmentExpression", + "start":6,"end":21,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":21}}, + "extra": { + "parenthesized": true, + "parenStart": 5 + }, + "operator": "=", + "left": { + "type": "ArrayPattern", + "start":6,"end":16,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":16}}, + "elements": [ + { + "type": "AssignmentPattern", + "start":7,"end":15,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":15}}, + "left": { + "type": "Identifier", + "start":8,"end":9,"loc":{"start":{"line":1,"column":8},"end":{"line":1,"column":9},"identifierName":"f"}, + "extra": { + "parenthesized": true, + "parenStart": 7 + }, + "name": "f" + }, + "right": { + "type": "ArrayExpression", + "start":13,"end":15,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":15}}, + "elements": [] + } + } + ] + }, + "right": { + "type": "ArrayExpression", + "start":19,"end":21,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":21}}, + "elements": [] + } + } + } + ], + "body": { + "type": "BlockStatement", + "start":27,"end":29,"loc":{"start":{"line":1,"column":27},"end":{"line":1,"column":29}}, + "body": [], + "directives": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2017/async-arrow/parenthesized-lhs-in-async-call-in-param/input.js b/packages/babel-parser/test/fixtures/es2017/async-arrow/parenthesized-lhs-in-async-call-in-param/input.js new file mode 100644 index 000000000000..da15aa9e528f --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2017/async-arrow/parenthesized-lhs-in-async-call-in-param/input.js @@ -0,0 +1 @@ +async (x = async([(f) = []])) => {}; diff --git a/packages/babel-parser/test/fixtures/es2017/async-arrow/parenthesized-lhs-in-async-call-in-param/output.json b/packages/babel-parser/test/fixtures/es2017/async-arrow/parenthesized-lhs-in-async-call-in-param/output.json new file mode 100644 index 000000000000..acf951ea057a --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2017/async-arrow/parenthesized-lhs-in-async-call-in-param/output.json @@ -0,0 +1,77 @@ +{ + "type": "File", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "program": { + "type": "Program", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "expression": { + "type": "ArrowFunctionExpression", + "start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}}, + "id": null, + "generator": false, + "async": true, + "params": [ + { + "type": "AssignmentPattern", + "start":7,"end":28,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":28}}, + "left": { + "type": "Identifier", + "start":7,"end":8,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":8},"identifierName":"x"}, + "name": "x" + }, + "right": { + "type": "CallExpression", + "start":11,"end":28,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":28}}, + "callee": { + "type": "Identifier", + "start":11,"end":16,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":16},"identifierName":"async"}, + "name": "async" + }, + "arguments": [ + { + "type": "ArrayExpression", + "start":17,"end":27,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":27}}, + "elements": [ + { + "type": "AssignmentExpression", + "start":18,"end":26,"loc":{"start":{"line":1,"column":18},"end":{"line":1,"column":26}}, + "operator": "=", + "left": { + "type": "Identifier", + "start":19,"end":20,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":20},"identifierName":"f"}, + "extra": { + "parenthesized": true, + "parenStart": 18 + }, + "name": "f" + }, + "right": { + "type": "ArrayExpression", + "start":24,"end":26,"loc":{"start":{"line":1,"column":24},"end":{"line":1,"column":26}}, + "elements": [] + } + } + ] + } + ] + } + } + ], + "body": { + "type": "BlockStatement", + "start":33,"end":35,"loc":{"start":{"line":1,"column":33},"end":{"line":1,"column":35}}, + "body": [], + "directives": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file