Skip to content

feat: add override keyword #2366

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 20 additions & 17 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
1 change: 1 addition & 0 deletions src/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
23 changes: 23 additions & 0 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1862,6 +1862,7 @@ export class Parser extends DiagnosticEmitter {
// 'declare'?
// ('public' | 'private' | 'protected')?
// ('static' | 'abstract')?
// 'override'?
// 'readonly'?
// ('get' | 'set')?
// Identifier ...
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/tokenizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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: {
Expand Down
2 changes: 1 addition & 1 deletion tests/compiler/class-overloading-cast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class A<T> {
}
}
class B<T, V> extends A<T> {
foo(a: T): string {
override foo(a: T): string {
return "B";
}
}
Expand Down
6 changes: 6 additions & 0 deletions tests/compiler/override-error.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"asc_flags": [],
"stderr": [
"TS4117: This member cannot have an 'override' modifier because it is not declared in the base class 'A'."
]
}
14 changes: 14 additions & 0 deletions tests/compiler/override-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class A {
method(): void {}
}

class B extends A {
override method(): void {}
// TS4117: 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();
}
3 changes: 3 additions & 0 deletions tests/parser/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,7 @@ export class Invalid<T> {
// 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 {}
}
2 changes: 2 additions & 0 deletions tests/parser/class.ts.fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export class Invalid<T> {
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)
Expand All @@ -31,3 +32,4 @@ export class Invalid<T> {
// 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)