Skip to content

Commit 54c539e

Browse files
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>
1 parent 135ab83 commit 54c539e

15 files changed

Lines changed: 322 additions & 108 deletions

File tree

‎packages/babel-parser/src/parser/expression.js‎

Lines changed: 53 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export default class ExpressionParser extends LValParser {
124124

125125
checkProto(
126126
prop: N.ObjectMember | N.SpreadElement,
127-
isRecord: boolean,
127+
isRecord: ?boolean,
128128
protoRef: { used: boolean },
129129
refExpressionErrors: ?ExpressionErrors,
130130
): void {
@@ -402,7 +402,7 @@ export default class ExpressionParser extends LValParser {
402402
minPrec: number,
403403
): N.Expression {
404404
if (this.isPrivateName(left)) {
405-
// https://tc39.es/proposal-private-fields-in-in
405+
// https://tc39.es/ecma262/#prod-RelationalExpression
406406
// RelationalExpression [In, Yield, Await]
407407
// [+In] PrivateIdentifier in ShiftExpression[?Yield, ?Await]
408408

@@ -1120,7 +1120,6 @@ export default class ExpressionParser extends LValParser {
11201120
this.state.type === tt.bracketBarL ? tt.bracketBarR : tt.bracketR,
11211121
/* canBePattern */ false,
11221122
/* isTuple */ true,
1123-
refExpressionErrors,
11241123
);
11251124
}
11261125
case tt.bracketL: {
@@ -1137,7 +1136,6 @@ export default class ExpressionParser extends LValParser {
11371136
this.state.type === tt.braceBarL ? tt.braceBarR : tt.braceR,
11381137
/* isPattern */ false,
11391138
/* isRecord */ true,
1140-
refExpressionErrors,
11411139
);
11421140
}
11431141
case tt.braceL: {
@@ -1513,21 +1511,6 @@ export default class ExpressionParser extends LValParser {
15131511
return this.finishNode(node, "Super");
15141512
}
15151513

1516-
parseMaybePrivateName(
1517-
isPrivateNameAllowed: boolean,
1518-
): N.PrivateName | N.Identifier {
1519-
const isPrivate = this.match(tt.privateName);
1520-
1521-
if (isPrivate) {
1522-
if (!isPrivateNameAllowed) {
1523-
this.raise(this.state.start + 1, Errors.UnexpectedPrivateField);
1524-
}
1525-
return this.parsePrivateName();
1526-
} else {
1527-
return this.parseIdentifier(true);
1528-
}
1529-
}
1530-
15311514
parsePrivateName(): N.PrivateName {
15321515
const node = this.startNode();
15331516
const id = this.startNodeAt(
@@ -1925,9 +1908,11 @@ export default class ExpressionParser extends LValParser {
19251908
}
19261909
}
19271910

1928-
const prop = this.parsePropertyDefinition(isPattern, refExpressionErrors);
1929-
if (!isPattern) {
1930-
// $FlowIgnore RestElement will never be returned if !isPattern
1911+
let prop;
1912+
if (isPattern) {
1913+
prop = this.parseBindingProperty();
1914+
} else {
1915+
prop = this.parsePropertyDefinition(refExpressionErrors);
19311916
this.checkProto(prop, isRecord, propHash, refExpressionErrors);
19321917
}
19331918

@@ -1974,9 +1959,8 @@ export default class ExpressionParser extends LValParser {
19741959

19751960
// https://tc39.es/ecma262/#prod-PropertyDefinition
19761961
parsePropertyDefinition(
1977-
isPattern: boolean,
19781962
refExpressionErrors?: ?ExpressionErrors,
1979-
): N.ObjectMember | N.SpreadElement | N.RestElement {
1963+
): N.ObjectMember | N.SpreadElement {
19801964
let decorators = [];
19811965
if (this.match(tt.at)) {
19821966
if (this.hasPlugin("decorators")) {
@@ -1991,22 +1975,13 @@ export default class ExpressionParser extends LValParser {
19911975
}
19921976

19931977
const prop = this.startNode();
1994-
let isGenerator = false;
19951978
let isAsync = false;
19961979
let isAccessor = false;
19971980
let startPos;
19981981
let startLoc;
19991982

20001983
if (this.match(tt.ellipsis)) {
20011984
if (decorators.length) this.unexpected();
2002-
if (isPattern) {
2003-
this.next();
2004-
// Don't use parseRestBinding() as we only allow Identifier here.
2005-
prop.argument = this.parseIdentifier();
2006-
this.checkCommaAfterRest(charCodes.rightCurlyBrace);
2007-
return this.finishNode(prop, "RestElement");
2008-
}
2009-
20101985
return this.parseSpread();
20111986
}
20121987

@@ -2017,32 +1992,25 @@ export default class ExpressionParser extends LValParser {
20171992

20181993
prop.method = false;
20191994

2020-
if (isPattern || refExpressionErrors) {
1995+
if (refExpressionErrors) {
20211996
startPos = this.state.start;
20221997
startLoc = this.state.startLoc;
20231998
}
20241999

2025-
if (!isPattern) {
2026-
isGenerator = this.eat(tt.star);
2027-
}
2028-
2000+
let isGenerator = this.eat(tt.star);
2001+
this.parsePropertyNamePrefixOperator(prop);
20292002
const containsEsc = this.state.containsEsc;
2030-
const key = this.parsePropertyName(prop, /* isPrivateNameAllowed */ false);
2003+
const key = this.parsePropertyName(prop);
20312004

2032-
if (
2033-
!isPattern &&
2034-
!isGenerator &&
2035-
!containsEsc &&
2036-
this.maybeAsyncOrAccessorProp(prop)
2037-
) {
2005+
if (!isGenerator && !containsEsc && this.maybeAsyncOrAccessorProp(prop)) {
20382006
const keyName = key.name;
20392007
// https://tc39.es/ecma262/#prod-AsyncMethod
20402008
// https://tc39.es/ecma262/#prod-AsyncGeneratorMethod
20412009
if (keyName === "async" && !this.hasPrecedingLineBreak()) {
20422010
isAsync = true;
20432011
this.resetPreviousNodeTrailingComments(key);
20442012
isGenerator = this.eat(tt.star);
2045-
this.parsePropertyName(prop, /* isPrivateNameAllowed */ false);
2013+
this.parsePropertyName(prop);
20462014
}
20472015
// get PropertyName[?Yield, ?Await] () { FunctionBody[~Yield, ~Await] }
20482016
// set PropertyName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] }
@@ -2055,7 +2023,7 @@ export default class ExpressionParser extends LValParser {
20552023
this.raise(this.state.pos, Errors.AccessorIsGenerator, keyName);
20562024
this.next();
20572025
}
2058-
this.parsePropertyName(prop, /* isPrivateNameAllowed */ false);
2026+
this.parsePropertyName(prop);
20592027
}
20602028
}
20612029

@@ -2065,7 +2033,7 @@ export default class ExpressionParser extends LValParser {
20652033
startLoc,
20662034
isGenerator,
20672035
isAsync,
2068-
isPattern,
2036+
false /* isPattern */,
20692037
isAccessor,
20702038
refExpressionErrors,
20712039
);
@@ -2231,23 +2199,44 @@ export default class ExpressionParser extends LValParser {
22312199

22322200
parsePropertyName(
22332201
prop: N.ObjectOrClassMember | N.ClassMember | N.TsNamedTypeElementBase,
2234-
isPrivateNameAllowed: boolean,
22352202
): N.Expression | N.Identifier {
22362203
if (this.eat(tt.bracketL)) {
22372204
(prop: $FlowSubtype<N.ObjectOrClassMember>).computed = true;
22382205
prop.key = this.parseMaybeAssignAllowIn();
22392206
this.expect(tt.bracketR);
22402207
} else {
22412208
// We check if it's valid for it to be a private name when we push it.
2242-
const type = this.state.type;
2243-
(prop: $FlowFixMe).key =
2244-
type === tt.num ||
2245-
type === tt.string ||
2246-
type === tt.bigint ||
2247-
type === tt.decimal
2248-
? this.parseExprAtom()
2249-
: this.parseMaybePrivateName(isPrivateNameAllowed);
2250-
2209+
const { type, value } = this.state;
2210+
let key;
2211+
// most un-computed property names are identifiers
2212+
if (tokenIsKeywordOrIdentifier(type)) {
2213+
key = this.parseIdentifier(true);
2214+
} else {
2215+
switch (type) {
2216+
case tt.num:
2217+
key = this.parseNumericLiteral(value);
2218+
break;
2219+
case tt.string:
2220+
key = this.parseStringLiteral(value);
2221+
break;
2222+
case tt.bigint:
2223+
key = this.parseBigIntLiteral(value);
2224+
break;
2225+
case tt.decimal:
2226+
key = this.parseDecimalLiteral(value);
2227+
break;
2228+
case tt.privateName: {
2229+
// the class private key has been handled in parseClassElementName
2230+
const privateKeyPos = this.state.start + 1;
2231+
this.raise(privateKeyPos, Errors.UnexpectedPrivateField);
2232+
key = this.parsePrivateName();
2233+
break;
2234+
}
2235+
default:
2236+
throw this.unexpected();
2237+
}
2238+
}
2239+
(prop: $FlowFixMe).key = key;
22512240
if (type !== tt.privateName) {
22522241
// ClassPrivateProperty is never computed, so we don't assign in that case.
22532242
prop.computed = false;
@@ -2974,4 +2963,10 @@ export default class ExpressionParser extends LValParser {
29742963
this.eat(tt.braceR);
29752964
return this.finishNode<N.ModuleExpression>(node, "ModuleExpression");
29762965
}
2966+
2967+
// Used in Flow plugin
2968+
parsePropertyNamePrefixOperator(
2969+
// eslint-disable-next-line no-unused-vars
2970+
prop: N.ObjectOrClassMember | N.ClassMember,
2971+
): void {}
29772972
}

‎packages/babel-parser/src/parser/lval.js‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import type {
1111
Pattern,
1212
RestElement,
1313
SpreadElement,
14+
/*:: ObjectOrClassMember, */
15+
/*:: ClassMember, */
16+
/*:: ObjectMember, */
17+
/*:: TsNamedTypeElementBase, */
1418
/*:: Identifier, */
1519
/*:: ObjectExpression, */
1620
/*:: ObjectPattern, */
@@ -46,6 +50,19 @@ export default class LValParser extends NodeUtils {
4650
isRecord?: ?boolean,
4751
refExpressionErrors?: ?ExpressionErrors,
4852
) => T;
53+
+parseObjPropValue: (
54+
prop: any,
55+
startPos: ?number,
56+
startLoc: ?Position,
57+
isGenerator: boolean,
58+
isAsync: boolean,
59+
isPattern: boolean,
60+
isAccessor: boolean,
61+
refExpressionErrors?: ?ExpressionErrors,
62+
) => void;
63+
+parsePropertyName: (
64+
prop: ObjectOrClassMember | ClassMember | TsNamedTypeElementBase,
65+
) => Expression | Identifier;
4966
*/
5067
// Forward-declaration: defined in statement.js
5168
/*::
@@ -386,6 +403,38 @@ export default class LValParser extends NodeUtils {
386403
return elts;
387404
}
388405

406+
// https://tc39.es/ecma262/#prod-BindingRestProperty
407+
parseBindingRestProperty(prop: RestElement): RestElement {
408+
this.next(); // eat '...'
409+
// Don't use parseRestBinding() as we only allow Identifier here.
410+
prop.argument = this.parseIdentifier();
411+
this.checkCommaAfterRest(charCodes.rightCurlyBrace);
412+
return this.finishNode(prop, "RestElement");
413+
}
414+
415+
// https://tc39.es/ecma262/#prod-BindingProperty
416+
parseBindingProperty(): ObjectMember | RestElement {
417+
const prop = this.startNode();
418+
const { type, start: startPos, startLoc } = this.state;
419+
if (type === tt.ellipsis) {
420+
return this.parseBindingRestProperty(prop);
421+
} else {
422+
this.parsePropertyName(prop);
423+
}
424+
prop.method = false;
425+
this.parseObjPropValue(
426+
prop,
427+
startPos,
428+
startLoc,
429+
false /* isGenerator */,
430+
false /* isAsync */,
431+
true /* isPattern */,
432+
false /* isAccessor */,
433+
);
434+
435+
return prop;
436+
}
437+
389438
parseAssignableListItem(
390439
allowModifiers: ?boolean,
391440
decorators: Decorator[],

‎packages/babel-parser/src/parser/statement.js‎

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,6 +1437,7 @@ export default class StatementParser extends ExpressionParser {
14371437
const publicMember: typeof publicMethod | typeof publicProp = publicMethod;
14381438

14391439
member.static = isStatic;
1440+
this.parsePropertyNamePrefixOperator(member);
14401441

14411442
if (this.eat(tt.star)) {
14421443
// a generator
@@ -1597,7 +1598,7 @@ export default class StatementParser extends ExpressionParser {
15971598
}
15981599
}
15991600

1600-
// https://tc39.es/proposal-class-fields/#prod-ClassElementName
1601+
// https://tc39.es/ecma262/#prod-ClassElementName
16011602
parseClassElementName(member: N.ClassMember): N.Expression | N.Identifier {
16021603
const { type, value, start } = this.state;
16031604
if (
@@ -1608,11 +1609,16 @@ export default class StatementParser extends ExpressionParser {
16081609
this.raise(start, Errors.StaticPrototype);
16091610
}
16101611

1611-
if (type === tt.privateName && value === "constructor") {
1612-
this.raise(start, Errors.ConstructorClassPrivateField);
1612+
if (type === tt.privateName) {
1613+
if (value === "constructor") {
1614+
this.raise(start, Errors.ConstructorClassPrivateField);
1615+
}
1616+
const key = this.parsePrivateName();
1617+
member.key = key;
1618+
return key;
16131619
}
16141620

1615-
return this.parsePropertyName(member, /* isPrivateNameAllowed */ true);
1621+
return this.parsePropertyName(member);
16161622
}
16171623

16181624
parseClassStaticBlock(
@@ -1733,7 +1739,7 @@ export default class StatementParser extends ExpressionParser {
17331739
methodOrProp: N.ClassMethod | N.ClassProperty,
17341740
): void {}
17351741

1736-
// https://tc39.es/proposal-class-fields/#prod-FieldDefinition
1742+
// https://tc39.es/ecma262/#prod-FieldDefinition
17371743
parseClassPrivateProperty(
17381744
node: N.ClassPrivateProperty,
17391745
): N.ClassPrivateProperty {
@@ -1742,14 +1748,14 @@ export default class StatementParser extends ExpressionParser {
17421748
return this.finishNode(node, "ClassPrivateProperty");
17431749
}
17441750

1745-
// https://tc39.es/proposal-class-fields/#prod-FieldDefinition
1751+
// https://tc39.es/ecma262/#prod-FieldDefinition
17461752
parseClassProperty(node: N.ClassProperty): N.ClassProperty {
17471753
this.parseInitializer(node);
17481754
this.semicolon();
17491755
return this.finishNode(node, "ClassProperty");
17501756
}
17511757

1752-
// https://tc39.es/proposal-class-fields/#prod-Initializer
1758+
// https://tc39.es/ecma262/#prod-Initializer
17531759
parseInitializer(node: N.ClassProperty | N.ClassPrivateProperty): void {
17541760
this.scope.enter(SCOPE_CLASS | SCOPE_SUPER);
17551761
this.expressionScope.enter(newExpressionScope());

0 commit comments

Comments
 (0)