Check that base constructor return types are identical

Treat class extends clause as expression position in services.ts
This commit is contained in:
Anders Hejlsberg 2015-06-15 15:44:43 -07:00
parent 956d73ef5e
commit cc81cc77f9
5 changed files with 30 additions and 8 deletions

View file

@ -2642,7 +2642,7 @@ module ts {
return type.baseConstructorType = unknownType;
}
if (baseConstructorType !== unknownType && baseConstructorType !== nullType && !isConstructorType(baseConstructorType)) {
error(baseTypeNode, Diagnostics.Base_expression_is_not_of_a_constructor_function_type);
error(baseTypeNode.expression, Diagnostics.Base_expression_is_not_of_a_constructor_function_type);
return type.baseConstructorType = unknownType;
}
type.baseConstructorType = baseConstructorType;
@ -2680,7 +2680,7 @@ module ts {
else {
let constructors = getInstantiatedConstructorsForTypeArguments(baseContructorType, baseTypeNode.typeArguments);
if (!constructors.length) {
error(baseTypeNode, Diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments);
error(baseTypeNode.expression, Diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments);
return;
}
baseType = getReturnTypeOfSignature(constructors[0]);
@ -2689,7 +2689,7 @@ module ts {
return;
}
if (!(getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface))) {
error(baseTypeNode, Diagnostics.Base_constructor_does_not_return_a_class_or_interface_type);
error(baseTypeNode.expression, Diagnostics.Base_constructor_does_not_return_a_class_or_interface_type);
return;
}
if (type === baseType || hasBaseType(<InterfaceType>baseType, type)) {
@ -10638,7 +10638,6 @@ module ts {
let baseTypeNode = getClassExtendsHeritageClauseElement(node);
if (baseTypeNode) {
emitExtends = emitExtends || !isInAmbientContext(node);
// !!! checkExpressionWithTypeArguments(baseTypeNode);
let baseTypes = getBaseTypes(type);
if (baseTypes.length && produceDiagnostics) {
let baseType = baseTypes[0];
@ -10650,6 +10649,12 @@ module 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)) {
let constructors = getInstantiatedConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments);
if (forEach(constructors, sig => getReturnTypeOfSignature(sig) !== baseType)) {
error(baseTypeNode.expression, Diagnostics.Base_constructors_must_all_have_the_same_return_type);
}
}
checkKindsOfPropertyMemberOverrides(type, baseType);
}
}
@ -11997,6 +12002,10 @@ module ts {
return unknownType;
}
if (isClassExtendsExpressionWithTypeArguments(node)) {
return getBaseTypes(<InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(node.parent.parent)))[0];
}
if (isTypeNode(node)) {
return getTypeFromTypeNode(<TypeNode>node);
}

View file

@ -383,6 +383,7 @@ module ts {
Base_expression_is_not_of_a_constructor_function_type: { code: 2507, category: DiagnosticCategory.Error, key: "Base expression is not of a constructor function type." },
No_base_constructor_has_the_specified_number_of_type_arguments: { code: 2508, category: DiagnosticCategory.Error, key: "No base constructor has the specified number of type arguments." },
Base_constructor_does_not_return_a_class_or_interface_type: { code: 2509, category: DiagnosticCategory.Error, key: "Base constructor does not return a class or interface type." },
Base_constructors_must_all_have_the_same_return_type: { code: 2510, category: DiagnosticCategory.Error, key: "Base constructors must all have the same return type." },
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

@ -1521,6 +1521,10 @@
"category": "Error",
"code": 2509
},
"Base constructors must all have the same return type.": {
"category": "Error",
"code": 2510
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",

View file

@ -426,7 +426,7 @@ module ts {
// Specialized signatures can have string literals as their parameters' type names
return node.parent.kind === SyntaxKind.Parameter;
case SyntaxKind.ExpressionWithTypeArguments:
return true;
return !isClassExtendsExpressionWithTypeArguments(node);
// Identifiers and qualified names may be type nodes, depending on their context. Climb
// above them to find the lowest container
@ -460,7 +460,7 @@ module ts {
}
switch (parent.kind) {
case SyntaxKind.ExpressionWithTypeArguments:
return true;
return !isClassExtendsExpressionWithTypeArguments(parent);
case SyntaxKind.TypeParameter:
return node === (<TypeParameterDeclaration>parent).constraint;
case SyntaxKind.PropertyDeclaration:
@ -872,7 +872,6 @@ module ts {
while (node.parent.kind === SyntaxKind.QualifiedName) {
node = node.parent;
}
return node.parent.kind === SyntaxKind.TypeQuery;
case SyntaxKind.Identifier:
if (node.parent.kind === SyntaxKind.TypeQuery) {
@ -920,6 +919,8 @@ module ts {
return node === (<ComputedPropertyName>parent).expression;
case SyntaxKind.Decorator:
return true;
case SyntaxKind.ExpressionWithTypeArguments:
return isClassExtendsExpressionWithTypeArguments(parent);
default:
if (isExpression(parent)) {
return true;
@ -1878,6 +1879,12 @@ module ts {
return token >= SyntaxKind.FirstAssignment && token <= SyntaxKind.LastAssignment;
}
export function isClassExtendsExpressionWithTypeArguments(node: Node): boolean {
return node.kind === SyntaxKind.ExpressionWithTypeArguments &&
(<HeritageClause>node.parent).token === SyntaxKind.ExtendsKeyword &&
node.parent.parent.kind === SyntaxKind.ClassDeclaration;
}
// Returns false if this heritage clause element's expression contains something unsupported
// (i.e. not a name or dotted name).
export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): boolean {

View file

@ -5811,7 +5811,8 @@ module ts {
node = node.parent;
}
return node.parent.kind === SyntaxKind.TypeReference || node.parent.kind === SyntaxKind.ExpressionWithTypeArguments;
return node.parent.kind === SyntaxKind.TypeReference ||
(node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isClassExtendsExpressionWithTypeArguments(<ExpressionWithTypeArguments>node.parent));
}
function isNamespaceReference(node: Node): boolean {