Skip to content

feat(57240): Unimplemented abstract methods result in verbose, repetitive message #57291

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 5 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
79 changes: 44 additions & 35 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45016,9 +45016,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {

// NOTE: assignability is checked in checkClassDeclaration
const baseProperties = getPropertiesOfType(baseType);
let inheritedAbstractMemberNotImplementedError: Diagnostic | undefined;
basePropertyCheck:
for (const baseProperty of baseProperties) {

interface MemberInfo {
missedProperties: string[];
baseTypeName: string;
typeName: string;
}
const notImplementedInfo = new Map<ClassLikeDeclaration, MemberInfo>();

basePropertyCheck: for (const baseProperty of baseProperties) {
const base = getTargetSymbol(baseProperty);

if (base.flags & SymbolFlags.Prototype) {
Expand Down Expand Up @@ -45055,38 +45061,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
continue basePropertyCheck;
}
}

if (!inheritedAbstractMemberNotImplementedError) {
inheritedAbstractMemberNotImplementedError = error(
derivedClassDecl,
Diagnostics.Non_abstract_class_0_does_not_implement_all_abstract_members_of_1,
typeToString(type),
typeToString(baseType),
);
}
if (derivedClassDecl.kind === SyntaxKind.ClassExpression) {
addRelatedInfo(
inheritedAbstractMemberNotImplementedError,
createDiagnosticForNode(
baseProperty.valueDeclaration ?? (baseProperty.declarations && first(baseProperty.declarations)) ?? derivedClassDecl,
Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1,
symbolToString(baseProperty),
typeToString(baseType),
),
);
}
else {
addRelatedInfo(
inheritedAbstractMemberNotImplementedError,
createDiagnosticForNode(
baseProperty.valueDeclaration ?? (baseProperty.declarations && first(baseProperty.declarations)) ?? derivedClassDecl,
Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2,
typeToString(type),
symbolToString(baseProperty),
typeToString(baseType),
),
);
}
const baseTypeName = typeToString(baseType);
const typeName = typeToString(type);
const basePropertyName = symbolToString(baseProperty);
const missedProperties = append(notImplementedInfo.get(derivedClassDecl)?.missedProperties, basePropertyName);
notImplementedInfo.set(derivedClassDecl, { baseTypeName, typeName, missedProperties });
}
}
else {
Expand Down Expand Up @@ -45170,6 +45149,36 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
error(getNameOfDeclaration(derived.valueDeclaration) || derived.valueDeclaration, errorMessage, typeToString(baseType), symbolToString(base), typeToString(type));
}
}

for (const [errorNode, memberInfo] of notImplementedInfo) {
if (length(memberInfo.missedProperties) === 1) {
if (isClassExpression(errorNode)) {
error(errorNode, Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1, first(memberInfo.missedProperties), memberInfo.baseTypeName);
}
else {
error(errorNode, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2, memberInfo.typeName, first(memberInfo.missedProperties), memberInfo.baseTypeName);
}
}
else if (length(memberInfo.missedProperties) > 5) {
const missedProperties = map(memberInfo.missedProperties.slice(0, 4), prop => `'${prop}'`).join(", ");
const remainingMissedProperties = length(memberInfo.missedProperties) - 4;
if (isClassExpression(errorNode)) {
error(errorNode, Diagnostics.Non_abstract_class_expression_is_missing_implementations_for_the_following_members_of_0_Colon_1_and_2_more, memberInfo.baseTypeName, missedProperties, remainingMissedProperties);
}
else {
error(errorNode, Diagnostics.Non_abstract_class_0_is_missing_implementations_for_the_following_members_of_1_Colon_2_and_3_more, memberInfo.typeName, memberInfo.baseTypeName, missedProperties, remainingMissedProperties);
}
}
else {
const missedProperties = map(memberInfo.missedProperties, prop => `'${prop}'`).join(", ");
if (isClassExpression(errorNode)) {
error(errorNode, Diagnostics.Non_abstract_class_expression_is_missing_implementations_for_the_following_members_of_0_Colon_1, memberInfo.baseTypeName, missedProperties);
}
else {
error(errorNode, Diagnostics.Non_abstract_class_0_is_missing_implementations_for_the_following_members_of_1_Colon_2, memberInfo.typeName, memberInfo.baseTypeName, missedProperties);
}
}
}
}

