From f250c821b691844ac2c088df3c71701b6ab8fe46 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sun, 10 Jul 2022 18:21:38 +0800 Subject: [PATCH 1/4] feat: add override in parser --- src/common.ts | 37 +++++++++++++++++--------------- src/parser.ts | 23 ++++++++++++++++++++ src/tokenizer.ts | 2 ++ tests/parser/class.ts | 3 +++ tests/parser/class.ts.fixture.ts | 2 ++ 5 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/common.ts b/src/common.ts index c48f5e7005..3ae02ece8f 100644 --- a/src/common.ts +++ b/src/common.ts @@ -36,49 +36,52 @@ export enum CommonFlags { GET = 1 << 11, /** Has a `set` modifier. */ SET = 1 << 12, + /** Has a `override` modifier. */ + OVERRIDE = 1 << 13, + /** Has a definite assignment assertion `!` as in `x!: i32;`. */ - DEFINITELY_ASSIGNED = 1 << 13, + DEFINITELY_ASSIGNED = 1 << 14, // Extended modifiers usually derived from basic modifiers /** Is ambient, that is either declared or nested in a declared element. */ - AMBIENT = 1 << 14, + AMBIENT = 1 << 15, /** Is generic. */ - GENERIC = 1 << 15, + GENERIC = 1 << 16, /** Is part of a generic context. */ - GENERIC_CONTEXT = 1 << 16, + GENERIC_CONTEXT = 1 << 17, /** Is an instance member. */ - INSTANCE = 1 << 17, + INSTANCE = 1 << 18, /** Is a constructor. */ - CONSTRUCTOR = 1 << 18, + CONSTRUCTOR = 1 << 19, /** Is a module export. */ - MODULE_EXPORT = 1 << 19, + MODULE_EXPORT = 1 << 20, /** Is a module import. */ - MODULE_IMPORT = 1 << 20, + MODULE_IMPORT = 1 << 21, // Compilation states /** Is resolved. */ - RESOLVED = 1 << 21, + RESOLVED = 1 << 22, /** Is compiled. */ - COMPILED = 1 << 22, + COMPILED = 1 << 23, /** Did error. */ - ERRORED = 1 << 23, + ERRORED = 1 << 24, /** Has a constant value and is therefore inlined. */ - INLINED = 1 << 24, + INLINED = 1 << 25, /** Is scoped. */ - SCOPED = 1 << 25, + SCOPED = 1 << 26, /** Is a stub. */ - STUB = 1 << 26, + STUB = 1 << 27, /** Is a virtual method. */ - VIRTUAL = 1 << 27, + VIRTUAL = 1 << 28, /** Is (part of) a closure. */ - CLOSURE = 1 << 28, + CLOSURE = 1 << 29, // Other /** Is quoted. */ - QUOTED = 1 << 29 + QUOTED = 1 << 30 } /** Path delimiter inserted between file system levels. */ diff --git a/src/parser.ts b/src/parser.ts index 664435260e..3155869fbf 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -1862,6 +1862,7 @@ export class Parser extends DiagnosticEmitter { // 'declare'? // ('public' | 'private' | 'protected')? // ('static' | 'abstract')? + // 'override'? // 'readonly'? // ('get' | 'set')? // Identifier ... @@ -1991,6 +1992,22 @@ export class Parser extends DiagnosticEmitter { if (parent.flags & CommonFlags.GENERIC) flags |= CommonFlags.GENERIC_CONTEXT; } + var overrideStart = 0; + var overrideEnd = 0; + if (tn.skip(Token.OVERRIDE)) { + if (isInterface || parent.extendsType == null) { + this.error( + DiagnosticCode._0_modifier_cannot_be_used_here, + tn.range(), "override" + ); + } else { + flags |= CommonFlags.OVERRIDE; + overrideStart = tn.tokenPos; + overrideEnd = tn.pos; + } + if (!startPos) startPos = tn.tokenPos; + } + var readonlyStart = 0; var readonlyEnd = 0; if (tn.peek() == Token.READONLY) { @@ -2104,6 +2121,12 @@ export class Parser extends DiagnosticEmitter { tn.range(staticStart, staticEnd), "static" ); // recoverable } + if (flags & CommonFlags.OVERRIDE) { + this.error( + DiagnosticCode._0_modifier_cannot_be_used_here, + tn.range(overrideStart, overrideEnd), "override" + ); + } if (flags & CommonFlags.ABSTRACT) { this.error( DiagnosticCode._0_modifier_cannot_be_used_here, diff --git a/src/tokenizer.ts b/src/tokenizer.ts index fab060fd5c..8d1cefcde5 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -80,6 +80,7 @@ export enum Token { NEW, // ES2017 NULL, // ES OF, + OVERRIDE, PACKAGE, // ES2017 non-lexical PRIVATE, // ES2017 non-lexical PROTECTED, // ES2017 non-lexical @@ -291,6 +292,7 @@ export function tokenFromKeyword(text: string): Token { } case CharCode.o: { if (text == "of") return Token.OF; + if (text == "override") return Token.OVERRIDE; break; } case CharCode.p: { diff --git a/tests/parser/class.ts b/tests/parser/class.ts index b55a2880e6..8f9c997e6f 100644 --- a/tests/parser/class.ts +++ b/tests/parser/class.ts @@ -37,4 +37,7 @@ export class Invalid { // 1031: 'declare' modifier cannot appear on class elements of this kind. // 1183: An implementation cannot be declared in ambient contexts. declare declareMethod(): i32 {} + + // ERROR 1042: "'override' modifier cannot be used here." + override overrideMethod(): void {} } diff --git a/tests/parser/class.ts.fixture.ts b/tests/parser/class.ts.fixture.ts index 9d25f7a31d..8d429d4f5c 100644 --- a/tests/parser/class.ts.fixture.ts +++ b/tests/parser/class.ts.fixture.ts @@ -17,6 +17,7 @@ export class Invalid { declare declareField: i32; declare declareInitializer: i32 = 0; declare declareMethod(): i32 {} + overrideMethod(): void {} } // ERROR 1092: "Type parameters cannot appear on a constructor declaration." in class.ts(15,14+3) // ERROR 1110: "Type expected." in class.ts(18,21+0) @@ -31,3 +32,4 @@ export class Invalid { // ERROR 1039: "Initializers are not allowed in ambient contexts." in class.ts(35,35+1) // ERROR 1031: "'declare' modifier cannot appear on class elements of this kind." in class.ts(39,3+7) // ERROR 1183: "An implementation cannot be declared in ambient contexts." in class.ts(39,32+1) +// ERROR 1042: "'override' modifier cannot be used here." in class.ts(42,3+8) From 675550ec832cf8f6b3de31d1de01fb18a49cd642 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sun, 10 Jul 2022 19:48:06 +0800 Subject: [PATCH 2/4] feat: add override check --- src/diagnosticMessages.json | 1 + src/program.ts | 6 ++++++ tests/compiler/class-overloading-cast.ts | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index a69e5d608f..2969b8964a 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -184,6 +184,7 @@ "Duplicate property '{0}'.": 2718, "Property '{0}' is missing in type '{1}' but required in type '{2}'.": 2741, "Type '{0}' has no call signatures.": 2757, + "This member cannot have an 'override' modifier because it is not declared in the base class '{0}'.": 4117, "File '{0}' not found.": 6054, "Numeric separators are not allowed here.": 6188, diff --git a/src/program.ts b/src/program.ts index 2958241478..0bb39bc43d 100644 --- a/src/program.ts +++ b/src/program.ts @@ -1524,6 +1524,12 @@ export class Program extends DiagnosticEmitter { } } } + if (thisMember.is(CommonFlags.OVERRIDE) && !baseInstanceMembers.has(thisMember.name)) { + this.error( + DiagnosticCode.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0, + thisMember.identifierNode.range, basePrototype.name + ); + } } } let nextPrototype = basePrototype.basePrototype; diff --git a/tests/compiler/class-overloading-cast.ts b/tests/compiler/class-overloading-cast.ts index 45457f0630..38b07f5c67 100644 --- a/tests/compiler/class-overloading-cast.ts +++ b/tests/compiler/class-overloading-cast.ts @@ -4,7 +4,7 @@ class A { } } class B extends A { - foo(a: T): string { + override foo(a: T): string { return "B"; } } From 41a62b63db02238f453b63fffc01526824637d1e Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sun, 10 Jul 2022 20:03:12 +0800 Subject: [PATCH 3/4] test: add override error testcase --- tests/compiler/override-error.json | 6 ++++++ tests/compiler/override-error.ts | 14 ++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/compiler/override-error.json create mode 100644 tests/compiler/override-error.ts diff --git a/tests/compiler/override-error.json b/tests/compiler/override-error.json new file mode 100644 index 0000000000..146ec5126f --- /dev/null +++ b/tests/compiler/override-error.json @@ -0,0 +1,6 @@ +{ + "asc_flags": [], + "stderr": [ + "This member cannot have an 'override' modifier because it is not declared in the base class 'A'." + ] +} diff --git a/tests/compiler/override-error.ts b/tests/compiler/override-error.ts new file mode 100644 index 0000000000..a9341a0351 --- /dev/null +++ b/tests/compiler/override-error.ts @@ -0,0 +1,14 @@ +class A { + method(): void {} +} + +class B extends A { + override method(): void {} + // This member cannot have an 'override' modifier because it is not declared in the base class 'A'. + override method_error(): void {} +} + +export function test(): void { + new A(); + new B(); +} From c2c1e355415768d287668f4764b29e4de1b46f58 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Wed, 13 Jul 2022 22:01:58 +0800 Subject: [PATCH 4/4] update stderr --- tests/compiler/override-error.json | 2 +- tests/compiler/override-error.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/compiler/override-error.json b/tests/compiler/override-error.json index 146ec5126f..90d85c530a 100644 --- a/tests/compiler/override-error.json +++ b/tests/compiler/override-error.json @@ -1,6 +1,6 @@ { "asc_flags": [], "stderr": [ - "This member cannot have an 'override' modifier because it is not declared in the base class 'A'." + "TS4117: This member cannot have an 'override' modifier because it is not declared in the base class 'A'." ] } diff --git a/tests/compiler/override-error.ts b/tests/compiler/override-error.ts index a9341a0351..7ee822ec83 100644 --- a/tests/compiler/override-error.ts +++ b/tests/compiler/override-error.ts @@ -4,7 +4,7 @@ class A { class B extends A { override method(): void {} - // This member cannot have an 'override' modifier because it is not declared in the base class 'A'. + // TS4117: This member cannot have an 'override' modifier because it is not declared in the base class 'A'. override method_error(): void {} }