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:
parent
4778e32570
commit
d242ea04c8
@ -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 {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
3
packages/babel-parser/test/fixtures/typescript/interface/abstract/input.ts
vendored
Normal file
3
packages/babel-parser/test/fixtures/typescript/interface/abstract/input.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
abstract interface Foo {
|
||||
foo: string;
|
||||
}
|
||||
50
packages/babel-parser/test/fixtures/typescript/interface/abstract/output.json
vendored
Normal file
50
packages/babel-parser/test/fixtures/typescript/interface/abstract/output.json
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"type": "File",
|
||||
"start":0,"end":41,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
|
||||
"errors": [
|
||||
"SyntaxError: 'abstract' modifier can only appear on a class, method, or property declaration. (1:0)"
|
||||
],
|
||||
"program": {
|
||||
"type": "Program",
|
||||
"start":0,"end":41,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
|
||||
"sourceType": "module",
|
||||
"interpreter": null,
|
||||
"body": [
|
||||
{
|
||||
"type": "TSInterfaceDeclaration",
|
||||
"start":0,"end":41,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
|
||||
"abstract": true,
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"start":19,"end":22,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":22},"identifierName":"Foo"},
|
||||
"name": "Foo"
|
||||
},
|
||||
"body": {
|
||||
"type": "TSInterfaceBody",
|
||||
"start":23,"end":41,"loc":{"start":{"line":1,"column":23},"end":{"line":3,"column":1}},
|
||||
"body": [
|
||||
{
|
||||
"type": "TSPropertySignature",
|
||||
"start":27,"end":39,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":14}},
|
||||
"key": {
|
||||
"type": "Identifier",
|
||||
"start":27,"end":30,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":5},"identifierName":"foo"},
|
||||
"name": "foo"
|
||||
},
|
||||
"computed": false,
|
||||
"typeAnnotation": {
|
||||
"type": "TSTypeAnnotation",
|
||||
"start":30,"end":38,"loc":{"start":{"line":2,"column":5},"end":{"line":2,"column":13}},
|
||||
"typeAnnotation": {
|
||||
"type": "TSStringKeyword",
|
||||
"start":32,"end":38,"loc":{"start":{"line":2,"column":7},"end":{"line":2,"column":13}}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"directives": []
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"throws": "Unexpected token, expected \"class\" (1:16)"
|
||||
}
|
||||
38
packages/babel-parser/test/fixtures/typescript/interface/export-abstract-interface/output.json
vendored
Normal file
38
packages/babel-parser/test/fixtures/typescript/interface/export-abstract-interface/output.json
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"type": "File",
|
||||
"start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
|
||||
"errors": [
|
||||
"SyntaxError: 'abstract' modifier can only appear on a class, method, or property declaration. (1:7)"
|
||||
],
|
||||
"program": {
|
||||
"type": "Program",
|
||||
"start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
|
||||
"sourceType": "module",
|
||||
"interpreter": null,
|
||||
"body": [
|
||||
{
|
||||
"type": "ExportNamedDeclaration",
|
||||
"start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
|
||||
"exportKind": "type",
|
||||
"specifiers": [],
|
||||
"source": null,
|
||||
"declaration": {
|
||||
"type": "TSInterfaceDeclaration",
|
||||
"start":7,"end":32,"loc":{"start":{"line":1,"column":7},"end":{"line":3,"column":1}},
|
||||
"abstract": true,
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"start":26,"end":27,"loc":{"start":{"line":1,"column":26},"end":{"line":1,"column":27},"identifierName":"I"},
|
||||
"name": "I"
|
||||
},
|
||||
"body": {
|
||||
"type": "TSInterfaceBody",
|
||||
"start":28,"end":32,"loc":{"start":{"line":1,"column":28},"end":{"line":3,"column":1}},
|
||||
"body": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"directives": []
|
||||
}
|
||||
}
|
||||
2
packages/babel-parser/test/fixtures/typescript/interface/invalid-abstract-interface/input.ts
vendored
Normal file
2
packages/babel-parser/test/fixtures/typescript/interface/invalid-abstract-interface/input.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
abstract interface
|
||||
Foo {}
|
||||
50
packages/babel-parser/test/fixtures/typescript/interface/invalid-abstract-interface/output.json
vendored
Normal file
50
packages/babel-parser/test/fixtures/typescript/interface/invalid-abstract-interface/output.json
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"type": "File",
|
||||
"start":0,"end":25,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":6}},
|
||||
"errors": [
|
||||
"SyntaxError: Missing semicolon (1:8)",
|
||||
"SyntaxError: Missing semicolon (2:3)"
|
||||
],
|
||||
"program": {
|
||||
"type": "Program",
|
||||
"start":0,"end":25,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":6}},
|
||||
"sourceType": "module",
|
||||
"interpreter": null,
|
||||
"body": [
|
||||
{
|
||||
"type": "ExpressionStatement",
|
||||
"start":0,"end":8,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":8}},
|
||||
"expression": {
|
||||
"type": "Identifier",
|
||||
"start":0,"end":8,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":8},"identifierName":"abstract"},
|
||||
"name": "abstract"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "ExpressionStatement",
|
||||
"start":9,"end":18,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":18}},
|
||||
"expression": {
|
||||
"type": "Identifier",
|
||||
"start":9,"end":18,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":18},"identifierName":"interface"},
|
||||
"name": "interface"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "ExpressionStatement",
|
||||
"start":19,"end":22,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":3}},
|
||||
"expression": {
|
||||
"type": "Identifier",
|
||||
"start":19,"end":22,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":3},"identifierName":"Foo"},
|
||||
"name": "Foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "BlockStatement",
|
||||
"start":23,"end":25,"loc":{"start":{"line":2,"column":4},"end":{"line":2,"column":6}},
|
||||
"body": [],
|
||||
"directives": []
|
||||
}
|
||||
],
|
||||
"directives": []
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user