From c4e1b898f2e8491e5614efe7e4816fef5c80441a Mon Sep 17 00:00:00 2001 From: ANDO Yasushi Date: Tue, 19 Apr 2022 00:09:11 +0900 Subject: [PATCH 1/4] fix: class extends itself #2201 --- src/diagnosticMessages.json | 1 + src/program.ts | 9 +++++++++ tests/compiler/class-extends-itself.json | 7 +++++++ tests/compiler/class-extends-itself.ts | 3 +++ 4 files changed, 20 insertions(+) create mode 100644 tests/compiler/class-extends-itself.json create mode 100644 tests/compiler/class-extends-itself.ts diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 5c9a03e6f2..b4c808cbde 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -46,6 +46,7 @@ "Property '{0}' is always assigned before being used.": 233, "Expression does not compile to a value at runtime.": 234, "Only variables, functions and enums become WebAssembly module exports.": 235, + "Circular inheritance": 236, "Importing the table disables some indirect call optimizations.": 901, "Exporting the table disables some indirect call optimizations.": 902, diff --git a/src/program.ts b/src/program.ts index 4e28ba2478..89d4eabfef 100644 --- a/src/program.ts +++ b/src/program.ts @@ -1442,6 +1442,7 @@ export class Program extends DiagnosticEmitter { // TODO: make this work with interfaaces as well var thisInstanceMembers = thisPrototype.instanceMembers; if (thisInstanceMembers) { + const prototypesSeen = new Set(); do { let baseInstanceMembers = basePrototype.instanceMembers; if (baseInstanceMembers) { @@ -1526,8 +1527,16 @@ export class Program extends DiagnosticEmitter { } } } + prototypesSeen.add(basePrototype); let nextPrototype = basePrototype.basePrototype; if (!nextPrototype) break; + if (prototypesSeen.has(nextPrototype)) { + this.error( + DiagnosticCode.Circular_inheritance, + nextPrototype.identifierNode.range, + ); + break; + } basePrototype = nextPrototype; } while (true); } diff --git a/tests/compiler/class-extends-itself.json b/tests/compiler/class-extends-itself.json new file mode 100644 index 0000000000..4238614450 --- /dev/null +++ b/tests/compiler/class-extends-itself.json @@ -0,0 +1,7 @@ +{ + "asc_flags": [ + ], + "stderr": [ + "AS236: Circular inheritance" + ] +} diff --git a/tests/compiler/class-extends-itself.ts b/tests/compiler/class-extends-itself.ts new file mode 100644 index 0000000000..8ea42a65f0 --- /dev/null +++ b/tests/compiler/class-extends-itself.ts @@ -0,0 +1,3 @@ +class Foo extends Foo { + bar(): void {} +} \ No newline at end of file From 94da48ba9cb4174efc74455915f93ff451899597 Mon Sep 17 00:00:00 2001 From: ANDO Yasushi Date: Tue, 19 Apr 2022 00:14:25 +0900 Subject: [PATCH 2/4] indent --- tests/compiler/class-extends-itself.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compiler/class-extends-itself.ts b/tests/compiler/class-extends-itself.ts index 8ea42a65f0..5dee8053d1 100644 --- a/tests/compiler/class-extends-itself.ts +++ b/tests/compiler/class-extends-itself.ts @@ -1,3 +1,3 @@ class Foo extends Foo { - bar(): void {} + bar(): void {} } \ No newline at end of file From 26537efcf36de717aa577561863ed247187198cd Mon Sep 17 00:00:00 2001 From: ANDO Yasushi Date: Tue, 19 Apr 2022 12:14:29 +0900 Subject: [PATCH 3/4] throw TS2506 --- src/diagnosticMessages.json | 1 - src/program.ts | 3 ++- tests/compiler/class-extends-itself.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index b4c808cbde..5c9a03e6f2 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -46,7 +46,6 @@ "Property '{0}' is always assigned before being used.": 233, "Expression does not compile to a value at runtime.": 234, "Only variables, functions and enums become WebAssembly module exports.": 235, - "Circular inheritance": 236, "Importing the table disables some indirect call optimizations.": 901, "Exporting the table disables some indirect call optimizations.": 902, diff --git a/src/program.ts b/src/program.ts index 89d4eabfef..1e88a83c40 100644 --- a/src/program.ts +++ b/src/program.ts @@ -1532,8 +1532,9 @@ export class Program extends DiagnosticEmitter { if (!nextPrototype) break; if (prototypesSeen.has(nextPrototype)) { this.error( - DiagnosticCode.Circular_inheritance, + DiagnosticCode._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, nextPrototype.identifierNode.range, + nextPrototype.identifierNode.text, ); break; } diff --git a/tests/compiler/class-extends-itself.json b/tests/compiler/class-extends-itself.json index 4238614450..088643f147 100644 --- a/tests/compiler/class-extends-itself.json +++ b/tests/compiler/class-extends-itself.json @@ -2,6 +2,6 @@ "asc_flags": [ ], "stderr": [ - "AS236: Circular inheritance" + "TS2506: 'Foo' is referenced directly or indirectly in its own base expression." ] } From c38f2d4b279677ce3b9a74dd8f6eb4e44fe41ed4 Mon Sep 17 00:00:00 2001 From: ANDO Yasushi Date: Fri, 29 Apr 2022 00:37:04 +0900 Subject: [PATCH 4/4] check basePrototype before setting --- src/program.ts | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/program.ts b/src/program.ts index 1e88a83c40..9efdf63be4 100644 --- a/src/program.ts +++ b/src/program.ts @@ -1309,7 +1309,15 @@ export class Program extends DiagnosticEmitter { Range.join(thisPrototype.identifierNode.range, extendsNode.range) ); } - thisPrototype.basePrototype = basePrototype; + if (!thisPrototype.extends(basePrototype)) { + thisPrototype.basePrototype = basePrototype; + } else { + this.error( + DiagnosticCode._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, + basePrototype.identifierNode.range, + basePrototype.identifierNode.text, + ); + } } else { this.error( DiagnosticCode.A_class_may_only_extend_another_class, @@ -1318,7 +1326,16 @@ export class Program extends DiagnosticEmitter { } } else if (thisPrototype.kind == ElementKind.INTERFACE_PROTOTYPE) { if (baseElement.kind == ElementKind.INTERFACE_PROTOTYPE) { - thisPrototype.basePrototype = baseElement; + const basePrototype = baseElement; + if (!thisPrototype.extends(basePrototype)) { + thisPrototype.basePrototype = basePrototype; + } else { + this.error( + DiagnosticCode._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, + basePrototype.identifierNode.range, + basePrototype.identifierNode.text, + ); + } } else { this.error( DiagnosticCode.An_interface_can_only_extend_an_interface, @@ -1442,7 +1459,6 @@ export class Program extends DiagnosticEmitter { // TODO: make this work with interfaaces as well var thisInstanceMembers = thisPrototype.instanceMembers; if (thisInstanceMembers) { - const prototypesSeen = new Set(); do { let baseInstanceMembers = basePrototype.instanceMembers; if (baseInstanceMembers) { @@ -1527,17 +1543,8 @@ export class Program extends DiagnosticEmitter { } } } - prototypesSeen.add(basePrototype); let nextPrototype = basePrototype.basePrototype; if (!nextPrototype) break; - if (prototypesSeen.has(nextPrototype)) { - this.error( - DiagnosticCode._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, - nextPrototype.identifierNode.range, - nextPrototype.identifierNode.text, - ); - break; - } basePrototype = nextPrototype; } while (true); }