check for inheriting abstract member functions

This commit is contained in:
Arthur Ozga 2015-06-18 12:19:07 -07:00
parent a07f86240d
commit 5ca3955473
3 changed files with 88 additions and 42 deletions

View file

@ -4561,7 +4561,24 @@ namespace ts {
let requireOptionalProperties = relation === subtypeRelation && !(source.flags & TypeFlags.ObjectLiteral);
for (let targetProp of properties) {
let sourceProp = getPropertyOfType(source, targetProp.name);
if (sourceProp !== targetProp) {
if (sourceProp === targetProp) { // source inherits targetProp and doesn't redeclare/override it.
if (source.flags & TypeFlags.Class && target.flags & TypeFlags.Class) {
let targetPropFlags = getDeclarationFlagsFromSymbol(targetProp);
let sourceDecl = getDeclarationOfKind(source.symbol, SyntaxKind.ClassDeclaration);
// if target is a class and it has an abstract method, then source, inheriting that method, must be declared abstract.
if (targetPropFlags & NodeFlags.Abstract && !(sourceDecl.flags & NodeFlags.Abstract)) {
if (reportErrors) {
reportError(Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_2,
typeToString(source), typeToString(target), symbolToString(targetProp));
}
return Ternary.False;
}
}
} else { // sourceProp !== targetProp -- ie: source and target have distinct declarations with the same name
if (!sourceProp) {
if (!(targetProp.flags & SymbolFlags.Optional) || requireOptionalProperties) {
if (reportErrors) {
@ -4571,24 +4588,24 @@ namespace ts {
}
}
else if (!(targetProp.flags & SymbolFlags.Prototype)) {
let sourceFlags = getDeclarationFlagsFromSymbol(sourceProp);
let targetFlags = getDeclarationFlagsFromSymbol(targetProp);
if (sourceFlags & NodeFlags.Private || targetFlags & NodeFlags.Private) {
let sourcePropFlags = getDeclarationFlagsFromSymbol(sourceProp);
let targetPropFlags = getDeclarationFlagsFromSymbol(targetProp);
if (sourcePropFlags & NodeFlags.Private || targetPropFlags & NodeFlags.Private) {
if (sourceProp.valueDeclaration !== targetProp.valueDeclaration) {
if (reportErrors) {
if (sourceFlags & NodeFlags.Private && targetFlags & NodeFlags.Private) {
if (sourcePropFlags & NodeFlags.Private && targetPropFlags & NodeFlags.Private) {
reportError(Diagnostics.Types_have_separate_declarations_of_a_private_property_0, symbolToString(targetProp));
}
else {
reportError(Diagnostics.Property_0_is_private_in_type_1_but_not_in_type_2, symbolToString(targetProp),
typeToString(sourceFlags & NodeFlags.Private ? source : target),
typeToString(sourceFlags & NodeFlags.Private ? target : source));
typeToString(sourcePropFlags & NodeFlags.Private ? source : target),
typeToString(sourcePropFlags & NodeFlags.Private ? target : source));
}
}
return Ternary.False;
}
}
else if (targetFlags & NodeFlags.Protected) {
else if (targetPropFlags & NodeFlags.Protected) {
let sourceDeclaredInClass = sourceProp.parent && sourceProp.parent.flags & SymbolFlags.Class;
let sourceClass = sourceDeclaredInClass ? <InterfaceType>getDeclaredTypeOfSymbol(sourceProp.parent) : undefined;
let targetClass = <InterfaceType>getDeclaredTypeOfSymbol(targetProp.parent);
@ -4600,7 +4617,7 @@ namespace ts {
return Ternary.False;
}
}
else if (sourceFlags & NodeFlags.Protected) {
else if (sourcePropFlags & NodeFlags.Protected) {
if (reportErrors) {
reportError(Diagnostics.Property_0_is_protected_in_type_1_but_public_in_type_2,
symbolToString(targetProp), typeToString(source), typeToString(target));
@ -6637,7 +6654,7 @@ namespace ts {
return s.valueDeclaration ? s.valueDeclaration.kind : SyntaxKind.PropertyDeclaration;
}
function getDeclarationFlagsFromSymbol(s: Symbol) {
function getDeclarationFlagsFromSymbol(s: Symbol): NodeFlags {
return s.valueDeclaration ? getCombinedNodeFlags(s.valueDeclaration) : s.flags & SymbolFlags.Prototype ? NodeFlags.Public | NodeFlags.Static : 0;
}
@ -10627,6 +10644,9 @@ namespace ts {
checkTypeAssignableTo(type, baseType, node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1);
checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node,
Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1);
if (!(staticBaseType.symbol && staticBaseType.symbol.flags & SymbolFlags.Class)) {
// When the static base type is a "class-like" constructor function (but not actually a class), we verify
// that all instantiated base constructor signatures return the same type.
@ -10662,6 +10682,11 @@ namespace ts {
}
forEach(node.members, checkSourceElement);
// Classes containing abstract members must be marked abstract
if (!(node.flags & NodeFlags.Abstract) && forEach(node.members, (element: ClassElement) => element.flags & NodeFlags.Abstract)) {
error(node, Diagnostics.Classes_containing_abstract_functions_must_be_marked_abstract);
}
if (produceDiagnostics) {
checkIndexConstraints(type);
checkTypeForDuplicateIndexSignatures(node);
@ -10700,8 +10725,19 @@ namespace ts {
}
let derived = getTargetSymbol(getPropertyOfObjectType(type, base.name));
if (derived) {
let baseDeclarationFlags = getDeclarationFlagsFromSymbol(base);
if (!derived) { // derived class inherits base without override/redeclaration
let derivedClassDecl = getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration);
Debug.assert(derivedClassDecl !== undefined);
// It is an error to inherit an abstract member without implementing it or being declared abstract.
if ((baseDeclarationFlags & NodeFlags.Abstract) && !(derivedClassDecl.flags & NodeFlags.Abstract)) {
error(derivedClassDecl, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_2,
typeToString(type), typeToString(baseType), symbolToString(baseProperty));
}
} else { // derived !== undefined -- derived overrides base
let derivedDeclarationFlags = getDeclarationFlagsFromSymbol(derived);
if ((baseDeclarationFlags & NodeFlags.Private) || (derivedDeclarationFlags & NodeFlags.Private)) {
// either base or derived property is private - not override, skip it

View file

@ -396,6 +396,8 @@ namespace ts {
Cannot_create_an_instance_of_the_abstract_class_0: { code: 2511, category: DiagnosticCategory.Error, key: "Cannot create an instance of the abstract class '{0}'." },
All_overload_signatures_must_match_with_respect_to_modifier_0: { code: 2512, category: DiagnosticCategory.Error, key: "All overload signatures must match with respect to modifier '{0}'." },
Abstract_member_function_0_on_type_1_cannot_be_called_via_super_expression: { code: 2513, category: DiagnosticCategory.Error, key: "Abstract member function '{0}' on type '{1}' cannot be called via super expression." },
Classes_containing_abstract_functions_must_be_marked_abstract: { code: 2514, category: DiagnosticCategory.Error, key: "Classes containing abstract functions must be marked abstract." },
Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_2: { code: 2515, category: DiagnosticCategory.Error, key: "Non-abstract class '{0}' does not implement inherited abstract member '{1}.{2}'." },
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." },

View file

@ -1571,6 +1571,14 @@
"category": "Error",
"code": 2513
},
"Classes containing abstract functions must be marked abstract.": {
"category": "Error",
"code": 2514
},
"Non-abstract class '{0}' does not implement inherited abstract member '{1}.{2}'." : {
"category": "Error",
"code": 2515
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
"code": 4000