Refactor bindingProperty parsing (#13929)

* refactor: inline parseMaybePrivateName

* correct test case

* perf: fast exit in checkExpressionErrors

* refactor: add parseBindingProperty

* fix: private property with variance

* Update packages/babel-parser/src/parser/expression.js

Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>

* chore: update testcase

* refactor: remove refExpressionErrors for record/tuple

They are always non-ambiguous.

Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
This commit is contained in:
Huáng Jùnliàng 2021-11-12 10:11:46 -05:00 committed by GitHub
parent 135ab837bc
commit 54c539ecc1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 322 additions and 108 deletions

View File

@ -124,7 +124,7 @@ export default class ExpressionParser extends LValParser {
checkProto(
prop: N.ObjectMember | N.SpreadElement,
isRecord: boolean,
isRecord: ?boolean,
protoRef: { used: boolean },
refExpressionErrors: ?ExpressionErrors,
): void {
@ -402,7 +402,7 @@ export default class ExpressionParser extends LValParser {
minPrec: number,
): N.Expression {
if (this.isPrivateName(left)) {
// https://tc39.es/proposal-private-fields-in-in
// https://tc39.es/ecma262/#prod-RelationalExpression
// RelationalExpression [In, Yield, Await]
// [+In] PrivateIdentifier in ShiftExpression[?Yield, ?Await]
@ -1120,7 +1120,6 @@ export default class ExpressionParser extends LValParser {
this.state.type === tt.bracketBarL ? tt.bracketBarR : tt.bracketR,
/* canBePattern */ false,
/* isTuple */ true,
refExpressionErrors,
);
}
case tt.bracketL: {
@ -1137,7 +1136,6 @@ export default class ExpressionParser extends LValParser {
this.state.type === tt.braceBarL ? tt.braceBarR : tt.braceR,
/* isPattern */ false,
/* isRecord */ true,
refExpressionErrors,
);
}
case tt.braceL: {
@ -1513,21 +1511,6 @@ export default class ExpressionParser extends LValParser {
return this.finishNode(node, "Super");
}
parseMaybePrivateName(
isPrivateNameAllowed: boolean,
): N.PrivateName | N.Identifier {
const isPrivate = this.match(tt.privateName);
if (isPrivate) {
if (!isPrivateNameAllowed) {
this.raise(this.state.start + 1, Errors.UnexpectedPrivateField);
}
return this.parsePrivateName();
} else {
return this.parseIdentifier(true);
}
}
parsePrivateName(): N.PrivateName {
const node = this.startNode();
const id = this.startNodeAt(
@ -1925,9 +1908,11 @@ export default class ExpressionParser extends LValParser {
}
}
const prop = this.parsePropertyDefinition(isPattern, refExpressionErrors);
if (!isPattern) {
// $FlowIgnore RestElement will never be returned if !isPattern
let prop;
if (isPattern) {
prop = this.parseBindingProperty();
} else {
prop = this.parsePropertyDefinition(refExpressionErrors);
this.checkProto(prop, isRecord, propHash, refExpressionErrors);
}
@ -1974,9 +1959,8 @@ export default class ExpressionParser extends LValParser {
// https://tc39.es/ecma262/#prod-PropertyDefinition
parsePropertyDefinition(
isPattern: boolean,
refExpressionErrors?: ?ExpressionErrors,
): N.ObjectMember | N.SpreadElement | N.RestElement {
): N.ObjectMember | N.SpreadElement {
let decorators = [];
if (this.match(tt.at)) {
if (this.hasPlugin("decorators")) {
@ -1991,7 +1975,6 @@ export default class ExpressionParser extends LValParser {
}
const prop = this.startNode();
let isGenerator = false;
let isAsync = false;
let isAccessor = false;
let startPos;
@ -1999,14 +1982,6 @@ export default class ExpressionParser extends LValParser {
if (this.match(tt.ellipsis)) {
if (decorators.length) this.unexpected();
if (isPattern) {
this.next();
// Don't use parseRestBinding() as we only allow Identifier here.
prop.argument = this.parseIdentifier();
this.checkCommaAfterRest(charCodes.rightCurlyBrace);
return this.finishNode(prop, "RestElement");
}
return this.parseSpread();
}
@ -2017,24 +1992,17 @@ export default class ExpressionParser extends LValParser {
prop.method = false;
if (isPattern || refExpressionErrors) {
if (refExpressionErrors) {
startPos = this.state.start;
startLoc = this.state.startLoc;
}
if (!isPattern) {
isGenerator = this.eat(tt.star);
}
let isGenerator = this.eat(tt.star);
this.parsePropertyNamePrefixOperator(prop);
const containsEsc = this.state.containsEsc;
const key = this.parsePropertyName(prop, /* isPrivateNameAllowed */ false);
const key = this.parsePropertyName(prop);
if (
!isPattern &&
!isGenerator &&
!containsEsc &&
this.maybeAsyncOrAccessorProp(prop)
) {
if (!isGenerator && !containsEsc && this.maybeAsyncOrAccessorProp(prop)) {
const keyName = key.name;
// https://tc39.es/ecma262/#prod-AsyncMethod
// https://tc39.es/ecma262/#prod-AsyncGeneratorMethod
@ -2042,7 +2010,7 @@ export default class ExpressionParser extends LValParser {
isAsync = true;
this.resetPreviousNodeTrailingComments(key);
isGenerator = this.eat(tt.star);
this.parsePropertyName(prop, /* isPrivateNameAllowed */ false);
this.parsePropertyName(prop);
}
// get PropertyName[?Yield, ?Await] () { FunctionBody[~Yield, ~Await] }
// set PropertyName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] }
@ -2055,7 +2023,7 @@ export default class ExpressionParser extends LValParser {
this.raise(this.state.pos, Errors.AccessorIsGenerator, keyName);
this.next();
}
this.parsePropertyName(prop, /* isPrivateNameAllowed */ false);
this.parsePropertyName(prop);
}
}
@ -2065,7 +2033,7 @@ export default class ExpressionParser extends LValParser {
startLoc,
isGenerator,
isAsync,
isPattern,
false /* isPattern */,
isAccessor,
refExpressionErrors,
);
@ -2231,7 +2199,6 @@ export default class ExpressionParser extends LValParser {
parsePropertyName(
prop: N.ObjectOrClassMember | N.ClassMember | N.TsNamedTypeElementBase,
isPrivateNameAllowed: boolean,
): N.Expression | N.Identifier {
if (this.eat(tt.bracketL)) {
(prop: $FlowSubtype<N.ObjectOrClassMember>).computed = true;
@ -2239,15 +2206,37 @@ export default class ExpressionParser extends LValParser {
this.expect(tt.bracketR);
} else {
// We check if it's valid for it to be a private name when we push it.
const type = this.state.type;
(prop: $FlowFixMe).key =
type === tt.num ||
type === tt.string ||
type === tt.bigint ||
type === tt.decimal
? this.parseExprAtom()
: this.parseMaybePrivateName(isPrivateNameAllowed);
const { type, value } = this.state;
let key;
// most un-computed property names are identifiers
if (tokenIsKeywordOrIdentifier(type)) {
key = this.parseIdentifier(true);
} else {
switch (type) {
case tt.num:
key = this.parseNumericLiteral(value);
break;
case tt.string:
key = this.parseStringLiteral(value);
break;
case tt.bigint:
key = this.parseBigIntLiteral(value);
break;
case tt.decimal:
key = this.parseDecimalLiteral(value);
break;
case tt.privateName: {
// the class private key has been handled in parseClassElementName
const privateKeyPos = this.state.start + 1;
this.raise(privateKeyPos, Errors.UnexpectedPrivateField);
key = this.parsePrivateName();
break;
}
default:
throw this.unexpected();
}
}
(prop: $FlowFixMe).key = key;
if (type !== tt.privateName) {
// ClassPrivateProperty is never computed, so we don't assign in that case.
prop.computed = false;
@ -2974,4 +2963,10 @@ export default class ExpressionParser extends LValParser {
this.eat(tt.braceR);
return this.finishNode<N.ModuleExpression>(node, "ModuleExpression");
}
// Used in Flow plugin
parsePropertyNamePrefixOperator(
// eslint-disable-next-line no-unused-vars
prop: N.ObjectOrClassMember | N.ClassMember,
): void {}
}

View File

@ -11,6 +11,10 @@ import type {
Pattern,
RestElement,
SpreadElement,
/*:: ObjectOrClassMember, */
/*:: ClassMember, */
/*:: ObjectMember, */
/*:: TsNamedTypeElementBase, */
/*:: Identifier, */
/*:: ObjectExpression, */
/*:: ObjectPattern, */
@ -46,6 +50,19 @@ export default class LValParser extends NodeUtils {
isRecord?: ?boolean,
refExpressionErrors?: ?ExpressionErrors,
) => T;
+parseObjPropValue: (
prop: any,
startPos: ?number,
startLoc: ?Position,
isGenerator: boolean,
isAsync: boolean,
isPattern: boolean,
isAccessor: boolean,
refExpressionErrors?: ?ExpressionErrors,
) => void;
+parsePropertyName: (
prop: ObjectOrClassMember | ClassMember | TsNamedTypeElementBase,
) => Expression | Identifier;
*/
// Forward-declaration: defined in statement.js
/*::
@ -386,6 +403,38 @@ export default class LValParser extends NodeUtils {
return elts;
}
// https://tc39.es/ecma262/#prod-BindingRestProperty
parseBindingRestProperty(prop: RestElement): RestElement {
this.next(); // eat '...'
// Don't use parseRestBinding() as we only allow Identifier here.
prop.argument = this.parseIdentifier();
this.checkCommaAfterRest(charCodes.rightCurlyBrace);
return this.finishNode(prop, "RestElement");
}
// https://tc39.es/ecma262/#prod-BindingProperty
parseBindingProperty(): ObjectMember | RestElement {
const prop = this.startNode();
const { type, start: startPos, startLoc } = this.state;
if (type === tt.ellipsis) {
return this.parseBindingRestProperty(prop);
} else {
this.parsePropertyName(prop);
}
prop.method = false;
this.parseObjPropValue(
prop,
startPos,
startLoc,
false /* isGenerator */,
false /* isAsync */,
true /* isPattern */,
false /* isAccessor */,
);
return prop;
}
parseAssignableListItem(
allowModifiers: ?boolean,
decorators: Decorator[],

View File

@ -1437,6 +1437,7 @@ export default class StatementParser extends ExpressionParser {
const publicMember: typeof publicMethod | typeof publicProp = publicMethod;
member.static = isStatic;
this.parsePropertyNamePrefixOperator(member);
if (this.eat(tt.star)) {
// a generator
@ -1597,7 +1598,7 @@ export default class StatementParser extends ExpressionParser {
}
}
// https://tc39.es/proposal-class-fields/#prod-ClassElementName
// https://tc39.es/ecma262/#prod-ClassElementName
parseClassElementName(member: N.ClassMember): N.Expression | N.Identifier {
const { type, value, start } = this.state;
if (
@ -1608,11 +1609,16 @@ export default class StatementParser extends ExpressionParser {
this.raise(start, Errors.StaticPrototype);
}
if (type === tt.privateName && value === "constructor") {
this.raise(start, Errors.ConstructorClassPrivateField);
if (type === tt.privateName) {
if (value === "constructor") {
this.raise(start, Errors.ConstructorClassPrivateField);
}
const key = this.parsePrivateName();
member.key = key;
return key;
}
return this.parsePropertyName(member, /* isPrivateNameAllowed */ true);
return this.parsePropertyName(member);
}
parseClassStaticBlock(
@ -1733,7 +1739,7 @@ export default class StatementParser extends ExpressionParser {
methodOrProp: N.ClassMethod | N.ClassProperty,
): void {}
// https://tc39.es/proposal-class-fields/#prod-FieldDefinition
// https://tc39.es/ecma262/#prod-FieldDefinition
parseClassPrivateProperty(
node: N.ClassPrivateProperty,
): N.ClassPrivateProperty {
@ -1742,14 +1748,14 @@ export default class StatementParser extends ExpressionParser {
return this.finishNode(node, "ClassPrivateProperty");
}
// https://tc39.es/proposal-class-fields/#prod-FieldDefinition
// https://tc39.es/ecma262/#prod-FieldDefinition
parseClassProperty(node: N.ClassProperty): N.ClassProperty {
this.parseInitializer(node);
this.semicolon();
return this.finishNode(node, "ClassProperty");
}
// https://tc39.es/proposal-class-fields/#prod-Initializer
// https://tc39.es/ecma262/#prod-Initializer
parseInitializer(node: N.ClassProperty | N.ClassPrivateProperty): void {
this.scope.enter(SCOPE_CLASS | SCOPE_SUPER);
this.expressionScope.enter(newExpressionScope());

View File

@ -264,19 +264,20 @@ export default class UtilParser extends Tokenizer {
if (!refExpressionErrors) return false;
const { shorthandAssign, doubleProto, optionalParameters } =
refExpressionErrors;
// shorthandAssign >= 0 || doubleProto >= 0 || optionalParameters >= 0
const hasErrors = shorthandAssign + doubleProto + optionalParameters > -3;
if (!andThrow) {
return (
shorthandAssign >= 0 || doubleProto >= 0 || optionalParameters >= 0
);
}
if (shorthandAssign >= 0) {
this.unexpected(shorthandAssign);
}
if (doubleProto >= 0) {
this.raise(doubleProto, Errors.DuplicateProto);
}
if (optionalParameters >= 0) {
this.unexpected(optionalParameters);
return hasErrors;
} else if (hasErrors) {
if (shorthandAssign >= 0) {
this.unexpected(shorthandAssign);
}
if (doubleProto >= 0) {
this.raise(doubleProto, Errors.DuplicateProto);
}
if (optionalParameters >= 0) {
this.unexpected(optionalParameters);
}
}
}
@ -304,7 +305,7 @@ export default class UtilParser extends Tokenizer {
/*
* Return the string value of a given private name
* WITHOUT `#`
* @see {@link https://tc39.es/proposal-class-fields/#sec-private-names-static-semantics-stringvalue}
* @see {@link https://tc39.es/ecma262/#sec-static-semantics-stringvalue}
*/
getPrivateNameSV(node: Node): string {
return node.id.name;

View File

@ -2475,15 +2475,10 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}
}
parsePropertyName(
node: N.ObjectOrClassMember | N.ClassMember | N.TsNamedTypeElementBase,
isPrivateNameAllowed: boolean,
): N.Identifier {
const variance = this.flowParseVariance();
const key = super.parsePropertyName(node, isPrivateNameAllowed);
// $FlowIgnore ("variance" not defined on TsNamedTypeElementBase)
node.variance = variance;
return key;
parsePropertyNamePrefixOperator(
node: N.ObjectOrClassMember | N.ClassMember,
): void {
node.variance = this.flowParseVariance();
}
// parse type parameters for object method shorthand

View File

@ -780,7 +780,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return idx;
}
this.parsePropertyName(node, /* isPrivateNameAllowed */ false);
this.parsePropertyName(node);
if (
!node.computed &&
node.key.type === "Identifier" &&
@ -788,7 +788,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.tsTokenCanFollowModifier()
) {
node.kind = node.key.name;
this.parsePropertyName(node, /* isPrivateNameAllowed */ false);
this.parsePropertyName(node);
}
return this.tsParsePropertyOrMethodSignature(node, !!node.readonly);
}

View File

@ -816,6 +816,7 @@ export type ClassPrivateMethod = NodeBase &
type: "ClassPrivateMethod",
key: PrivateName,
computed: false,
variance?: ?FlowVariance, // TODO: Not in spec
};
export type ClassProperty = ClassMemberBase &
@ -825,6 +826,8 @@ export type ClassProperty = ClassMemberBase &
value: ?Expression, // TODO: Not in spec that this is nullable.
typeAnnotation?: ?TypeAnnotationBase, // TODO: Not in spec
// Flow only:
variance?: ?FlowVariance, // TODO: Not in spec
// TypeScript only: (TODO: Not in spec)
@ -847,6 +850,9 @@ export type ClassPrivateProperty = NodeBase & {
definite?: true,
readonly?: true,
override?: true,
// Flow only
variance?: ?FlowVariance,
};
export type OptClassDeclaration = ClassBase &

View File

@ -1,3 +1,3 @@
class C {
#p = ({ #x: 42 });
#p = ({ #x() {} });
}

View File

@ -1,18 +1,18 @@
{
"type": "File",
"start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"start":0,"end":33,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Private names can only be used as the name of a class element (i.e. class C { #p = 42; #m() {} } )\n or a property of member expression (i.e. this.#p). (2:11)"
],
"program": {
"type": "Program",
"start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"start":0,"end":33,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"start":0,"end":33,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7},"identifierName":"C"},
@ -21,11 +21,11 @@
"superClass": null,
"body": {
"type": "ClassBody",
"start":8,"end":32,"loc":{"start":{"line":1,"column":8},"end":{"line":3,"column":1}},
"start":8,"end":33,"loc":{"start":{"line":1,"column":8},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassPrivateProperty",
"start":12,"end":30,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":20}},
"start":12,"end":31,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":21}},
"static": false,
"key": {
"type": "PrivateName",
@ -38,12 +38,12 @@
},
"value": {
"type": "ObjectExpression",
"start":18,"end":28,"loc":{"start":{"line":2,"column":8},"end":{"line":2,"column":18}},
"start":18,"end":29,"loc":{"start":{"line":2,"column":8},"end":{"line":2,"column":19}},
"properties": [
{
"type": "ObjectProperty",
"start":20,"end":26,"loc":{"start":{"line":2,"column":10},"end":{"line":2,"column":16}},
"method": false,
"type": "ObjectMethod",
"start":20,"end":27,"loc":{"start":{"line":2,"column":10},"end":{"line":2,"column":17}},
"method": true,
"key": {
"type": "PrivateName",
"start":20,"end":22,"loc":{"start":{"line":2,"column":10},"end":{"line":2,"column":12}},
@ -53,15 +53,16 @@
"name": "x"
}
},
"shorthand": false,
"value": {
"type": "NumericLiteral",
"start":24,"end":26,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":16}},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
"kind": "method",
"id": null,
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start":25,"end":27,"loc":{"start":{"line":2,"column":15},"end":{"line":2,"column":17}},
"body": [],
"directives": []
}
}
],

View File

@ -0,0 +1,3 @@
class C {
#p = ({ #x: 42 });
}

View File

@ -0,0 +1,80 @@
{
"type": "File",
"start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Private names can only be used as the name of a class element (i.e. class C { #p = 42; #m() {} } )\n or a property of member expression (i.e. this.#p). (2:11)"
],
"program": {
"type": "Program",
"start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7},"identifierName":"C"},
"name": "C"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":8,"end":32,"loc":{"start":{"line":1,"column":8},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassPrivateProperty",
"start":12,"end":30,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":20}},
"static": false,
"key": {
"type": "PrivateName",
"start":12,"end":14,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":4}},
"id": {
"type": "Identifier",
"start":13,"end":14,"loc":{"start":{"line":2,"column":3},"end":{"line":2,"column":4},"identifierName":"p"},
"name": "p"
}
},
"value": {
"type": "ObjectExpression",
"start":18,"end":28,"loc":{"start":{"line":2,"column":8},"end":{"line":2,"column":18}},
"properties": [
{
"type": "ObjectProperty",
"start":20,"end":26,"loc":{"start":{"line":2,"column":10},"end":{"line":2,"column":16}},
"method": false,
"key": {
"type": "PrivateName",
"start":20,"end":22,"loc":{"start":{"line":2,"column":10},"end":{"line":2,"column":12}},
"id": {
"type": "Identifier",
"start":21,"end":22,"loc":{"start":{"line":2,"column":11},"end":{"line":2,"column":12},"identifierName":"x"},
"name": "x"
}
},
"shorthand": false,
"value": {
"type": "NumericLiteral",
"start":24,"end":26,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":16}},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}
],
"extra": {
"parenthesized": true,
"parenStart": 17
}
}
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1 @@
var { @foo foo } = foo;

View File

@ -0,0 +1,4 @@
{
"plugins": [["decorators-legacy"]],
"throws": "Unexpected token (1:6)"
}

View File

@ -0,0 +1,4 @@
class A {
+#foo;
-#bar;
}

View File

@ -0,0 +1,69 @@
{
"type": "File",
"start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
"program": {
"type": "Program",
"start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7},"identifierName":"A"},
"name": "A"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":8,"end":29,"loc":{"start":{"line":1,"column":8},"end":{"line":4,"column":1}},
"body": [
{
"type": "ClassPrivateProperty",
"start":12,"end":18,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":8}},
"static": false,
"variance": {
"type": "Variance",
"start":12,"end":13,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":3}},
"kind": "plus"
},
"key": {
"type": "PrivateName",
"start":13,"end":17,"loc":{"start":{"line":2,"column":3},"end":{"line":2,"column":7}},
"id": {
"type": "Identifier",
"start":14,"end":17,"loc":{"start":{"line":2,"column":4},"end":{"line":2,"column":7},"identifierName":"foo"},
"name": "foo"
}
},
"value": null
},
{
"type": "ClassPrivateProperty",
"start":21,"end":27,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":8}},
"static": false,
"variance": {
"type": "Variance",
"start":21,"end":22,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":3}},
"kind": "minus"
},
"key": {
"type": "PrivateName",
"start":22,"end":26,"loc":{"start":{"line":3,"column":3},"end":{"line":3,"column":7}},
"id": {
"type": "Identifier",
"start":23,"end":26,"loc":{"start":{"line":3,"column":4},"end":{"line":3,"column":7},"identifierName":"bar"},
"name": "bar"
}
},
"value": null
}
]
}
}
],
"directives": []
}
}