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