Skip to content

Commit

Permalink
Add parser support for optional chain in assignments
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Jul 5, 2023
1 parent e5c78d1 commit 5933ab1
Show file tree
Hide file tree
Showing 43 changed files with 779 additions and 6 deletions.
1 change: 1 addition & 0 deletions packages/babel-parser/data/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@
"objectRestSpread",
"optionalCatchBinding",
"optionalChaining",
"optionalChainingAssign",
"partialApplication",
"pipelineOperator",
"placeholders",
Expand Down
4 changes: 4 additions & 0 deletions packages/babel-parser/src/parse-error/standard-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ export default {
`Invalid left-hand side in ${toNodeDescription(ancestor)}.`,
InvalidLhsBinding: ({ ancestor }: { ancestor: LValAncestor }) =>
`Binding invalid left-hand side in ${toNodeDescription(ancestor)}.`,
InvalidLhsOptionalChaining: ({ ancestor }: { ancestor: LValAncestor }) =>
`Invalid optional chaining in the left-hand side of ${toNodeDescription(
ancestor,
)}.`,
InvalidNumber: "Invalid number.",
InvalidOrMissingExponent:
"Floating-point numbers require a valid exponent after the 'e'.",
Expand Down
28 changes: 23 additions & 5 deletions packages/babel-parser/src/parser/lval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ export default abstract class LValParser extends NodeUtils {
Errors.InvalidParenthesizedAssignment,
{ at: node },
);
} else if (parenthesized.type !== "MemberExpression") {
} else if (
parenthesized.type !== "MemberExpression" &&
!this.isOptionalMemberExpression(parenthesized)
) {
// A parenthesized member expression can be in LHS but not in pattern.
// If the LHS is later interpreted as a pattern, `checkLVal` will throw for member expression binding
// i.e. `([(a.b) = []] = []) => {}`
Expand Down Expand Up @@ -556,6 +559,11 @@ export default abstract class LValParser extends NodeUtils {
);
}

// Overridden by the estree plugin
isOptionalMemberExpression(expression: Node) {
return expression.type === "OptionalMemberExpression";
}

