Skip to content

Commit c1f0f7c

Browse files
authored
feat(57240): Unimplemented abstract methods result in verbose, repetitive message (#57291)
1 parent 884d649 commit c1f0f7c

24 files changed

+331
-177
lines changed

src/compiler/checker.ts

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -45069,9 +45069,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4506945069

4507045070
// NOTE: assignability is checked in checkClassDeclaration
4507145071
const baseProperties = getPropertiesOfType(baseType);
45072-
let inheritedAbstractMemberNotImplementedError: Diagnostic | undefined;
45073-
basePropertyCheck:
45074-
for (const baseProperty of baseProperties) {
45072+
45073+
interface MemberInfo {
45074+
missedProperties: string[];
45075+
baseTypeName: string;
45076+
typeName: string;
45077+
}
45078+
const notImplementedInfo = new Map<ClassLikeDeclaration, MemberInfo>();
45079+
45080+
basePropertyCheck: for (const baseProperty of baseProperties) {
4507545081
const base = getTargetSymbol(baseProperty);
4507645082

4507745083
if (base.flags & SymbolFlags.Prototype) {
@@ -45108,38 +45114,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4510845114
continue basePropertyCheck;
4510945115
}
4511045116
}
45111-
45112-
if (!inheritedAbstractMemberNotImplementedError) {
45113-
inheritedAbstractMemberNotImplementedError = error(
45114-
derivedClassDecl,
45115-
Diagnostics.Non_abstract_class_0_does_not_implement_all_abstract_members_of_1,
45116-
typeToString(type),
45117-
typeToString(baseType),
45118-
);
45119-
}
45120-
if (derivedClassDecl.kind === SyntaxKind.ClassExpression) {
45121-
addRelatedInfo(
45122-
inheritedAbstractMemberNotImplementedError,
45123-
createDiagnosticForNode(
45124-
baseProperty.valueDeclaration ?? (baseProperty.declarations && first(baseProperty.declarations)) ?? derivedClassDecl,
45125-
Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1,
45126-
symbolToString(baseProperty),
45127-
typeToString(baseType),
45128-
),
45129-
);
45130-
}
45131-
else {
45132-
addRelatedInfo(
45133-
inheritedAbstractMemberNotImplementedError,
45134-
createDiagnosticForNode(
45135-
baseProperty.valueDeclaration ?? (baseProperty.declarations && first(baseProperty.declarations)) ?? derivedClassDecl,
45136-
Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2,
45137-
typeToString(type),
45138-
symbolToString(baseProperty),
45139-
typeToString(baseType),
45140-
),
45141-
);
45142-
}
45117+
const baseTypeName = typeToString(baseType);
45118+
const typeName = typeToString(type);
45119+
const basePropertyName = symbolToString(baseProperty);
45120+
const missedProperties = append(notImplementedInfo.get(derivedClassDecl)?.missedProperties, basePropertyName);
45121+
notImplementedInfo.set(derivedClassDecl, { baseTypeName, typeName, missedProperties });
4514345122
}
4514445123
}
4514545124
else {
@@ -45223,6 +45202,36 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4522345202
error(getNameOfDeclaration(derived.valueDeclaration) || derived.valueDeclaration, errorMessage, typeToString(baseType), symbolToString(base), typeToString(type));
4522445203
}
4522545204
}
45205+
45206+
for (const [errorNode, memberInfo] of notImplementedInfo) {
45207+
if (length(memberInfo.missedProperties) === 1) {
45208+
if (isClassExpression(errorNode)) {
45209+
error(errorNode, Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1, first(memberInfo.missedProperties), memberInfo.baseTypeName);
45210+
}
45211+
else {
45212+
error(errorNode, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2, memberInfo.typeName, first(memberInfo.missedProperties), memberInfo.baseTypeName);
45213+
}
45214+
}
45215+
else if (length(memberInfo.missedProperties) > 5) {
45216+
const missedProperties = map(memberInfo.missedProperties.slice(0, 4), prop => `'${prop}'`).join(", ");
45217+
const remainingMissedProperties = length(memberInfo.missedProperties) - 4;
45218+
if (isClassExpression(errorNode)) {
45219+
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);
45220+
}
45221+
else {
45222+
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);
45223+
}
45224+
}
45225+
else {
45226+
const missedProperties = map(memberInfo.missedProperties, prop => `'${prop}'`).join(", ");
45227+
if (isClassExpression(errorNode)) {
45228+
error(errorNode, Diagnostics.Non_abstract_class_expression_is_missing_implementations_for_the_following_members_of_0_Colon_1, memberInfo.baseTypeName, missedProperties);
45229+
}
45230+
else {
45231+
error(errorNode, Diagnostics.Non_abstract_class_0_is_missing_implementations_for_the_following_members_of_1_Colon_2, memberInfo.typeName, memberInfo.baseTypeName, missedProperties);
45232+
}
45233+
}
45234+
}
4522645235
}
4522745236

