Extract 'parseClassMember' method (#533)

This commit is contained in:
Andy 2017-05-26 20:44:56 -07:00 committed by Henry Zhu
parent aad95c63ec
commit 50694f99b1
4 changed files with 120 additions and 102 deletions

View File

@ -887,7 +887,7 @@ export default class ExpressionParser extends LValParser {
// get methods aren't allowed to have any parameters // get methods aren't allowed to have any parameters
// set methods must have exactly 1 parameter // set methods must have exactly 1 parameter
checkGetterSetterParamCount(method: N.ObjectMethod): void { checkGetterSetterParamCount(method: N.ObjectMethod | N.ClassMethod): void {
const paramCount = method.kind === "get" ? 0 : 1; const paramCount = method.kind === "get" ? 0 : 1;
if (method.params.length !== paramCount) { if (method.params.length !== paramCount) {
const start = method.start; const start = method.start;
@ -956,12 +956,14 @@ export default class ExpressionParser extends LValParser {
if (!node) this.unexpected(); if (!node) this.unexpected();
} }
parsePropertyName(prop: N.ObjectMember): N.Identifier { parsePropertyName(prop: N.ObjectOrClassMember): N.Identifier {
if (this.eat(tt.bracketL)) { if (this.eat(tt.bracketL)) {
// $FlowFixMe (ClassPrivateMember shouldn't be allowed to be computed!)
prop.computed = true; prop.computed = true;
prop.key = this.parseMaybeAssign(); prop.key = this.parseMaybeAssign();
this.expect(tt.bracketR); this.expect(tt.bracketR);
} else { } else {
// $FlowFixMe (ClassPrivateMember shouldn't be allowed to be computed!)
prop.computed = false; prop.computed = false;
const oldInPropertyName = this.state.inPropertyName; const oldInPropertyName = this.state.inPropertyName;
this.state.inPropertyName = true; this.state.inPropertyName = true;

View File

@ -639,9 +639,11 @@ export default class StatementParser extends ExpressionParser {
return this.match(tt.parenL); return this.match(tt.parenL);
} }
isNonstaticConstructor(method: N.ClassMethod): boolean { isNonstaticConstructor(method: N.ClassMethod | N.ClassProperty): boolean {
return !method.computed && !method.static && ( return !method.computed && !method.static && (
// $FlowFixMe ('key' downcasting)
(method.key.name === "constructor") || // Identifier (method.key.name === "constructor") || // Identifier
// $FlowFixMe ('key' downcasting)
(method.key.value === "constructor") // Literal (method.key.value === "constructor") // Literal
); );
} }
@ -652,7 +654,7 @@ export default class StatementParser extends ExpressionParser {
this.state.strict = true; this.state.strict = true;
this.state.inClass = true; this.state.inClass = true;
let hadConstructor = false; const state = { hadConstructor: false };
let decorators = []; let decorators = [];
const classBody = this.startNode(); const classBody = this.startNode();
@ -673,22 +675,43 @@ export default class StatementParser extends ExpressionParser {
continue; continue;
} }
const method = this.startNode(); const member = this.startNode();
// steal the decorators if there are any // steal the decorators if there are any
if (decorators.length) { if (decorators.length) {
method.decorators = decorators; member.decorators = decorators;
decorators = []; decorators = [];
} }
if (this.hasPlugin("classPrivateProperties") && this.match(tt.hash)) { // Private property this.parseClassMember(classBody, member, state);
this.next();
this.parsePropertyName(method);
classBody.body.push(this.parsePrivateClassProperty(method));
continue;
} }
method.static = false; if (decorators.length) {
this.raise(this.state.start, "You have trailing decorators with no method");
}
node.body = this.finishNode(classBody, "ClassBody");
this.state.inClass = false;
this.state.strict = oldStrict;
}
parseClassMember(classBody: N.ClassBody, member: N.ClassMember, state: { hadConstructor: boolean }): void {
// Use the appropriate variable to represent `member` once a more specific type is known.
const memberAny: any = member;
const methodOrProp: N.ClassMethod | N.ClassProperty = memberAny;
const method: N.ClassMethod = memberAny;
const prop: N.ClassProperty = memberAny;
if (this.hasPlugin("classPrivateProperties") && this.match(tt.hash)) { // Private property
this.next();
const privateProp: N.ClassPrivateProperty = memberAny;
this.parsePropertyName(privateProp);
classBody.body.push(this.parsePrivateClassProperty(privateProp));
return;
}
methodOrProp.static = false;
if (this.match(tt.name) && this.state.value === "static") { if (this.match(tt.name) && this.state.value === "static") {
const key = this.parseIdentifier(true); // eats 'static' const key = this.parseIdentifier(true); // eats 'static'
if (this.isClassMethod()) { if (this.isClassMethod()) {
@ -697,16 +720,16 @@ export default class StatementParser extends ExpressionParser {
method.computed = false; method.computed = false;
method.key = key; method.key = key;
this.parseClassMethod(classBody, method, false, false); this.parseClassMethod(classBody, method, false, false);
continue; return;
} else if (this.isClassProperty()) { } else if (this.isClassProperty()) {
// a property named 'static' // a property named 'static'
method.computed = false; prop.computed = false;
method.key = key; prop.key = key;
classBody.body.push(this.parseClassProperty(method)); classBody.body.push(this.parseClassProperty(prop));
continue; return;
} }
// otherwise something static // otherwise something static
method.static = true; methodOrProp.static = true;
} }
if (this.eat(tt.star)) { if (this.eat(tt.star)) {
@ -720,21 +743,24 @@ export default class StatementParser extends ExpressionParser {
this.raise(method.key.start, "Classes may not have static property named prototype"); this.raise(method.key.start, "Classes may not have static property named prototype");
} }
this.parseClassMethod(classBody, method, true, false); this.parseClassMethod(classBody, method, true, false);
} else { return;
}
const isSimple = this.match(tt.name); const isSimple = this.match(tt.name);
const key = this.parsePropertyName(method); const key = this.parsePropertyName(methodOrProp);
if (!method.computed && method.static && (method.key.name === "prototype" || method.key.value === "prototype")) { // $FlowFixMe ('key' downcasting)
this.raise(method.key.start, "Classes may not have static property named prototype"); if (!methodOrProp.computed && methodOrProp.static && (methodOrProp.key.name === "prototype" || methodOrProp.key.value === "prototype")) {
this.raise(methodOrProp.key.start, "Classes may not have static property named prototype");
} }
if (this.isClassMethod()) { if (this.isClassMethod()) {
// a normal method // a normal method
if (this.isNonstaticConstructor(method)) { if (this.isNonstaticConstructor(method)) {
if (hadConstructor) { if (state.hadConstructor) {
this.raise(key.start, "Duplicate constructor in the same class"); this.raise(key.start, "Duplicate constructor in the same class");
} else if (method.decorators) { } else if (method.decorators) {
this.raise(method.start, "You can't attach decorators to a class constructor"); this.raise(method.start, "You can't attach decorators to a class constructor");
} }
hadConstructor = true; state.hadConstructor = true;
method.kind = "constructor"; method.kind = "constructor";
} else { } else {
method.kind = "method"; method.kind = "method";
@ -742,10 +768,10 @@ export default class StatementParser extends ExpressionParser {
this.parseClassMethod(classBody, method, false, false); this.parseClassMethod(classBody, method, false, false);
} else if (this.isClassProperty()) { } else if (this.isClassProperty()) {
// a normal property // a normal property
if (this.isNonstaticConstructor(method)) { if (this.isNonstaticConstructor(prop)) {
this.raise(method.key.start, "Classes may not have a non-static field named 'constructor'"); this.raise(prop.key.start, "Classes may not have a non-static field named 'constructor'");
} }
classBody.body.push(this.parseClassProperty(method)); classBody.body.push(this.parseClassProperty(prop));
} else if (isSimple && key.name === "async" && !this.isLineTerminator()) { } else if (isSimple && key.name === "async" && !this.isLineTerminator()) {
// an async method // an async method
const isGenerator = this.hasPlugin("asyncGenerators") && this.eat(tt.star); const isGenerator = this.hasPlugin("asyncGenerators") && this.eat(tt.star);
@ -766,26 +792,14 @@ export default class StatementParser extends ExpressionParser {
this.checkGetterSetterParamCount(method); this.checkGetterSetterParamCount(method);
} else if (this.isLineTerminator()) { } else if (this.isLineTerminator()) {
// an uninitialized class property (due to ASI, since we don't otherwise recognize the next token) // an uninitialized class property (due to ASI, since we don't otherwise recognize the next token)
if (this.isNonstaticConstructor(method)) { if (this.isNonstaticConstructor(prop)) {
this.raise(method.key.start, "Classes may not have a non-static field named 'constructor'"); this.raise(prop.key.start, "Classes may not have a non-static field named 'constructor'");
} }
classBody.body.push(this.parseClassProperty(method)); classBody.body.push(this.parseClassProperty(prop));
} else { } else {
this.unexpected(); this.unexpected();
} }
} }
}
if (decorators.length) {
this.raise(this.state.start, "You have trailing decorators with no method");
}
node.body = this.finishNode(classBody, "ClassBody");
this.state.inClass = false;
this.state.strict = oldStrict;
}
parsePrivateClassProperty(node: N.ClassPrivateProperty): N.ClassPrivateProperty { parsePrivateClassProperty(node: N.ClassPrivateProperty): N.ClassPrivateProperty {
this.state.inClassProperty = true; this.state.inClassProperty = true;

View File

@ -59,8 +59,9 @@ export default (superClass: Class<Parser>): Class<Parser> => class extends super
} }
} }
checkGetterSetterParamCount(prop: N.ObjectMethod): void { checkGetterSetterParamCount(prop: N.ObjectMethod | N.ClassMethod): void {
const paramCount = prop.kind === "get" ? 0 : 1; const paramCount = prop.kind === "get" ? 0 : 1;
// $FlowFixMe (prop.value present for ObjectMethod, but for ClassMethod should use prop.params?)
if (prop.value.params.length !== paramCount) { if (prop.value.params.length !== paramCount) {
const start = prop.start; const start = prop.start;
if (prop.kind === "get") { if (prop.kind === "get") {

View File

@ -1242,7 +1242,7 @@ export default (superClass: Class<Parser>): Class<Parser> => class extends super
return this.match(tt.colon) || super.isClassProperty(); return this.match(tt.colon) || super.isClassProperty();
} }
isNonstaticConstructor(method: N.ClassMethod): boolean { isNonstaticConstructor(method: N.ClassMethod | N.ClassProperty): boolean {
return !this.match(tt.colon) && super.isNonstaticConstructor(method); return !this.match(tt.colon) && super.isNonstaticConstructor(method);
} }
@ -1281,9 +1281,10 @@ export default (superClass: Class<Parser>): Class<Parser> => class extends super
} }
} }
parsePropertyName(node: N.ObjectMember): N.Identifier { parsePropertyName(node: N.ObjectOrClassMember): N.Identifier {
const variance = this.flowParseVariance(); const variance = this.flowParseVariance();
const key = super.parsePropertyName(node); const key = super.parsePropertyName(node);
// $FlowFixMe (variance not defined on ClassPrivateProperty)
node.variance = variance; node.variance = variance;
return key; return key;
} }