babel-parser(ts): Raise recoverable error for abstract interface (#12771)

* Support parsing abstract interface

* Address review

Address reviews

Address reviews

* Fix types

* Add hasFollowingLineBreak
This commit is contained in:
Sosuke Suzuki
2021-02-09 17:56:18 +09:00
committed by GitHub
parent 4778e32570
commit d242ea04c8
8 changed files with 190 additions and 17 deletions

View File

@@ -96,6 +96,12 @@ export default class UtilParser extends Tokenizer {
);
}
hasFollowingLineBreak(): boolean {
return lineBreak.test(
this.input.slice(this.state.end, this.nextTokenStart()),
);
}
// TODO
isLineTerminator(): boolean {

View File

@@ -89,6 +89,8 @@ const TSErrors = Object.freeze({
"Tuple members must all have names or all not have names.",
NonAbstractClassHasAbstractMethod:
"Abstract methods can only appear within an abstract class.",
NonClassMethodPropertyHasAbstractModifer:
"'abstract' modifier can only appear on a class, method, or property declaration.",
OptionalTypeBeforeRequired:
"A required element cannot follow an optional element.",
PatternIsOptional:
@@ -1585,20 +1587,13 @@ export default (superClass: Class<Parser>): Class<Parser> =>
): ?N.Declaration {
switch (value) {
case "abstract":
if (this.tsCheckLineTerminatorAndMatch(tt._class, next)) {
const cls: N.ClassDeclaration = node;
cls.abstract = true;
if (next) {
this.next();
if (!this.match(tt._class)) {
this.unexpected(null, tt._class);
}
}
return this.parseClass(
cls,
/* isStatement */ true,
/* optionalId */ false,
);
if (
this.tsCheckLineTerminatorAndMatch(tt._class, next) ||
// for interface
this.tsCheckLineTerminatorAndMatch(tt.name, next)
) {
if (next) this.next();
return this.tsParseAbstractDeclaration(node);
}
break;
@@ -2849,4 +2844,36 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.state.inAbstractClass = oldInAbstractClass;
}
}
tsParseAbstractDeclaration(
node: any,
): N.ClassDeclaration | N.TsInterfaceDeclaration | typeof undefined {
if (this.match(tt._class)) {
node.abstract = true;
return this.parseClass<N.ClassDeclaration>(
(node: N.ClassDeclaration),
/* isStatement */ true,
/* optionalId */ false,
);
} else if (this.isContextual("interface")) {
// for invalid abstract interface
// To avoid
// abstract interface
// Foo {}
if (!this.hasFollowingLineBreak()) {
node.abstract = true;
this.raise(
node.start,
TSErrors.NonClassMethodPropertyHasAbstractModifer,
);
this.next();
return this.tsParseInterfaceDeclaration(
(node: N.TsInterfaceDeclaration),
);
}
} else {
this.unexpected(null, tt._class);
}
}
};