4522845237
function isPropertyAbstractOrInterface(declaration: Declaration, baseDeclarationFlags: ModifierFlags) {

src/compiler/diagnosticMessages.json

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2484,7 +2484,7 @@
24842484
"category": "Error",
24852485
"code": 2514
24862486
},
2487-
"Non-abstract class '{0}' does not implement inherited abstract member '{1}' from class '{2}'.": {
2487+
"Non-abstract class '{0}' does not implement inherited abstract member {1} from class '{2}'.": {
24882488
"category": "Error",
24892489
"code": 2515
24902490
},
@@ -2926,6 +2926,10 @@
29262926
"category": "Error",
29272927
"code": 2649
29282928
},
2929+
"Non-abstract class expression is missing implementations for the following members of '{0}': {1} and {2} more.": {
2930+
"category": "Error",
2931+
"code": 2650
2932+
},
29292933
"A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums.": {
29302934
"category": "Error",
29312935
"code": 2651
@@ -2938,6 +2942,18 @@
29382942
"category": "Error",
29392943
"code": 2653
29402944
},
2945+
"Non-abstract class '{0}' is missing implementations for the following members of '{1}': {2}.": {
2946+
"category": "Error",
2947+
"code": 2654
2948+
},
2949+
"Non-abstract class '{0}' is missing implementations for the following members of '{1}': {2} and {3} more.": {
2950+
"category": "Error",
2951+
"code": 2655
2952+
},
2953+
"Non-abstract class expression is missing implementations for the following members of '{0}': {1}.": {
2954+
"category": "Error",
2955+
"code": 2656
2956+
},
29412957
"JSX expressions must have one parent element.": {
29422958
"category": "Error",
29432959
"code": 2657
@@ -7937,10 +7953,6 @@
79377953
"category": "Error",
79387954
"code": 18051
79397955
},
7940-
"Non-abstract class '{0}' does not implement all abstract members of '{1}'": {
7941-
"category": "Error",
7942-
"code": 18052
7943-
},
79447956
"Its type '{0}' is not a valid JSX element type.": {
79457957
"category": "Error",
79467958
"code": 18053

src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,14 @@ import {
2626
} from "../_namespaces/ts.codefix";
2727

2828
const errorCodes = [
29-
Diagnostics.Non_abstract_class_0_does_not_implement_all_abstract_members_of_1.code,
29+
Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2.code,
30+
Diagnostics.Non_abstract_class_0_is_missing_implementations_for_the_following_members_of_1_Colon_2.code,
31+
Diagnostics.Non_abstract_class_0_is_missing_implementations_for_the_following_members_of_1_Colon_2_and_3_more.code,
32+
Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1.code,
33+
Diagnostics.Non_abstract_class_expression_is_missing_implementations_for_the_following_members_of_0_Colon_1.code,
34+
Diagnostics.Non_abstract_class_expression_is_missing_implementations_for_the_following_members_of_0_Colon_1_and_2_more.code,
3035
];
36+
3137
const fixId = "fixClassDoesntImplementInheritedAbstractMember";
3238
registerCodeFix({
3339
errorCodes,

tests/baselines/reference/abstractPropertyNegative.errors.txt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
abstractPropertyNegative.ts(13,7): error TS18052: Non-abstract class 'C' does not implement all abstract members of 'B'
1+
abstractPropertyNegative.ts(13,7): error TS2654: Non-abstract class 'C' is missing implementations for the following members of 'B': 'prop', 'readonlyProp', 'm', 'mismatch'.
22
abstractPropertyNegative.ts(15,5): error TS1253: Abstract properties can only appear within an abstract class.
33
abstractPropertyNegative.ts(16,37): error TS1005: '{' expected.
44
abstractPropertyNegative.ts(19,3): error TS2540: Cannot assign to 'ro' because it is a read-only property.
@@ -29,11 +29,7 @@ abstractPropertyNegative.ts(41,18): error TS2676: Accessors must both be abstrac
2929
}
3030
class C extends B {
3131
~
32-
!!! error TS18052: Non-abstract class 'C' does not implement all abstract members of 'B'
33-
!!! related TS2515 abstractPropertyNegative.ts:6:14: Non-abstract class 'C' does not implement inherited abstract member 'prop' from class 'B'.
34-
!!! related TS2515 abstractPropertyNegative.ts:8:18: Non-abstract class 'C' does not implement inherited abstract member 'readonlyProp' from class 'B'.
35-
!!! related TS2515 abstractPropertyNegative.ts:9:14: Non-abstract class 'C' does not implement inherited abstract member 'm' from class 'B'.
36-
!!! related TS2515 abstractPropertyNegative.ts:10:18: Non-abstract class 'C' does not implement inherited abstract member 'mismatch' from class 'B'.
32+
!!! error TS2654: Non-abstract class 'C' is missing implementations for the following members of 'B': 'prop', 'readonlyProp', 'm', 'mismatch'.
3733
readonly ro = "readonly please";
3834
abstract notAllowed: string;
3935
~~~~~~~~

tests/baselines/reference/classAbstractDeclarations.d.errors.txt

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
classAbstractDeclarations.d.ts(2,5): error TS1242: 'abstract' modifier can only appear on a class, method, or property declaration.
22
classAbstractDeclarations.d.ts(2,28): error TS1183: An implementation cannot be declared in ambient contexts.
3-
classAbstractDeclarations.d.ts(11,15): error TS18052: Non-abstract class 'CC' does not implement all abstract members of 'AA'
4-
classAbstractDeclarations.d.ts(13,15): error TS18052: Non-abstract class 'DD' does not implement all abstract members of 'BB'
5-
classAbstractDeclarations.d.ts(17,15): error TS18052: Non-abstract class 'FF' does not implement all abstract members of 'CC'
3+
classAbstractDeclarations.d.ts(11,15): error TS2515: Non-abstract class 'CC' does not implement inherited abstract member foo from class 'AA'.
4+
classAbstractDeclarations.d.ts(13,15): error TS2515: Non-abstract class 'DD' does not implement inherited abstract member foo from class 'BB'.
5+
classAbstractDeclarations.d.ts(17,15): error TS2515: Non-abstract class 'FF' does not implement inherited abstract member foo from class 'CC'.
66

77

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

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

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

3331
declare abstract class EE extends BB {}
3432

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

4037
declare abstract class GG extends CC {}
4138

tests/baselines/reference/classAbstractExtends.errors.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
classAbstractExtends.ts(9,7): error TS18052: Non-abstract class 'C' does not implement all abstract members of 'B'
1+
classAbstractExtends.ts(9,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member bar from class 'B'.
22

33

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

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

1817
abstract class D extends B {}
1918

tests/baselines/reference/classAbstractGeneric.errors.txt

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
classAbstractGeneric.ts(10,7): error TS18052: Non-abstract class 'C<T>' does not implement all abstract members of 'A<T>'
2-
classAbstractGeneric.ts(12,7): error TS18052: Non-abstract class 'D' does not implement all abstract members of 'A<number>'
3-
classAbstractGeneric.ts(14,7): error TS18052: Non-abstract class 'E<T>' does not implement all abstract members of 'A<T>'
4-
classAbstractGeneric.ts(18,7): error TS18052: Non-abstract class 'F<T>' does not implement all abstract members of 'A<T>'
1+
classAbstractGeneric.ts(10,7): error TS2654: Non-abstract class 'C<T>' is missing implementations for the following members of 'A<T>': 'foo', 'bar'.
2+
classAbstractGeneric.ts(12,7): error TS2654: Non-abstract class 'D' is missing implementations for the following members of 'A<number>': 'foo', 'bar'.
3+
classAbstractGeneric.ts(14,7): error TS2515: Non-abstract class 'E<T>' does not implement inherited abstract member bar from class 'A<T>'.
4+
classAbstractGeneric.ts(18,7): error TS2515: Non-abstract class 'F<T>' does not implement inherited abstract member foo from class 'A<T>'.
55

66

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

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

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

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

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

tests/baselines/reference/classAbstractInheritance.errors.txt

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)