Skip to content

Commit

Permalink
(ts) Throw for abstract methods in a non-abstract class (#12686)
Browse files Browse the repository at this point in the history
  • Loading branch information
sosukesuzuki committed Jan 27, 2021
1 parent 463cb33 commit 45fdde0
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 0 deletions.
16 changes: 16 additions & 0 deletions packages/babel-parser/src/plugins/typescript/index.js
Expand Up @@ -87,6 +87,8 @@ const TSErrors = Object.freeze({
"Tuple members must be labeled with a simple identifier.",
MixedLabeledAndUnlabeledElements:
"Tuple members must all have names or all not have names.",
NonAbstractClassHasAbstractMethod:
"Abstract methods can only appear within an abstract class.",
OptionalTypeBeforeRequired:
"A required element cannot follow an optional element.",
PatternIsOptional:
Expand Down Expand Up @@ -2172,6 +2174,10 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return;
}

if (!this.state.inAbstractClass && (member: any).abstract) {
this.raise(member.start, TSErrors.NonAbstractClassHasAbstractMethod);
}

/*:: invariant(member.type !== "TSIndexSignature") */

super.parseClassMemberWithIsStatic(classBody, member, state, isStatic);
Expand Down Expand Up @@ -2835,4 +2841,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.state.isDeclareContext = oldIsDeclareContext;
}
}