/**
* Verify that a target expression is an lval (something that can be assigned to).
*
Expand Down Expand Up @@ -604,7 +612,19 @@ export default abstract class LValParser extends NodeUtils {
// toAssignable already reported this error with a nicer message.
if (this.isObjectMethod(expression)) return;

if (type === "MemberExpression") {
const isOptionalMemberExpression =
this.isOptionalMemberExpression(expression);

if (isOptionalMemberExpression || type === "MemberExpression") {
if (isOptionalMemberExpression) {
this.expectPlugin("optionalChainingAssign", expression.loc.start);
if (ancestor.type !== "AssignmentExpression") {
this.raise(Errors.InvalidLhsOptionalChaining, {
at: expression,
ancestor,
});
}
}
if (binding !== BIND_NONE) {
this.raise(Errors.InvalidPropertyBindingPattern, { at: expression });
}
Expand Down Expand Up @@ -651,9 +671,7 @@ export default abstract class LValParser extends NodeUtils {
? validity
: [validity, type === "ParenthesizedExpression"];
const nextAncestor =
type === "ArrayPattern" ||
type === "ObjectPattern" ||
type === "ParenthesizedExpression"
type === "ArrayPattern" || type === "ObjectPattern"
? ({ type } as const)
: ancestor;

Expand Down
7 changes: 7 additions & 0 deletions packages/babel-parser/src/plugins/estree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,13 @@ export default (superClass: typeof Parser) =>
return node;
}

isOptionalMemberExpression(node: N.Node) {
if (node.type === "ChainExpression") {
return node.expression.type === "MemberExpression";
}
return super.isOptionalMemberExpression(node);
}

hasPropertyAsPrivateName(node: N.Node): boolean {
if (node.type === "ChainExpression") {
node = node.expression;
Expand Down
1 change: 1 addition & 0 deletions packages/babel-parser/src/typings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export type Plugin =
| "objectRestSpread"
| "optionalCatchBinding"
| "optionalChaining"
| "optionalChainingAssign"
| "partialApplication"
| "placeholders"
| "privateIn" // Enabled by default
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":9,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":9,"index":9}},
"errors": [
"SyntaxError: Invalid left-hand side in parenthesized expression. (1:1)"
"SyntaxError: Invalid left-hand side in assignment expression. (1:1)"
],
"program": {
"type": "Program",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a?.b = c;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: \"optionalChainingAssign\". (1:0)"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[a?.b] = [];
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"type": "File",
"start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}},
"errors": [
"SyntaxError: Invalid optional chaining in the left-hand side of array destructuring pattern. (1:1)"
],
"program": {
"type": "Program",
"start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}},
"expression": {
"type": "AssignmentExpression",
"start":0,"end":11,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":11,"index":11}},
"operator": "=",
"left": {
"type": "ArrayPattern",
"start":0,"end":6,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":6,"index":6}},
"elements": [
{
"type": "OptionalMemberExpression",
"start":1,"end":5,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":5,"index":5}},
"object": {
"type": "Identifier",
"start":1,"end":2,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":2,"index":2},"identifierName":"a"},
"name": "a"
},
"computed": false,
"property": {
"type": "Identifier",
"start":4,"end":5,"loc":{"start":{"line":1,"column":4,"index":4},"end":{"line":1,"column":5,"index":5},"identifierName":"b"},
"name": "b"
},
"optional": true
}
]
},
"right": {
"type": "ArrayExpression",
"start":9,"end":11,"loc":{"start":{"line":1,"column":9,"index":9},"end":{"line":1,"column":11,"index":11}},
"elements": []
}
}
}
],
"directives": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
({ prop: a?.b } = {});
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"type": "File",
"start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":22,"index":22}},
"errors": [
"SyntaxError: Invalid optional chaining in the left-hand side of object destructuring pattern. (1:9)"
],
"program": {
"type": "Program",
"start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":22,"index":22}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":22,"index":22}},
"expression": {
"type": "AssignmentExpression",
"start":1,"end":20,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":20,"index":20}},
"operator": "=",
"left": {
"type": "ObjectPattern",
"start":1,"end":15,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":15,"index":15}},
"properties": [
{
"type": "ObjectProperty",
"start":3,"end":13,"loc":{"start":{"line":1,"column":3,"index":3},"end":{"line":1,"column":13,"index":13}},
"method": false,
"key": {
"type": "Identifier",
"start":3,"end":7,"loc":{"start":{"line":1,"column":3,"index":3},"end":{"line":1,"column":7,"index":7},"identifierName":"prop"},
"name": "prop"
},
"computed": false,
"shorthand": false,
"value": {
"type": "OptionalMemberExpression",
"start":9,"end":13,"loc":{"start":{"line":1,"column":9,"index":9},"end":{"line":1,"column":13,"index":13}},
"object": {
"type": "Identifier",
"start":9,"end":10,"loc":{"start":{"line":1,"column":9,"index":9},"end":{"line":1,"column":10,"index":10},"identifierName":"a"},
"name": "a"
},
"computed": false,
"property": {
"type": "Identifier",
"start":12,"end":13,"loc":{"start":{"line":1,"column":12,"index":12},"end":{"line":1,"column":13,"index":13},"identifierName":"b"},
"name": "b"
},
"optional": true
}
}
]
},
"right": {
"type": "ObjectExpression",
"start":18,"end":20,"loc":{"start":{"line":1,"column":18,"index":18},"end":{"line":1,"column":20,"index":20}},
"properties": []
},
"extra": {
"parenthesized": true,
"parenStart": 0
}
}
}
],
"directives": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(a?.b) => {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"type": "File",
"start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}},
"errors": [
"SyntaxError: Invalid optional chaining in the left-hand side of function parameter list. (1:1)",
"SyntaxError: Binding member expression. (1:1)"
],
"program": {
"type": "Program",
"start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}},
"expression": {
"type": "ArrowFunctionExpression",
"start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}},
"id": null,
"generator": false,
"async": false,
"params": [
{
"type": "OptionalMemberExpression",
"start":1,"end":5,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":5,"index":5}},
"object": {
"type": "Identifier",
"start":1,"end":2,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":2,"index":2},"identifierName":"a"},
"name": "a"
},
"computed": false,
"property": {
"type": "Identifier",
"start":4,"end":5,"loc":{"start":{"line":1,"column":4,"index":4},"end":{"line":1,"column":5,"index":5},"identifierName":"b"},
"name": "b"
},
"optional": true
}
],
"body": {
"type": "BlockStatement",
"start":10,"end":12,"loc":{"start":{"line":1,"column":10,"index":10},"end":{"line":1,"column":12,"index":12}},
"body": [],
"directives": []
}
}
}
],
"directives": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
function f(a?.b = c) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \",\" (1:12)"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
function f(a?.b) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \",\" (1:12)"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
for (a?.b of x);
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"type": "File",
"start":0,"end":16,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":16,"index":16}},
"errors": [
"SyntaxError: Invalid optional chaining in the left-hand side of for-of statement. (1:5)"
],
"program": {
"type": "Program",
"start":0,"end":16,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":16,"index":16}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ForOfStatement",
"start":0,"end":16,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":16,"index":16}},
"await": false,
"left": {
"type": "OptionalMemberExpression",
"start":5,"end":9,"loc":{"start":{"line":1,"column":5,"index":5},"end":{"line":1,"column":9,"index":9}},
"object": {
"type": "Identifier",
"start":5,"end":6,"loc":{"start":{"line":1,"column":5,"index":5},"end":{"line":1,"column":6,"index":6},"identifierName":"a"},
"name": "a"
},
"computed": false,
"property": {
"type": "Identifier",
"start":8,"end":9,"loc":{"start":{"line":1,"column":8,"index":8},"end":{"line":1,"column":9,"index":9},"identifierName":"b"},
"name": "b"
},
"optional": true
},
"right": {
"type": "Identifier",
"start":13,"end":14,"loc":{"start":{"line":1,"column":13,"index":13},"end":{"line":1,"column":14,"index":14},"identifierName":"x"},
"name": "x"
},
"body": {
"type": "EmptyStatement",
"start":15,"end":16,"loc":{"start":{"line":1,"column":15,"index":15},"end":{"line":1,"column":16,"index":16}}
}
}
],
"directives": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
for (a?.b in x);

0 comments on commit 5933ab1

Please sign in to comment.