From cc81cc77f902cb986a38911f20d0ab0b17a9ceb3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 15 Jun 2015 15:44:43 -0700 Subject: [PATCH] Check that base constructor return types are identical Treat class extends clause as expression position in services.ts --- src/compiler/checker.ts | 17 +++++++++++++---- .../diagnosticInformationMap.generated.ts | 1 + src/compiler/diagnosticMessages.json | 4 ++++ src/compiler/utilities.ts | 13 ++++++++++--- src/services/services.ts | 3 ++- 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 13235f64b9..8395fc0462 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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(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(getDeclaredTypeOfSymbol(getSymbolOfNode(node.parent.parent)))[0]; + } + if (isTypeNode(node)) { return getTypeFromTypeNode(node); } diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 3c75f388a3..7d61a4bb6c 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -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}'." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index db062b759f..89171ca175 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -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", diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 6c0943f22f..799f9efb44 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -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 === (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 === (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 && + (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 { diff --git a/src/services/services.ts b/src/services/services.ts index 5f030bf320..0ed67f322a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -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(node.parent)); } function isNamespaceReference(node: Node): boolean {