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; return type.baseConstructorType = unknownType;
} }
if (baseConstructorType !== unknownType && baseConstructorType !== nullType && !isConstructorType(baseConstructorType)) { 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; return type.baseConstructorType = unknownType;
} }
type.baseConstructorType = baseConstructorType; type.baseConstructorType = baseConstructorType;
@ -2680,7 +2680,7 @@ module ts {
else { else {
let constructors = getInstantiatedConstructorsForTypeArguments(baseContructorType, baseTypeNode.typeArguments); let constructors = getInstantiatedConstructorsForTypeArguments(baseContructorType, baseTypeNode.typeArguments);
if (!constructors.length) { 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; return;
} }
baseType = getReturnTypeOfSignature(constructors[0]); baseType = getReturnTypeOfSignature(constructors[0]);
@ -2689,7 +2689,7 @@ module ts {
return; return;
} }
if (!(getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface))) { 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; return;
} }
if (type === baseType || hasBaseType(<InterfaceType>baseType, type)) { if (type === baseType || hasBaseType(<InterfaceType>baseType, type)) {
@ -10638,7 +10638,6 @@ module ts {
let baseTypeNode = getClassExtendsHeritageClauseElement(node); let baseTypeNode = getClassExtendsHeritageClauseElement(node);
if (baseTypeNode) { if (baseTypeNode) {
emitExtends = emitExtends || !isInAmbientContext(node); emitExtends = emitExtends || !isInAmbientContext(node);
// !!! checkExpressionWithTypeArguments(baseTypeNode);
let baseTypes = getBaseTypes(type); let baseTypes = getBaseTypes(type);
if (baseTypes.length && produceDiagnostics) { if (baseTypes.length && produceDiagnostics) {
let baseType = baseTypes[0]; 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(type, baseType, node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1);
checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node, checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node,
Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1); 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); checkKindsOfPropertyMemberOverrides(type, baseType);
} }
} }
@ -11997,6 +12002,10 @@ module ts {
return unknownType; return unknownType;
} }
if (isClassExtendsExpressionWithTypeArguments(node)) {
return getBaseTypes(<InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(node.parent.parent)))[0];
}
if (isTypeNode(node)) { if (isTypeNode(node)) {
return getTypeFromTypeNode(<TypeNode>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." }, 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." }, 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_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}'." }, 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_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}'." }, 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", "category": "Error",
"code": 2509 "code": 2509
}, },
"Base constructors must all have the same return type.": {
"category": "Error",
"code": 2510
},
"Import declaration '{0}' is using private name '{1}'.": { "Import declaration '{0}' is using private name '{1}'.": {
"category": "Error", "category": "Error",

View file

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

View file

@ -5811,7 +5811,8 @@ module ts {
node = node.parent; 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 { function isNamespaceReference(node: Node): boolean {