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

This commit is contained in:
Oleksandr T
2024-03-13 00:56:09 +02:00
committed by GitHub
parent 884d649846
commit c1f0f7cb58
24 changed files with 331 additions and 177 deletions

View File

@@ -45069,9 +45069,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) {
@@ -45108,38 +45114,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 {
@@ -45223,6 +45202,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) {

View File

@@ -2484,7 +2484,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
},
@@ -2926,6 +2926,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
@@ -2938,6 +2942,18 @@
"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
@@ -7937,10 +7953,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

View File

@@ -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,