parseClass<T: N.Class>(node: T, ...args: any[]): T {
const oldInAbstractClass = this.state.inAbstractClass;
this.state.inAbstractClass = !!(node: any).abstract;
try {
return super.parseClass(node, ...args);
} finally {
this.state.inAbstractClass = oldInAbstractClass;
}
}
};
1 change: 1 addition & 0 deletions packages/babel-parser/src/tokenizer/state.js
Expand Up @@ -65,6 +65,7 @@ export default class State {
hasFlowComment: boolean = false;
isIterator: boolean = false;
isDeclareContext: boolean = false;
inAbstractClass: boolean = false;

// For the smartPipelines plugin:
topicContext: TopicContextState = {
Expand Down
@@ -0,0 +1,3 @@
class Foo {
abstract method();
}
@@ -0,0 +1,49 @@
{
"type": "File",
"start":0,"end":34,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Abstract methods can only appear within an abstract class. (2:2)"
],
"program": {
"type": "Program",
"start":0,"end":34,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":34,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":34,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
"body": [
{
"type": "TSDeclareMethod",
"start":14,"end":32,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":20}},
"abstract": true,
"static": false,
"key": {
"type": "Identifier",
"start":23,"end":29,"loc":{"start":{"line":2,"column":11},"end":{"line":2,"column":17},"identifierName":"method"},
"name": "method"
},
"computed": false,
"kind": "method",
"id": null,
"generator": false,
"async": false,
"params": []
}
]
}
}
],
"directives": []
}
}
@@ -0,0 +1,7 @@
abstract class Foo {
method() {
return class {
abstract m();
}
}
}
@@ -0,0 +1,89 @@
{
"type": "File",
"start":0,"end":84,"loc":{"start":{"line":1,"column":0},"end":{"line":7,"column":1}},
"errors": [
"SyntaxError: Abstract methods can only appear within an abstract class. (4:6)"
],
"program": {
"type": "Program",
"start":0,"end":84,"loc":{"start":{"line":1,"column":0},"end":{"line":7,"column":1}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":84,"loc":{"start":{"line":1,"column":0},"end":{"line":7,"column":1}},
"abstract": true,
"id": {
"type": "Identifier",
"start":15,"end":18,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":18},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":19,"end":84,"loc":{"start":{"line":1,"column":19},"end":{"line":7,"column":1}},
"body": [
{
"type": "ClassMethod",
"start":23,"end":82,"loc":{"start":{"line":2,"column":2},"end":{"line":6,"column":3}},
"static": false,
"key": {
"type": "Identifier",
"start":23,"end":29,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":8},"identifierName":"method"},
"name": "method"
},
"computed": false,
"kind": "method",
"id": null,
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start":32,"end":82,"loc":{"start":{"line":2,"column":11},"end":{"line":6,"column":3}},
"body": [
{
"type": "ReturnStatement",
"start":38,"end":78,"loc":{"start":{"line":3,"column":4},"end":{"line":5,"column":5}},
"argument": {
"type": "ClassExpression",
"start":45,"end":78,"loc":{"start":{"line":3,"column":11},"end":{"line":5,"column":5}},
"id": null,
"superClass": null,
"body": {
"type": "ClassBody",
"start":51,"end":78,"loc":{"start":{"line":3,"column":17},"end":{"line":5,"column":5}},
"body": [
{
"type": "TSDeclareMethod",
"start":59,"end":72,"loc":{"start":{"line":4,"column":6},"end":{"line":4,"column":19}},
"abstract": true,
"static": false,
"key": {
"type": "Identifier",
"start":68,"end":69,"loc":{"start":{"line":4,"column":15},"end":{"line":4,"column":16},"identifierName":"m"},
"name": "m"
},
"computed": false,
"kind": "method",
"id": null,
"generator": false,
"async": false,
"params": []
}
]
}
}
}
],
"directives": []
}
}
]
}
}
],
"directives": []
}
}
@@ -0,0 +1 @@
abstract class C { p = class { abstract method() } }
@@ -0,0 +1,73 @@
{
"type": "File",
"start":0,"end":52,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":52}},
"errors": [
"SyntaxError: Abstract methods can only appear within an abstract class. (1:31)"
],
"program": {
"type": "Program",
"start":0,"end":52,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":52}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":52,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":52}},
"abstract": true,
"id": {
"type": "Identifier",
"start":15,"end":16,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":16},"identifierName":"C"},
"name": "C"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":17,"end":52,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":52}},
"body": [
{
"type": "ClassProperty",
"start":19,"end":50,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":50}},
"static": false,
"key": {
"type": "Identifier",
"start":19,"end":20,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":20},"identifierName":"p"},
"name": "p"
},
"computed": false,
"value": {
"type": "ClassExpression",
"start":23,"end":50,"loc":{"start":{"line":1,"column":23},"end":{"line":1,"column":50}},
"id": null,
"superClass": null,
"body": {
"type": "ClassBody",
"start":29,"end":50,"loc":{"start":{"line":1,"column":29},"end":{"line":1,"column":50}},
"body": [
{
"type": "TSDeclareMethod",
"start":31,"end":48,"loc":{"start":{"line":1,"column":31},"end":{"line":1,"column":48}},
"abstract": true,
"static": false,
"key": {
"type": "Identifier",
"start":40,"end":46,"loc":{"start":{"line":1,"column":40},"end":{"line":1,"column":46},"identifierName":"method"},
"name": "method"
},
"computed": false,
"kind": "method",
"id": null,
"generator": false,
"async": false,
"params": []
}
]
}
}
}
]
}
}
],
"directives": []
}
}
@@ -1,6 +1,9 @@
{
"type": "File",
"start":0,"end":139,"loc":{"start":{"line":1,"column":0},"end":{"line":9,"column":1}},
"errors": [
"SyntaxError: Abstract methods can only appear within an abstract class. (5:2)"
],
"program": {
"type": "Program",
"start":0,"end":139,"loc":{"start":{"line":1,"column":0},"end":{"line":9,"column":1}},
Expand Down
2 changes: 2 additions & 0 deletions scripts/parser-tests/typescript/allowlist.txt
Expand Up @@ -4,6 +4,7 @@ ParameterList13.ts
ParameterList4.ts
ParameterList5.ts
ParameterList6.ts
abstractPropertyNegative.ts
accessorParameterAccessibilityModifier.ts
accessorWithoutBody1.ts
accessorWithoutBody2.ts
Expand All @@ -26,6 +27,7 @@ anonClassDeclarationEmitIsAnon.ts
anyDeclare.ts
argumentsBindsToFunctionScopeArgumentList.ts
arrayOfExportedClass.ts
asiAbstract.ts
asyncFunctionsAcrossFiles.ts
augmentExportEquals1.ts
augmentExportEquals1_1.ts
Expand Down

0 comments on commit 45fdde0

Please sign in to comment.