Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor bindingProperty parsing #13929

Merged
merged 8 commits into from Nov 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
111 changes: 53 additions & 58 deletions packages/babel-parser/src/parser/expression.js
Expand Up @@ -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 {
Expand Down Expand Up @@ -401,7 +401,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]

Expand Down Expand Up @@ -1119,7 +1119,6 @@ export default class ExpressionParser extends LValParser {
this.state.type === tt.bracketBarL ? tt.bracketBarR : tt.bracketR,
/* canBePattern */ false,
/* isTuple */ true,
refExpressionErrors,
);
}
case tt.bracketL: {
Expand All @@ -1136,7 +1135,6 @@ export default class ExpressionParser extends LValParser {
this.state.type === tt.braceBarL ? tt.braceBarR : tt.braceR,
/* isPattern */ false,
/* isRecord */ true,
refExpressionErrors,
);
}
case tt.braceL: {
Expand Down Expand Up @@ -1512,21 +1510,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(
Expand Down Expand Up @@ -1924,9 +1907,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);
}

Expand Down Expand Up @@ -1973,9 +1958,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")) {
Expand All @@ -1990,22 +1974,13 @@ export default class ExpressionParser extends LValParser {
}

const prop = this.startNode();
let isGenerator = false;
let isAsync = false;
let isAccessor = false;
let startPos;
let startLoc;

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();
}

Expand All @@ -2016,32 +1991,25 @@ 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
if (keyName === "async" && !this.hasPrecedingLineBreak()) {
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] }
Expand All @@ -2054,7 +2022,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);
}
}

Expand All @@ -2064,7 +2032,7 @@ export default class ExpressionParser extends LValParser {
startLoc,
isGenerator,
isAsync,
isPattern,
false /* isPattern */,
isAccessor,
refExpressionErrors,
);
Expand Down Expand Up @@ -2230,23 +2198,44 @@ 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;
prop.key = this.parseMaybeAssignAllowIn();
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;
Expand Down Expand Up @@ -2973,4 +2962,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 {}
}
49 changes: 49 additions & 0 deletions packages/babel-parser/src/parser/lval.js
Expand Up @@ -11,6 +11,10 @@ import type {
Pattern,
RestElement,
SpreadElement,
/*:: ObjectOrClassMember, */
/*:: ClassMember, */
/*:: ObjectMember, */
/*:: TsNamedTypeElementBase, */
/*:: Identifier, */
/*:: ObjectExpression, */
/*:: ObjectPattern, */
Expand Down Expand Up @@ -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
/*::
Expand Down Expand Up @@ -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[],
Expand Down
20 changes: 13 additions & 7 deletions packages/babel-parser/src/parser/statement.js
Expand Up @@ -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
Expand Down Expand Up @@ -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 (
Expand All @@ -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(
Expand Down Expand Up @@ -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 {
Expand All @@ -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());
Expand Down