diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index bfbcc0fa163c..20f883246882 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -93,16 +93,9 @@ export default class LValParser extends NodeUtils { * @param {boolean} [isLHS=false] Whether we are parsing a LeftHandSideExpression. * If isLHS is `true`, the following cases are allowed: `[(a)] = [0]`, `[(a.b)] = [0]` * If isLHS is `false`, we are in an arrow function parameters list. - * @param {boolean} [isInObjectPattern=false] `true` if the parent is an object pattern or a rest element - * of an object pattern. It's necessary because ...'s argument follows - * different rules in array and object patterns. * @memberof LValParser */ - toAssignable( - node: Node, - isLHS: boolean = false, - isInObjectPattern: boolean = false, - ): void { + toAssignable(node: Node, isLHS: boolean = false): void { let parenthesized = undefined; if (node.type === "ParenthesizedExpression" || node.extra?.parenthesized) { parenthesized = unwrapParenthesizedExpression(node); @@ -171,11 +164,10 @@ export default class LValParser extends NodeUtils { } case "SpreadElement": { - node.type = "RestElement"; - const arg = node.argument; - this.checkToRestConversion(arg, /* allowPattern */ !isInObjectPattern); - this.toAssignable(arg, isLHS, isInObjectPattern); - break; + throw new Error( + "Internal @babel/parser error (this is a bug, please report it)." + + " SpreadElement should be converted by .toAssignable's caller.", + ); } case "ArrayExpression": @@ -220,10 +212,17 @@ export default class LValParser extends NodeUtils { : Errors.PatternHasMethod, { at: prop.key }, ); - } else if (prop.type === "SpreadElement" && !isLast) { - this.raise(Errors.RestTrailingComma, { at: prop }); + } else if (prop.type === "SpreadElement") { + prop.type = "RestElement"; + const arg = prop.argument; + this.checkToRestConversion(arg, /* allowPattern */ false); + this.toAssignable(arg, isLHS); + + if (!isLast) { + this.raise(Errors.RestTrailingComma, { at: prop }); + } } else { - this.toAssignable(prop, isLHS, /* isInObjectPattern */ true); + this.toAssignable(prop, isLHS); } } @@ -234,27 +233,26 @@ export default class LValParser extends NodeUtils { trailingCommaLoc?: ?Position, isLHS: boolean, ): void { - let end = exprList.length; - if (end) { - const last = exprList[end - 1]; - if (last?.type === "RestElement") { - --end; - } else if (last?.type === "SpreadElement") { - this.toAssignable(last, isLHS, /* isInObjectPattern */ false); - - if (trailingCommaLoc) { - this.raise(Errors.RestTrailingComma, { at: trailingCommaLoc }); - } + const end = exprList.length - 1; - --end; - } - } - for (let i = 0; i < end; i++) { + for (let i = 0; i <= end; i++) { const elt = exprList[i]; - if (elt) { - this.toAssignable(elt, isLHS, /* isInObjectPattern */ false); - if (elt.type === "RestElement") { + if (!elt) continue; + + if (elt.type === "SpreadElement") { + elt.type = "RestElement"; + const arg = elt.argument; + this.checkToRestConversion(arg, /* allowPattern */ true); + this.toAssignable(arg, isLHS); + } else { + this.toAssignable(elt, isLHS); + } + + if (elt.type === "RestElement") { + if (i < end) { this.raise(Errors.RestTrailingComma, { at: elt }); + } else if (trailingCommaLoc) { + this.raise(Errors.RestTrailingComma, { at: trailingCommaLoc }); } } } diff --git a/packages/babel-parser/src/plugins/estree.js b/packages/babel-parser/src/plugins/estree.js index 2d738576587b..b4863fcba0ec 100644 --- a/packages/babel-parser/src/plugins/estree.js +++ b/packages/babel-parser/src/plugins/estree.js @@ -354,11 +354,7 @@ export default (superClass: Class): Class => return super.isAssignable(node, isBinding); } - toAssignable( - node: N.Node, - isLHS: boolean = false, - isInObjectPattern?: boolean, - ): void { + toAssignable(node: N.Node, isLHS: boolean = false): void { if (node != null && this.isObjectProperty(node)) { const { key, value } = node; if (this.isPrivateName(key)) { @@ -367,9 +363,9 @@ export default (superClass: Class): Class => key.loc.start, ); } - this.toAssignable(value, isLHS, isInObjectPattern); + this.toAssignable(value, isLHS); } else { - super.toAssignable(node, isLHS, isInObjectPattern); + super.toAssignable(node, isLHS); } } diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index af62deedfc57..05525510791c 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3250,18 +3250,10 @@ export default (superClass: Class): Class => } } - toAssignable( - node: N.Node, - isLHS: boolean = false, - isInObjectPattern?: boolean, - ): void { + toAssignable(node: N.Node, isLHS: boolean = false): void { switch (node.type) { case "ParenthesizedExpression": - this.toAssignableParenthesizedExpression( - node, - isLHS, - isInObjectPattern, - ); + this.toAssignableParenthesizedExpression(node, isLHS); break; case "TSAsExpression": case "TSNonNullExpression": @@ -3274,7 +3266,7 @@ export default (superClass: Class): Class => } else { this.raise(TSErrors.UnexpectedTypeCastInParameter, { at: node }); } - this.toAssignable(node.expression, isLHS, isInObjectPattern); + this.toAssignable(node.expression, isLHS); break; case "AssignmentExpression": if (!isLHS && node.left.type === "TSTypeCastExpression") { @@ -3282,24 +3274,20 @@ export default (superClass: Class): Class => } /* fall through */ default: - super.toAssignable(node, isLHS, isInObjectPattern); + super.toAssignable(node, isLHS); } } - toAssignableParenthesizedExpression( - node: N.Node, - isLHS: boolean, - isInObjectPattern?: boolean, - ): void { + toAssignableParenthesizedExpression(node: N.Node, isLHS: boolean): void { switch (node.expression.type) { case "TSAsExpression": case "TSNonNullExpression": case "TSTypeAssertion": case "ParenthesizedExpression": - this.toAssignable(node.expression, isLHS, isInObjectPattern); + this.toAssignable(node.expression, isLHS); break; default: - super.toAssignable(node, isLHS, isInObjectPattern); + super.toAssignable(node, isLHS); } } diff --git a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/expression-rest-not-last-invalid/output.json b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/expression-rest-not-last-invalid/output.json index 6ab32e02056b..a988993ba48c 100644 --- a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/expression-rest-not-last-invalid/output.json +++ b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/expression-rest-not-last-invalid/output.json @@ -2,8 +2,7 @@ "type": "File", "start":0,"end":19,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":19,"index":19}}, "errors": [ - "SyntaxError: Unexpected trailing comma after rest element. (1:2)", - "SyntaxError: Invalid left-hand side in object destructuring pattern. (1:2)" + "SyntaxError: Unexpected trailing comma after rest element. (1:2)" ], "program": { "type": "Program", @@ -23,7 +22,7 @@ "start":1,"end":13,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":13,"index":13}}, "properties": [ { - "type": "SpreadElement", + "type": "RestElement", "start":2,"end":9,"loc":{"start":{"line":1,"column":2,"index":2},"end":{"line":1,"column":9,"index":9}}, "argument": { "type": "Identifier",