diff --git a/acorn/src/lval.js b/acorn/src/lval.js index 0449815f9..0825a6e68 100644 --- a/acorn/src/lval.js +++ b/acorn/src/lval.js @@ -75,8 +75,8 @@ pp.toAssignable = function(node, isBinding, refDestructuringErrors) { case "MemberExpression": if (isBinding) this.raise(node.start, "Assigning to rvalue") - if (this.options.ecmaVersion >= 11) { - this.checkOptionalChainingInLVal(node) + if (this.options.ecmaVersion >= 11 && (node.optional || node.shortCircuited)) { + this.raise(node.start, "Optional chaining cannot appear in left-hand side") } break @@ -87,25 +87,6 @@ pp.toAssignable = function(node, isBinding, refDestructuringErrors) { return node } -pp.checkOptionalChainingInLVal = function(node) { - while (node) { - switch (node.type) { - case "CallExpression": - if (node.optional) this.raise(node.start, "Optional chaining cannot appear in left-hand side") - node = node.callee - break - - case "MemberExpression": - if (node.optional) this.raise(node.start, "Optional chaining cannot appear in left-hand side") - node = node.object - break - - default: - return - } - } -} - // Convert list of expression atoms to binding list. pp.toAssignableList = function(exprList, isBinding) { @@ -226,7 +207,9 @@ pp.checkLVal = function(expr, bindingType = BIND_NONE, checkClashes) { case "MemberExpression": if (bindingType) this.raiseRecoverable(expr.start, "Binding member expression") - if (this.options.ecmaVersion >= 11) this.checkOptionalChainingInLVal(expr) + if (this.options.ecmaVersion >= 11 && (expr.optional || expr.shortCircuited)) { + this.raise(expr.start, "Optional chaining cannot appear in left-hand side") + } break case "ObjectPattern": diff --git a/test/tests-optional-chaining.js b/test/tests-optional-chaining.js index c787230cc..cb1f6cca4 100644 --- a/test/tests-optional-chaining.js +++ b/test/tests-optional-chaining.js @@ -888,6 +888,179 @@ test("(obj?.foo)`template`", { ], "sourceType": "script" }, { ecmaVersion: 11 }) +test("(obj?.foo).bar = 0", { + "type": "Program", + "start": 0, + "end": 18, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 18, + "expression": { + "type": "AssignmentExpression", + "start": 0, + "end": 18, + "operator": "=", + "left": { + "type": "MemberExpression", + "start": 0, + "end": 14, + "object": { + "type": "MemberExpression", + "start": 1, + "end": 9, + "object": { + "type": "Identifier", + "start": 1, + "end": 4, + "name": "obj" + }, + "property": { + "type": "Identifier", + "start": 6, + "end": 9, + "name": "foo" + }, + "computed": false, + "optional": true, + "shortCircuited": false + }, + "property": { + "type": "Identifier", + "start": 11, + "end": 14, + "name": "bar" + }, + "computed": false, + "optional": false, + "shortCircuited": false + }, + "right": { + "type": "Literal", + "start": 17, + "end": 18, + "value": 0, + "raw": "0" + } + } + } + ], + "sourceType": "script" +}, { ecmaVersion: 11 }) +test("(obj?.foo).bar++", { + "type": "Program", + "start": 0, + "end": 16, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 16, + "expression": { + "type": "UpdateExpression", + "start": 0, + "end": 16, + "operator": "++", + "prefix": false, + "argument": { + "type": "MemberExpression", + "start": 0, + "end": 14, + "object": { + "type": "MemberExpression", + "start": 1, + "end": 9, + "object": { + "type": "Identifier", + "start": 1, + "end": 4, + "name": "obj" + }, + "property": { + "type": "Identifier", + "start": 6, + "end": 9, + "name": "foo" + }, + "computed": false, + "optional": true, + "shortCircuited": false + }, + "property": { + "type": "Identifier", + "start": 11, + "end": 14, + "name": "bar" + }, + "computed": false, + "optional": false, + "shortCircuited": false + } + } + } + ], + "sourceType": "script" +}, { ecmaVersion: 11 }) +test("for ((obj?.foo).bar of []);", { + "type": "Program", + "start": 0, + "end": 27, + "body": [ + { + "type": "ForOfStatement", + "start": 0, + "end": 27, + "await": false, + "left": { + "type": "MemberExpression", + "start": 5, + "end": 19, + "object": { + "type": "MemberExpression", + "start": 6, + "end": 14, + "object": { + "type": "Identifier", + "start": 6, + "end": 9, + "name": "obj" + }, + "property": { + "type": "Identifier", + "start": 11, + "end": 14, + "name": "foo" + }, + "computed": false, + "optional": true, + "shortCircuited": false + }, + "property": { + "type": "Identifier", + "start": 16, + "end": 19, + "name": "bar" + }, + "computed": false, + "optional": false, + "shortCircuited": false + }, + "right": { + "type": "ArrayExpression", + "start": 23, + "end": 25, + "elements": [] + }, + "body": { + "type": "EmptyStatement", + "start": 26, + "end": 27 + } + } + ], + "sourceType": "script" +}, { ecmaVersion: 11 }) testFail("obj?.foo", "Unexpected token (1:4)", { ecmaVersion: 10 }) testFail("obj?.[foo]", "Unexpected token (1:4)", { ecmaVersion: 10 })