Skip to content

Commit

Permalink
Refactor for classFields:
Browse files Browse the repository at this point in the history
- Fixes babel#540
- Allow decorators for comma separated fields
- Added tests
  • Loading branch information
Diego committed Jul 2, 2017
1 parent 5e1e949 commit 8dcb23b
Show file tree
Hide file tree
Showing 26 changed files with 2,013 additions and 17 deletions.
74 changes: 60 additions & 14 deletions src/parser/statement.js
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,12 @@ export default class StatementParser extends ExpressionParser {
}

isClassProperty(): boolean {
return this.match(tt.eq) || this.match(tt.semi) || this.match(tt.braceR);
return (
this.match(tt.eq) ||
this.match(tt.semi) ||
this.match(tt.braceR) ||
this.match(tt.comma)
);
}

isClassMethod(): boolean {
Expand All @@ -849,7 +854,10 @@ export default class StatementParser extends ExpressionParser {
this.state.strict = true;
this.state.classLevel++;

const state = { hadConstructor: false };
const context = {
hadConstructor: false,
inCommaSeparatedClassFields: false,
};
let decorators: N.Decorator[] = [];
const classBody: N.ClassBody = this.startNode();

Expand All @@ -859,13 +867,18 @@ export default class StatementParser extends ExpressionParser {

while (!this.eat(tt.braceR)) {
if (this.eat(tt.semi)) {
if (context.inCommaSeparatedClassFields) {
// class { foo,; }
this.unexpected(this.state.lastTokEnd);
}

if (decorators.length > 0) {
this.raise(
this.state.lastTokEnd,
"Decorators must not be followed by a semicolon",
);
}
continue;
continue; // Go up to keep consuming `;`
}

if (this.match(tt.at)) {
Expand All @@ -877,14 +890,17 @@ export default class StatementParser extends ExpressionParser {

// steal the decorators if there are any
if (decorators.length) {
member.decorators = decorators;
if (this.hasPlugin("decorators2")) {
member.decorators = decorators.slice();
// Reset only once in case we have comma separated fields
if (
this.hasPlugin("decorators2") &&
!context.inCommaSeparatedClassFields
) {
this.resetStartLocationFromNode(member, decorators[0]);
}
decorators = [];
}

this.parseClassMember(classBody, member, state);
this.parseClassMember(classBody, member, context);

if (
this.hasPlugin("decorators2") &&
Expand All @@ -897,6 +913,20 @@ export default class StatementParser extends ExpressionParser {
"Stage 2 decorators may only be used with a class or a class method",
);
}

// Weak check for classFields
if (member.kind == null) {
context.inCommaSeparatedClassFields = this.eat(tt.comma);
}

if (context.inCommaSeparatedClassFields) {
// Check for dangling commas class Foo { x, }
if (this.match(tt.braceR)) {
this.unexpected(this.state.lastTokStart);
}
} else {
decorators = [];
}
}

if (decorators.length) {
Expand All @@ -915,7 +945,7 @@ export default class StatementParser extends ExpressionParser {
parseClassMember(
classBody: N.ClassBody,
member: N.ClassMember,
state: { hadConstructor: boolean },
context: { hadConstructor: boolean, inCommaSeparatedClassFields: boolean },
): void {
// Use the appropriate variable to represent `member` once a more specific type is known.
const memberAny: any = member;
Expand All @@ -935,6 +965,9 @@ export default class StatementParser extends ExpressionParser {
if (this.match(tt.name) && this.state.value === "static") {
const key = this.parseIdentifier(true); // eats 'static'
if (this.isClassMethod()) {
if (context.inCommaSeparatedClassFields) {
this.unexpected();
}
// a method named 'static'
method.kind = "method";
method.computed = false;
Expand All @@ -960,13 +993,13 @@ export default class StatementParser extends ExpressionParser {
isStatic = true;
}

this.parseClassMemberWithIsStatic(classBody, member, state, isStatic);
this.parseClassMemberWithIsStatic(classBody, member, context, isStatic);
}

parseClassMemberWithIsStatic(
classBody: N.ClassBody,
member: N.ClassMember,
state: { hadConstructor: boolean },
context: { hadConstructor: boolean, inCommaSeparatedClassFields: boolean },
isStatic: boolean,
) {
const memberAny: any = member;
Expand Down Expand Up @@ -1009,6 +1042,9 @@ export default class StatementParser extends ExpressionParser {
this.parsePostMemberNameModifiers(methodOrProp);

if (this.isClassMethod()) {
if (context.inCommaSeparatedClassFields) {
this.unexpected();
}
// a normal method
const isConstructor = this.isNonstaticConstructor(method);
if (isConstructor) {
Expand All @@ -1026,10 +1062,10 @@ export default class StatementParser extends ExpressionParser {
}

// TypeScript allows multiple overloaded constructor declarations.
if (state.hadConstructor && !this.hasPlugin("typescript")) {
if (context.hadConstructor && !this.hasPlugin("typescript")) {
this.raise(key.start, "Duplicate constructor in the same class");
}
state.hadConstructor = true;
context.hadConstructor = true;
}

this.parseClassMethod(classBody, method, false, false, isConstructor);
Expand Down Expand Up @@ -1135,8 +1171,10 @@ export default class StatementParser extends ExpressionParser {
} else {
node.value = null;
}
this.semicolon();

this.state.inClassProperty = false;

this.parseMaybeCommaSeparator();
return this.finishNode(node, "ClassPrivateProperty");
}

Expand All @@ -1158,8 +1196,10 @@ export default class StatementParser extends ExpressionParser {
} else {
node.value = null;
}
this.semicolon();

this.state.inClassProperty = false;

this.parseMaybeCommaSeparator();
return this.finishNode(node, "ClassProperty");
}

Expand All @@ -1181,6 +1221,12 @@ export default class StatementParser extends ExpressionParser {
);
}

parseMaybeCommaSeparator() {
if (!this.match(tt.comma)) {
this.semicolon();
}
}

parseClassId(
node: N.Class,
isStatement: boolean,
Expand Down
9 changes: 6 additions & 3 deletions src/plugins/typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -1421,18 +1421,21 @@ export default (superClass: Class<Parser>): Class<Parser> =>
parseClassMember(
classBody: N.ClassBody,
member: any,
state: { hadConstructor: boolean },
context: {
hadConstructor: boolean,
inCommaSeparatedClassFields: boolean,
},
): void {
const accessibility = this.parseAccessModifier();
if (accessibility) member.accessibility = accessibility;

super.parseClassMember(classBody, member, state);
super.parseClassMember(classBody, member, context);
}

parseClassMemberWithIsStatic(
classBody: N.ClassBody,
member: any,
state: { hadConstructor: boolean },
state: { hadConstructor: boolean, inCommaSeparatedClassFields: boolean },
isStatic: boolean,
): void {
const methodOrProp: N.ClassMethod | N.ClassProperty = member;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class C {
#p1,#p2;
#p3 = 1, #p4 = 1;
#p5 = 1, #p6, #p7;
}
Loading

0 comments on commit 8dcb23b

Please sign in to comment.