diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 3961e094cde6b..989efb9c4196a 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1100,6 +1100,8 @@ export function append(to: T[], value: T | undefined): T[] | undefined { * * @internal */ +export function combine(xs: T[] | undefined, ys: T[] | undefined): T[] | undefined; +/** @internal */ export function combine(xs: T | readonly T[] | undefined, ys: T | readonly T[] | undefined): T | readonly T[] | undefined; /** @internal */ export function combine(xs: T | T[] | undefined, ys: T | T[] | undefined): T | T[] | undefined; diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 1bc5bbb658c27..4bf31860481cf 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -8,6 +8,7 @@ import { CharacterCodes, ClassLikeDeclaration, CodeFixContextBase, + combine, Debug, Diagnostics, emptyArray, @@ -31,6 +32,7 @@ import { getSynthesizedDeepClone, getTokenAtPosition, getTsConfigObjectLiteralExpression, + hasAbstractModifier, Identifier, idText, IntersectionType, @@ -197,7 +199,7 @@ export function addNewNodeForMemberSymbol( if (declaration && isAutoAccessorPropertyDeclaration(declaration)) { modifierFlags |= ModifierFlags.Accessor; } - const modifiers = modifierFlags ? factory.createNodeArray(factory.createModifiersFromModifierFlags(modifierFlags)) : undefined; + const modifiers = createModifiers(); const type = checker.getWidenedType(checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration)); const optional = !!(symbol.flags & SymbolFlags.Optional); const ambient = !!(enclosingDeclaration.flags & NodeFlags.Ambient) || isAmbient; @@ -304,6 +306,25 @@ export function addNewNodeForMemberSymbol( if (method) addClassElement(method); } + + function createModifiers(): NodeArray | undefined { + let modifiers: Modifier[] | undefined; + + if (modifierFlags) { + modifiers = combine(modifiers, factory.createModifiersFromModifierFlags(modifierFlags)); + } + + if (shouldAddOverrideKeyword()) { + modifiers = append(modifiers, factory.createToken(SyntaxKind.OverrideKeyword)); + } + + return modifiers && factory.createNodeArray(modifiers); + } + + function shouldAddOverrideKeyword(): boolean { + return !!(context.program.getCompilerOptions().noImplicitOverride && declaration && hasAbstractModifier(declaration)); + } + function createName(node: PropertyName) { return getSynthesizedDeepClone(node, /*includeTrivia*/ false); } diff --git a/tests/cases/fourslash/codeFixAmbientClassExtendAbstractMethod.ts b/tests/cases/fourslash/codeFixAmbientClassExtendAbstractMethod.ts index 7f854405d2fb8..7d21d615808ed 100644 --- a/tests/cases/fourslash/codeFixAmbientClassExtendAbstractMethod.ts +++ b/tests/cases/fourslash/codeFixAmbientClassExtendAbstractMethod.ts @@ -1,5 +1,6 @@ /// +// @noImplicitOverride: true ////abstract class A { //// abstract f(a: number, b: string): boolean; //// abstract f(a: number, b: string): this; @@ -30,12 +31,12 @@ verify.codeFix({ } declare class C extends A { - f(a: number, b: string): boolean; - f(a: number, b: string): this; - f(a: string, b: number): Function; - f(a: string): Function; - f1(this: A): number; - f2(this: A, a: number, b: string): number; - foo(): number; + override f(a: number, b: string): boolean; + override f(a: number, b: string): this; + override f(a: string, b: number): Function; + override f(a: string): Function; + override f1(this: A): number; + override f2(this: A, a: number, b: string): number; + override foo(): number; }` }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts b/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts index 00c140dfcccb3..8560b2bcdeef3 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts @@ -1,5 +1,6 @@ /// +// @noImplicitOverride: true ////abstract class A { //// abstract get a(): number | string; //// abstract get b(): this; @@ -38,28 +39,28 @@ verify.codeFix({ abstract class B extends A {} class C extends A { - get a(): string | number { + override get a(): string | number { throw new Error("Method not implemented."); } - get b(): this { + override get b(): this { throw new Error("Method not implemented."); } - get c(): A { + override get c(): A { throw new Error("Method not implemented."); } - set d(arg: string | number) { + override set d(arg: string | number) { throw new Error("Method not implemented."); } - set e(arg: this) { + override set e(arg: this) { throw new Error("Method not implemented."); } - set f(arg: A) { + override set f(arg: A) { throw new Error("Method not implemented."); } - get g(): string { + override get g(): string { throw new Error("Method not implemented."); } - set g(newName: string) { + override set g(newName: string) { throw new Error("Method not implemented."); } }` diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts index c97c08ad7f95c..6c6121d7fb12b 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts @@ -1,5 +1,6 @@ /// +// @noImplicitOverride: true ////abstract class A { //// abstract f(a: number, b: string): boolean; //// abstract f(a: number, b: string): this; @@ -22,14 +23,14 @@ verify.codeFix({ } class C extends A { - f(a: number, b: string): boolean; - f(a: number, b: string): this; - f(a: string, b: number): Function; - f(a: string): Function; - f(a: unknown, b?: unknown): boolean | Function | this { + override f(a: number, b: string): boolean; + override f(a: number, b: string): this; + override f(a: string, b: number): Function; + override f(a: string): Function; + override f(a: unknown, b?: unknown): boolean | Function | this { throw new Error("Method not implemented."); } - foo(): number { + override foo(): number { throw new Error("Method not implemented."); } }` diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethodModifiersAlreadyIncludeOverride.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethodModifiersAlreadyIncludeOverride.ts new file mode 100644 index 0000000000000..6e7e0256ba197 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethodModifiersAlreadyIncludeOverride.ts @@ -0,0 +1,29 @@ +/// + +// @noImplicitOverride: true +//// abstract class A { +//// abstract foo(n: number | string): number | string; +//// } +//// +//// abstract class B extends A { +//// abstract override foo(n: number): number; +//// } +//// +//// class C extends B { } + +verify.codeFix({ + description: "Implement inherited abstract class", + newFileContent: `abstract class A { + abstract foo(n: number | string): number | string; +} + +abstract class B extends A { + abstract override foo(n: number): number; +} + +class C extends B { + override foo(n: number): number { + throw new Error("Method not implemented."); + } +}`, +}); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts index 49770ec85ea7f..a4ed8cd5d0415 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts @@ -1,5 +1,6 @@ /// +// @noImplicitOverride: true ////abstract class A { //// abstract m(): void; //// abstract n(): void; @@ -16,18 +17,18 @@ verify.codeFixAll({ abstract n(): void; } class B extends A { - m(): void { + override m(): void { throw new Error("Method not implemented."); } - n(): void { + override n(): void { throw new Error("Method not implemented."); } } class C extends A { - m(): void { + override m(): void { throw new Error("Method not implemented."); } - n(): void { + override n(): void { throw new Error("Method not implemented."); } }`, diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethod_comment.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethod_comment.ts index 909fd4ba7c71a..d7d91d8c87225 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethod_comment.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethod_comment.ts @@ -1,5 +1,6 @@ /// +// @noImplicitOverride: true ////abstract class A { //// abstract m() : void; ////} @@ -16,7 +17,7 @@ verify.codeFix({ } class B extends A { - m(): void { + override m(): void { throw new Error("Method not implemented."); } // comment diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractPrivateProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractPrivateProperty.ts index b4bd301a41553..4f22bede7340f 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractPrivateProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractPrivateProperty.ts @@ -1,5 +1,6 @@ /// +// @noImplicitOverride: true //// abstract class A { //// private abstract x: number; //// m() { this.x; } // Avoid unused private diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts index 3525bf220e920..dd56da3fd3a7e 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts @@ -1,5 +1,6 @@ /// +// @noImplicitOverride: true ////abstract class A { //// abstract x: number; //// abstract y: this; @@ -18,8 +19,8 @@ verify.codeFix({ } class C extends A { - x: number; - y: this; - z: A; + override x: number; + override y: this; + override z: A; }` }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts b/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts index 06b9dddcd8a9d..b9dc081aa489d 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts @@ -1,5 +1,6 @@ /// +// @noImplicitOverride: true ////abstract class A { //// abstract x: this; ////} @@ -14,6 +15,6 @@ verify.codeFix({ } class C extends A { - x: this; + override x: this; }`, }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts index a150dd07f8dce..7fa30cb5e24e7 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts @@ -1,5 +1,6 @@ /// +// @noImplicitOverride: true ////abstract class A { //// protected abstract x: number; ////} @@ -14,6 +15,6 @@ verify.codeFix({ } class C extends A { - protected x: number; + protected override x: number; }`, }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts index 48a023e96fa4e..2d107211ede56 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts @@ -1,5 +1,6 @@ /// +// @noImplicitOverride: true ////abstract class A { //// public abstract x: number; ////} @@ -14,6 +15,6 @@ verify.codeFix({ } class C extends A { - public x: number; + public override x: number; }`, }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractSomePropertiesPresent.ts b/tests/cases/fourslash/codeFixClassExtendAbstractSomePropertiesPresent.ts index fb66a04cd5cb9..0a9df34bc8851 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractSomePropertiesPresent.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractSomePropertiesPresent.ts @@ -1,5 +1,6 @@ /// +// @noImplicitOverride: true //// abstract class A { //// abstract x: number; //// abstract y: number; @@ -12,5 +13,5 @@ //// } verify.rangeAfterCodeFix(` -z: number; +override z: number; `);