function isPropertyAbstractOrInterface(declaration: Declaration, baseDeclarationFlags: ModifierFlags) {
Expand Down
24 changes: 18 additions & 6 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2496,7 +2496,7 @@
"category": "Error",
"code": 2514
},
"Non-abstract class '{0}' does not implement inherited abstract member '{1}' from class '{2}'.": {
"Non-abstract class '{0}' does not implement inherited abstract member {1} from class '{2}'.": {
"category": "Error",
"code": 2515
},
Expand Down Expand Up @@ -2938,6 +2938,10 @@
"category": "Error",
"code": 2649
},
"Non-abstract class expression is missing implementations for the following members of '{0}': {1} and {2} more.": {
"category": "Error",
"code": 2650
},
"A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums.": {
"category": "Error",
"code": 2651
Expand All @@ -2946,10 +2950,22 @@
"category": "Error",
"code": 2652
},
"Non-abstract class expression does not implement inherited abstract member '{0}' from class '{1}'.": {
"Non-abstract class expression does not implement inherited abstract member {0} from class '{1}'.": {
"category": "Error",
"code": 2653
},
"Non-abstract class '{0}' is missing implementations for the following members of '{1}': {2}.": {
"category": "Error",
"code": 2654
},
"Non-abstract class '{0}' is missing implementations for the following members of '{1}': {2} and {3} more.": {
"category": "Error",
"code": 2655
},
"Non-abstract class expression is missing implementations for the following members of '{0}': {1}.": {
"category": "Error",
"code": 2656
},
"JSX expressions must have one parent element.": {
"category": "Error",
"code": 2657
Expand Down Expand Up @@ -7973,10 +7989,6 @@
"category": "Error",
"code": 18051
},
"Non-abstract class '{0}' does not implement all abstract members of '{1}'": {
"category": "Error",
"code": 18052
},
"Its type '{0}' is not a valid JSX element type.": {
"category": "Error",
"code": 18053
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@ import {
} from "../_namespaces/ts.codefix";

const errorCodes = [
Diagnostics.Non_abstract_class_0_does_not_implement_all_abstract_members_of_1.code,
Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2.code,
Diagnostics.Non_abstract_class_0_is_missing_implementations_for_the_following_members_of_1_Colon_2.code,
Diagnostics.Non_abstract_class_0_is_missing_implementations_for_the_following_members_of_1_Colon_2_and_3_more.code,
Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1.code,
Diagnostics.Non_abstract_class_expression_is_missing_implementations_for_the_following_members_of_0_Colon_1.code,
Diagnostics.Non_abstract_class_expression_is_missing_implementations_for_the_following_members_of_0_Colon_1_and_2_more.code,
];

const fixId = "fixClassDoesntImplementInheritedAbstractMember";
registerCodeFix({
errorCodes,
Expand Down
8 changes: 2 additions & 6 deletions tests/baselines/reference/abstractPropertyNegative.errors.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
abstractPropertyNegative.ts(13,7): error TS18052: Non-abstract class 'C' does not implement all abstract members of 'B'
abstractPropertyNegative.ts(13,7): error TS2654: Non-abstract class 'C' is missing implementations for the following members of 'B': 'prop', 'readonlyProp', 'm', 'mismatch'.
abstractPropertyNegative.ts(15,5): error TS1253: Abstract properties can only appear within an abstract class.
abstractPropertyNegative.ts(16,37): error TS1005: '{' expected.
abstractPropertyNegative.ts(19,3): error TS2540: Cannot assign to 'ro' because it is a read-only property.
Expand Down Expand Up @@ -29,11 +29,7 @@ abstractPropertyNegative.ts(41,18): error TS2676: Accessors must both be abstrac
}
class C extends B {
~
!!! error TS18052: Non-abstract class 'C' does not implement all abstract members of 'B'
!!! related TS2515 abstractPropertyNegative.ts:6:14: Non-abstract class 'C' does not implement inherited abstract member 'prop' from class 'B'.
!!! related TS2515 abstractPropertyNegative.ts:8:18: Non-abstract class 'C' does not implement inherited abstract member 'readonlyProp' from class 'B'.
!!! related TS2515 abstractPropertyNegative.ts:9:14: Non-abstract class 'C' does not implement inherited abstract member 'm' from class 'B'.
!!! related TS2515 abstractPropertyNegative.ts:10:18: Non-abstract class 'C' does not implement inherited abstract member 'mismatch' from class 'B'.
!!! error TS2654: Non-abstract class 'C' is missing implementations for the following members of 'B': 'prop', 'readonlyProp', 'm', 'mismatch'.
readonly ro = "readonly please";
abstract notAllowed: string;
~~~~~~~~
Expand Down
15 changes: 6 additions & 9 deletions tests/baselines/reference/classAbstractDeclarations.d.errors.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
classAbstractDeclarations.d.ts(2,5): error TS1242: 'abstract' modifier can only appear on a class, method, or property declaration.
classAbstractDeclarations.d.ts(2,28): error TS1183: An implementation cannot be declared in ambient contexts.
classAbstractDeclarations.d.ts(11,15): error TS18052: Non-abstract class 'CC' does not implement all abstract members of 'AA'
classAbstractDeclarations.d.ts(13,15): error TS18052: Non-abstract class 'DD' does not implement all abstract members of 'BB'
classAbstractDeclarations.d.ts(17,15): error TS18052: Non-abstract class 'FF' does not implement all abstract members of 'CC'
classAbstractDeclarations.d.ts(11,15): error TS2515: Non-abstract class 'CC' does not implement inherited abstract member foo from class 'AA'.
classAbstractDeclarations.d.ts(13,15): error TS2515: Non-abstract class 'DD' does not implement inherited abstract member foo from class 'BB'.
classAbstractDeclarations.d.ts(17,15): error TS2515: Non-abstract class 'FF' does not implement inherited abstract member foo from class 'CC'.


==== classAbstractDeclarations.d.ts (5 errors) ====
Expand All @@ -22,20 +22,17 @@ classAbstractDeclarations.d.ts(17,15): error TS18052: Non-abstract class 'FF' do

declare class CC extends AA {}
~~
!!! error TS18052: Non-abstract class 'CC' does not implement all abstract members of 'AA'
!!! related TS2515 classAbstractDeclarations.d.ts:6:14: Non-abstract class 'CC' does not implement inherited abstract member 'foo' from class 'AA'.
!!! error TS2515: Non-abstract class 'CC' does not implement inherited abstract member foo from class 'AA'.

declare class DD extends BB {}
~~
!!! error TS18052: Non-abstract class 'DD' does not implement all abstract members of 'BB'
!!! related TS2515 classAbstractDeclarations.d.ts:6:14: Non-abstract class 'DD' does not implement inherited abstract member 'foo' from class 'BB'.
!!! error TS2515: Non-abstract class 'DD' does not implement inherited abstract member foo from class 'BB'.

declare abstract class EE extends BB {}

declare class FF extends CC {}
~~
!!! error TS18052: Non-abstract class 'FF' does not implement all abstract members of 'CC'
!!! related TS2515 classAbstractDeclarations.d.ts:6:14: Non-abstract class 'FF' does not implement inherited abstract member 'foo' from class 'CC'.
!!! error TS2515: Non-abstract class 'FF' does not implement inherited abstract member foo from class 'CC'.

declare abstract class GG extends CC {}

Expand Down
5 changes: 2 additions & 3 deletions tests/baselines/reference/classAbstractExtends.errors.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
classAbstractExtends.ts(9,7): error TS18052: Non-abstract class 'C' does not implement all abstract members of 'B'
classAbstractExtends.ts(9,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member bar from class 'B'.


==== classAbstractExtends.ts (1 errors) ====
Expand All @@ -12,8 +12,7 @@ classAbstractExtends.ts(9,7): error TS18052: Non-abstract class 'C' does not imp

class C extends B { }
~
!!! error TS18052: Non-abstract class 'C' does not implement all abstract members of 'B'
!!! related TS2515 classAbstractExtends.ts:6:14: Non-abstract class 'C' does not implement inherited abstract member 'bar' from class 'B'.
!!! error TS2515: Non-abstract class 'C' does not implement inherited abstract member bar from class 'B'.

abstract class D extends B {}

Expand Down
22 changes: 8 additions & 14 deletions tests/baselines/reference/classAbstractGeneric.errors.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
classAbstractGeneric.ts(10,7): error TS18052: Non-abstract class 'C<T>' does not implement all abstract members of 'A<T>'
classAbstractGeneric.ts(12,7): error TS18052: Non-abstract class 'D' does not implement all abstract members of 'A<number>'
classAbstractGeneric.ts(14,7): error TS18052: Non-abstract class 'E<T>' does not implement all abstract members of 'A<T>'
classAbstractGeneric.ts(18,7): error TS18052: Non-abstract class 'F<T>' does not implement all abstract members of 'A<T>'
classAbstractGeneric.ts(10,7): error TS2654: Non-abstract class 'C<T>' is missing implementations for the following members of 'A<T>': 'foo', 'bar'.
classAbstractGeneric.ts(12,7): error TS2654: Non-abstract class 'D' is missing implementations for the following members of 'A<number>': 'foo', 'bar'.
classAbstractGeneric.ts(14,7): error TS2515: Non-abstract class 'E<T>' does not implement inherited abstract member bar from class 'A<T>'.
classAbstractGeneric.ts(18,7): error TS2515: Non-abstract class 'F<T>' does not implement inherited abstract member foo from class 'A<T>'.


==== classAbstractGeneric.ts (4 errors) ====
Expand All @@ -16,27 +16,21 @@ classAbstractGeneric.ts(18,7): error TS18052: Non-abstract class 'F<T>' does not

class C<T> extends A<T> {} // error -- inherits abstract methods
~
!!! error TS18052: Non-abstract class 'C<T>' does not implement all abstract members of 'A<T>'
!!! related TS2515 classAbstractGeneric.ts:4:14: Non-abstract class 'C<T>' does not implement inherited abstract member 'foo' from class 'A<T>'.
!!! related TS2515 classAbstractGeneric.ts:5:14: Non-abstract class 'C<T>' does not implement inherited abstract member 'bar' from class 'A<T>'.
!!! error TS2654: Non-abstract class 'C<T>' is missing implementations for the following members of 'A<T>': 'foo', 'bar'.

class D extends A<number> {} // error -- inherits abstract methods
~
!!! error TS18052: Non-abstract class 'D' does not implement all abstract members of 'A<number>'
!!! related TS2515 classAbstractGeneric.ts:4:14: Non-abstract class 'D' does not implement inherited abstract member 'foo' from class 'A<number>'.
!!! related TS2515 classAbstractGeneric.ts:5:14: Non-abstract class 'D' does not implement inherited abstract member 'bar' from class 'A<number>'.
!!! error TS2654: Non-abstract class 'D' is missing implementations for the following members of 'A<number>': 'foo', 'bar'.

class E<T> extends A<T> { // error -- doesn't implement bar
~
!!! error TS18052: Non-abstract class 'E<T>' does not implement all abstract members of 'A<T>'
!!! related TS2515 classAbstractGeneric.ts:5:14: Non-abstract class 'E<T>' does not implement inherited abstract member 'bar' from class 'A<T>'.
!!! error TS2515: Non-abstract class 'E<T>' does not implement inherited abstract member bar from class 'A<T>'.
foo() { return this.t; }
}

class F<T> extends A<T> { // error -- doesn't implement foo
~
!!! error TS18052: Non-abstract class 'F<T>' does not implement all abstract members of 'A<T>'
!!! related TS2515 classAbstractGeneric.ts:4:14: Non-abstract class 'F<T>' does not implement inherited abstract member 'foo' from class 'A<T>'.
!!! error TS2515: Non-abstract class 'F<T>' does not implement inherited abstract member foo from class 'A<T>'.
bar(t : T) {}
}

Expand Down
36 changes: 0 additions & 36 deletions tests/baselines/reference/classAbstractInheritance.errors.txt

This file was deleted.

Loading