From 72fc5dbf9b521eed0a13abab7a8c02fd36514b39 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Wed, 13 Aug 2014 15:12:51 -0700 Subject: [PATCH 1/3] added fullTypeCheckFlag to TypeChecker --- src/compiler/checker.ts | 348 +++++++++++++++++++--------------- src/compiler/parser.ts | 2 +- src/compiler/tsc.ts | 2 +- src/compiler/types.ts | 2 +- src/harness/fourslash.ts | 2 +- src/harness/harness.ts | 2 +- src/harness/projectsRunner.ts | 2 +- src/services/services.ts | 35 ++-- 8 files changed, 223 insertions(+), 172 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a9533171238..7f5ea91631c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11,7 +11,7 @@ module ts { var nextNodeId = 1; var nextMergeId = 1; - export function createTypeChecker(program: Program): TypeChecker { + export function createTypeChecker(program: Program, fullTypeCheck: boolean): TypeChecker { var Symbol = objectAllocator.getSymbolConstructor(); var Type = objectAllocator.getTypeConstructor(); @@ -57,7 +57,6 @@ module ts { var globalRegExpType: ObjectType; var stringLiteralTypes: Map = {}; - var emitExtends = false; var mergedSymbols: Symbol[] = []; @@ -1197,7 +1196,7 @@ module ts { return type; function checkImplicitAny(type: Type) { - if (!program.getCompilerOptions().noImplicitAny) { + if (!fullTypeCheck || !program.getCompilerOptions().noImplicitAny) { return; } // We need to have ended up with 'any', 'any[]', 'any[][]', etc. @@ -3945,7 +3944,7 @@ module ts { var typeArgNode = typeArguments[i]; var typeArgument = getTypeFromTypeNode(typeArgNode); var constraint = getConstraintOfTypeParameter(typeParameters[i]); - if (constraint) { + if (constraint && fullTypeCheck) { checkTypeAssignableTo(typeArgument, constraint, typeArgNode, Diagnostics.Type_0_does_not_satisfy_the_constraint_1_Colon, Diagnostics.Type_0_does_not_satisfy_the_constraint_1); } result.push(typeArgument); @@ -4161,7 +4160,7 @@ module ts { function checkTypeAssertion(node: TypeAssertion): Type { var exprType = checkExpression(node.operand); var targetType = getTypeFromTypeNode(node.type); - if (targetType !== unknownType) { + if (fullTypeCheck && targetType !== unknownType) { var widenedType = getWidenedType(exprType); if (!(isTypeAssignableTo(exprType, targetType) || isTypeAssignableTo(targetType, widenedType))) { checkTypeAssignableTo(targetType, widenedType, node, Diagnostics.Neither_type_0_nor_type_1_is_assignable_to_the_other_Colon, Diagnostics.Neither_type_0_nor_type_1_is_assignable_to_the_other); @@ -4195,7 +4194,7 @@ module ts { var unwidenedType = checkAndMarkExpression(func.body, contextualMapper); var widenedType = getWidenedType(unwidenedType); - if (program.getCompilerOptions().noImplicitAny && widenedType !== unwidenedType && getInnermostTypeOfNestedArrayTypes(widenedType) === anyType) { + if (fullTypeCheck && program.getCompilerOptions().noImplicitAny && widenedType !== unwidenedType && getInnermostTypeOfNestedArrayTypes(widenedType) === anyType) { error(func, Diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeToString(widenedType)); } @@ -4217,7 +4216,7 @@ module ts { var widenedType = getWidenedType(commonType); // Check and report for noImplicitAny if the best common type implicitly gets widened to an 'any'/arrays-of-'any' type. - if (program.getCompilerOptions().noImplicitAny && widenedType !== commonType && getInnermostTypeOfNestedArrayTypes(widenedType) === anyType) { + if (fullTypeCheck && program.getCompilerOptions().noImplicitAny && widenedType !== commonType && getInnermostTypeOfNestedArrayTypes(widenedType) === anyType) { var typeName = typeToString(widenedType); if (func.name) { @@ -4297,6 +4296,10 @@ module ts { // must have at least one return statement somewhere in its body. // An exception to this rule is if the function implementation consists of a single 'throw' statement. function checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(func: FunctionDeclaration, returnType: Type): void { + if (!fullTypeCheck) { + return; + } + // Functions that return 'void' or 'any' don't need any return expressions. if (returnType === voidType || returnType === anyType) { return; @@ -4355,7 +4358,7 @@ module ts { } } } - if (!(links.flags & NodeCheckFlags.TypeChecked)) { + if (fullTypeCheck && !(links.flags & NodeCheckFlags.TypeChecked)) { checkSignatureDeclaration(node); if (node.type) { checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type)); @@ -4600,7 +4603,7 @@ module ts { } function checkAssignmentOperator(valueType: Type): void { - if (operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) { + if (fullTypeCheck && operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) { // TypeScript 1.0 spec (April 2014): 4.17 // An assignment of the form // VarExpr = ValueExpr @@ -4714,28 +4717,32 @@ module ts { // DECLARATION AND STATEMENT TYPE CHECKING function checkTypeParameter(node: TypeParameterDeclaration) { - checkTypeNameIsReserved(node.name, Diagnostics.Type_parameter_name_cannot_be_0); checkSourceElement(node.constraint); - checkTypeParameterHasIllegalReferencesInConstraint(node); + if (fullTypeCheck) { + checkTypeParameterHasIllegalReferencesInConstraint(node); + checkTypeNameIsReserved(node.name, Diagnostics.Type_parameter_name_cannot_be_0); + } // TODO: Check multiple declarations are identical } function checkParameter(parameterDeclaration: ParameterDeclaration) { checkVariableDeclaration(parameterDeclaration); - checkCollisionWithIndexVariableInGeneratedCode(parameterDeclaration, parameterDeclaration.name); + if (fullTypeCheck) { + checkCollisionWithIndexVariableInGeneratedCode(parameterDeclaration, parameterDeclaration.name); - if (parameterDeclaration.flags & (NodeFlags.Public | NodeFlags.Private) && !(parameterDeclaration.parent.kind === SyntaxKind.Constructor && (parameterDeclaration.parent).body)) { - error(parameterDeclaration, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation); - } - if (parameterDeclaration.flags & NodeFlags.Rest) { - if (!isArrayType(getTypeOfSymbol(parameterDeclaration.symbol))) { - error(parameterDeclaration, Diagnostics.A_rest_parameter_must_be_of_an_array_type); + if (parameterDeclaration.flags & (NodeFlags.Public | NodeFlags.Private) && !(parameterDeclaration.parent.kind === SyntaxKind.Constructor && (parameterDeclaration.parent).body)) { + error(parameterDeclaration, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation); } - } - else { - if (parameterDeclaration.initializer && !(parameterDeclaration.parent).body) { - error(parameterDeclaration, Diagnostics.A_parameter_initializer_is_only_allowed_in_a_function_or_constructor_implementation); + if (parameterDeclaration.flags & NodeFlags.Rest) { + if (!isArrayType(getTypeOfSymbol(parameterDeclaration.symbol))) { + error(parameterDeclaration, Diagnostics.A_rest_parameter_must_be_of_an_array_type); + } + } + else { + if (parameterDeclaration.initializer && !(parameterDeclaration.parent).body) { + error(parameterDeclaration, Diagnostics.A_parameter_initializer_is_only_allowed_in_a_function_or_constructor_implementation); + } } } @@ -4779,18 +4786,19 @@ module ts { if (node.type) { checkSourceElement(node.type); } - - checkCollisionWithCapturedSuperVariable(node, node.name); - checkCollisionWithCapturedThisVariable(node, node.name); - checkCollisionWithArgumentsInGeneratedCode(node); - if (program.getCompilerOptions().noImplicitAny && !node.type) { - switch (node.kind) { - case SyntaxKind.ConstructSignature: - error(node, Diagnostics.Construct_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type); - break; - case SyntaxKind.CallSignature: - error(node, Diagnostics.Call_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type); - break; + if (fullTypeCheck) { + checkCollisionWithCapturedSuperVariable(node, node.name); + checkCollisionWithCapturedThisVariable(node, node.name); + checkCollisionWithArgumentsInGeneratedCode(node); + if (program.getCompilerOptions().noImplicitAny && !node.type) { + switch (node.kind) { + case SyntaxKind.ConstructSignature: + error(node, Diagnostics.Construct_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type); + break; + case SyntaxKind.CallSignature: + error(node, Diagnostics.Call_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type); + break; + } } } @@ -4867,6 +4875,10 @@ module ts { return; } + if (!fullTypeCheck) { + return; + } + function isSuperCallExpression(n: Node): boolean { return n.kind === SyntaxKind.CallExpression && (n).func.kind === SyntaxKind.SuperKeyword; } @@ -4931,29 +4943,31 @@ module ts { } function checkAccessorDeclaration(node: AccessorDeclaration) { - if (node.kind === SyntaxKind.GetAccessor) { - if (!isInAmbientContext(node) && node.body && !(bodyContainsAReturnStatement(node.body) || bodyContainsSingleThrowStatement(node.body))) { - error(node.name, Diagnostics.A_get_accessor_must_return_a_value_or_consist_of_a_single_throw_statement); - } - } - - // TypeScript 1.0 spec (April 2014): 8.4.3 - // Accessors for the same member name must specify the same accessibility. - var otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; - var otherAccessor = getDeclarationOfKind(node.symbol, otherKind); - if (otherAccessor) { - var visibilityFlags = NodeFlags.Private | NodeFlags.Public; - if (((node.flags & visibilityFlags) !== (otherAccessor.flags & visibilityFlags))) { - error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility); + if (fullTypeCheck) { + if (node.kind === SyntaxKind.GetAccessor) { + if (!isInAmbientContext(node) && node.body && !(bodyContainsAReturnStatement(node.body) || bodyContainsSingleThrowStatement(node.body))) { + error(node.name, Diagnostics.A_get_accessor_must_return_a_value_or_consist_of_a_single_throw_statement); + } } - var thisType = getAnnotatedAccessorType(node); - var otherType = getAnnotatedAccessorType(otherAccessor); - // TypeScript 1.0 spec (April 2014): 4.5 - // If both accessors include type annotations, the specified types must be identical. - if (thisType && otherType) { - if (!isTypeIdenticalTo(thisType, otherType)) { - error(node, Diagnostics.get_and_set_accessor_must_have_the_same_type); + // TypeScript 1.0 spec (April 2014): 8.4.3 + // Accessors for the same member name must specify the same accessibility. + var otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; + var otherAccessor = getDeclarationOfKind(node.symbol, otherKind); + if (otherAccessor) { + var visibilityFlags = NodeFlags.Private | NodeFlags.Public; + if (((node.flags & visibilityFlags) !== (otherAccessor.flags & visibilityFlags))) { + error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility); + } + + var thisType = getAnnotatedAccessorType(node); + var otherType = getAnnotatedAccessorType(otherAccessor); + // TypeScript 1.0 spec (April 2014): 4.5 + // If both accessors include type annotations, the specified types must be identical. + if (thisType && otherType) { + if (!isTypeIdenticalTo(thisType, otherType)) { + error(node, Diagnostics.get_and_set_accessor_must_have_the_same_type); + } } } } @@ -4970,7 +4984,7 @@ module ts { for (var i = 0; i < len; i++) { checkSourceElement(node.typeArguments[i]); var constraint = getConstraintOfTypeParameter((type).target.typeParameters[i]); - if (constraint) { + if (fullTypeCheck && constraint) { var typeArgument = (type).typeArguments[i]; checkTypeAssignableTo(typeArgument, constraint, node, Diagnostics.Type_0_does_not_satisfy_the_constraint_1_Colon, Diagnostics.Type_0_does_not_satisfy_the_constraint_1); } @@ -4984,9 +4998,11 @@ module ts { function checkTypeLiteral(node: TypeLiteralNode) { forEach(node.members, checkSourceElement); - var type = getTypeFromTypeLiteralNode(node); - checkIndexConstraints(type); - checkTypeForDuplicateIndexSignatures(node); + if (fullTypeCheck) { + var type = getTypeFromTypeLiteralNode(node); + checkIndexConstraints(type); + checkTypeForDuplicateIndexSignatures(node); + } } function checkArrayType(node: ArrayTypeNode) { @@ -4998,6 +5014,9 @@ module ts { } function checkSpecializedSignatureDeclaration(signatureDeclarationNode: SignatureDeclaration): void { + if (!fullTypeCheck) { + return; + } var signature = getSignatureFromDeclaration(signatureDeclarationNode); if (!signature.hasStringLiterals) { return; @@ -5051,7 +5070,10 @@ module ts { return flags & flagsToCheck; } - function checkFunctionOrConstructorSymbol(symbol: Symbol) { + function checkFunctionOrConstructorSymbol(symbol: Symbol): void { + if (!fullTypeCheck) { + return; + } function checkFlagAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionDeclaration, flagsToCheck: NodeFlags, someOverloadFlags: NodeFlags, allOverloadFlags: NodeFlags): void { // Error if some overloads have a flag that is not shared by all overloads. To find the @@ -5221,7 +5243,11 @@ module ts { } } - function checkExportsOnMergedDeclarations(node: Node) { + function checkExportsOnMergedDeclarations(node: Node): void { + if (!fullTypeCheck) { + return; + } + var symbol: Symbol; // Exports should be checked only if enclosing module contains both exported and non exported declarations. @@ -5291,7 +5317,7 @@ module ts { } } - function checkFunctionDeclaration(node: FunctionDeclaration) { + function checkFunctionDeclaration(node: FunctionDeclaration): void { checkSignatureDeclaration(node); var symbol = getSymbolOfNode(node); @@ -5320,7 +5346,7 @@ module ts { } // If there is no body and no explicit return type, then report an error. - if (program.getCompilerOptions().noImplicitAny && !node.body && !node.type) { + if (fullTypeCheck && program.getCompilerOptions().noImplicitAny && !node.body && !node.type) { // Ignore privates within ambient contexts; they exist purely for documentative purposes to avoid name clashing. // (e.g. privates within .d.ts files do not expose type information) if (!isPrivateWithinAmbient(node)) { @@ -5507,36 +5533,38 @@ module ts { function checkVariableDeclaration(node: VariableDeclaration) { checkSourceElement(node.type); - checkExportsOnMergedDeclarations(node); - var symbol = getSymbolOfNode(node); - - var typeOfValueDeclaration = getTypeOfVariableOrParameterOrProperty(symbol); - var type: Type; - var useTypeFromValueDeclaration = node === symbol.valueDeclaration; - if (useTypeFromValueDeclaration) { - type = typeOfValueDeclaration; - } - else { - type = getTypeOfVariableDeclaration(node); - } + if (fullTypeCheck) { + var symbol = getSymbolOfNode(node); - if (node.initializer) { - if (!(getNodeLinks(node.initializer).flags & NodeCheckFlags.TypeChecked)) { - // Use default messages - checkTypeAssignableTo(checkAndMarkExpression(node.initializer), type, node, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); + var typeOfValueDeclaration = getTypeOfVariableOrParameterOrProperty(symbol); + var type: Type; + var useTypeFromValueDeclaration = node === symbol.valueDeclaration; + if (useTypeFromValueDeclaration) { + type = typeOfValueDeclaration; + } + else { + type = getTypeOfVariableDeclaration(node); } - } - checkCollisionWithCapturedSuperVariable(node, node.name); - checkCollisionWithCapturedThisVariable(node, node.name); - if (!useTypeFromValueDeclaration) { - // TypeScript 1.0 spec (April 2014): 5.1 - // Multiple declarations for the same variable name in the same declaration space are permitted, - // provided that each declaration associates the same type with the variable. - if (typeOfValueDeclaration !== unknownType && type !== unknownType && !isTypeIdenticalTo(typeOfValueDeclaration, type)) { - error(node.name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, identifierToString(node.name), typeToString(typeOfValueDeclaration), typeToString(type)); + + if (node.initializer) { + if (!(getNodeLinks(node.initializer).flags & NodeCheckFlags.TypeChecked)) { + // Use default messages + checkTypeAssignableTo(checkAndMarkExpression(node.initializer), type, node, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); + } + } + + checkCollisionWithCapturedSuperVariable(node, node.name); + checkCollisionWithCapturedThisVariable(node, node.name); + if (!useTypeFromValueDeclaration) { + // TypeScript 1.0 spec (April 2014): 5.1 + // Multiple declarations for the same variable name in the same declaration space are permitted, + // provided that each declaration associates the same type with the variable. + if (typeOfValueDeclaration !== unknownType && type !== unknownType && !isTypeIdenticalTo(typeOfValueDeclaration, type)) { + error(node.name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, identifierToString(node.name), typeToString(typeOfValueDeclaration), typeToString(type)); + } } } } @@ -5668,7 +5696,7 @@ module ts { function checkSwitchStatement(node: SwitchStatement) { var expressionType = checkExpression(node.expression); forEach(node.clauses, clause => { - if (clause.expression) { + if (fullTypeCheck && clause.expression) { // TypeScript 1.0 spec (April 2014):5.9 // In a 'switch' statement, each 'case' expression must be of a type that is assignable to or from the type of the 'switch' expression. var caseType = checkExpression(clause.expression); @@ -5783,9 +5811,12 @@ module ts { for (var i = 0; i < typeParameterDeclarations.length; i++) { var node = typeParameterDeclarations[i]; checkTypeParameter(node); - for (var j = 0; j < i; j++) { - if (typeParameterDeclarations[j].symbol === node.symbol) { - error(node.name, Diagnostics.Duplicate_identifier_0, identifierToString(node.name)); + + if (fullTypeCheck) { + for (var j = 0; j < i; j++) { + if (typeParameterDeclarations[j].symbol === node.symbol) { + error(node.name, Diagnostics.Duplicate_identifier_0, identifierToString(node.name)); + } } } } @@ -5805,39 +5836,45 @@ module ts { checkTypeReference(node.baseType); } if (type.baseTypes.length) { - var baseType = type.baseTypes[0]; - checkTypeAssignableTo(type, baseType, node.name, Diagnostics.Class_0_incorrectly_extends_base_class_1_Colon, Diagnostics.Class_0_incorrectly_extends_base_class_1); - var staticBaseType = getTypeOfSymbol(baseType.symbol); - checkTypeAssignableTo(staticType, getTypeWithoutConstructors(staticBaseType), node.name, - Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1_Colon, Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1); - if (baseType.symbol !== resolveEntityName(node, node.baseType.typeName, SymbolFlags.Value)) { - error(node.baseType, Diagnostics.Type_name_0_in_extends_clause_does_not_reference_constructor_function_for_0, typeToString(baseType)); + if (fullTypeCheck) { + var baseType = type.baseTypes[0]; + checkTypeAssignableTo(type, baseType, node.name, Diagnostics.Class_0_incorrectly_extends_base_class_1_Colon, Diagnostics.Class_0_incorrectly_extends_base_class_1); + var staticBaseType = getTypeOfSymbol(baseType.symbol); + checkTypeAssignableTo(staticType, getTypeWithoutConstructors(staticBaseType), node.name, + Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1_Colon, Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1); + if (baseType.symbol !== resolveEntityName(node, node.baseType.typeName, SymbolFlags.Value)) { + error(node.baseType, Diagnostics.Type_name_0_in_extends_clause_does_not_reference_constructor_function_for_0, typeToString(baseType)); + } + + checkKindsOfPropertyMemberOverrides(type, baseType); } // Check that base type can be evaluated as expression checkExpression(node.baseType.typeName); - - checkKindsOfPropertyMemberOverrides(type, baseType); } if (node.implementedTypes) { forEach(node.implementedTypes, typeRefNode => { checkTypeReference(typeRefNode); - var t = getTypeFromTypeReferenceNode(typeRefNode); - if (t !== unknownType) { - var declaredType = (t.flags & TypeFlags.Reference) ? (t).target : t; - if (declaredType.flags & (TypeFlags.Class | TypeFlags.Interface)) { - checkTypeAssignableTo(type, t, node.name, Diagnostics.Class_0_incorrectly_implements_interface_1_Colon, Diagnostics.Class_0_incorrectly_implements_interface_1); - } - else { - error(typeRefNode, Diagnostics.A_class_may_only_implement_another_class_or_interface); + if (fullTypeCheck) { + var t = getTypeFromTypeReferenceNode(typeRefNode); + if (t !== unknownType) { + var declaredType = (t.flags & TypeFlags.Reference) ? (t).target : t; + if (declaredType.flags & (TypeFlags.Class | TypeFlags.Interface)) { + checkTypeAssignableTo(type, t, node.name, Diagnostics.Class_0_incorrectly_implements_interface_1_Colon, Diagnostics.Class_0_incorrectly_implements_interface_1); + } + else { + error(typeRefNode, Diagnostics.A_class_may_only_implement_another_class_or_interface); + } } } }); } - checkIndexConstraints(type); - forEach(node.members, checkSourceElement); - checkTypeForDuplicateIndexSignatures(node); + forEach(node.members, checkSourceElement); + if (fullTypeCheck) { + checkIndexConstraints(type); + checkTypeForDuplicateIndexSignatures(node); + } } function getTargetSymbol(s: Symbol) { @@ -5949,32 +5986,37 @@ module ts { } function checkInterfaceDeclaration(node: InterfaceDeclaration) { - checkTypeNameIsReserved(node.name, Diagnostics.Interface_name_cannot_be_0); checkTypeParameters(node.typeParameters); - checkExportsOnMergedDeclarations(node); - var symbol = getSymbolOfNode(node); - var firstInterfaceDecl = getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration); - if (symbol.declarations.length > 1) { - if (node !== firstInterfaceDecl && !areTypeParametersIdentical(firstInterfaceDecl.typeParameters, node.typeParameters)) { - error(node.name, Diagnostics.All_declarations_of_an_interface_must_have_identical_type_parameters); - } - } + if (fullTypeCheck) { + checkTypeNameIsReserved(node.name, Diagnostics.Interface_name_cannot_be_0); - // Only check this symbol once - if (node === firstInterfaceDecl) { - var type = getDeclaredTypeOfSymbol(symbol); - // run subsequent checks only if first set succeeded - if (checkInheritedPropertiesAreIdentical(type, node.name)) { - forEach(type.baseTypes, baseType => { - checkTypeAssignableTo(type, baseType, node.name, Diagnostics.Interface_0_incorrectly_extends_interface_1_Colon, Diagnostics.Interface_0_incorrectly_extends_interface_1); - }); - checkIndexConstraints(type); + checkExportsOnMergedDeclarations(node); + var symbol = getSymbolOfNode(node); + var firstInterfaceDecl = getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration); + if (symbol.declarations.length > 1) { + if (node !== firstInterfaceDecl && !areTypeParametersIdentical(firstInterfaceDecl.typeParameters, node.typeParameters)) { + error(node.name, Diagnostics.All_declarations_of_an_interface_must_have_identical_type_parameters); + } + } + + // Only check this symbol once + if (node === firstInterfaceDecl) { + var type = getDeclaredTypeOfSymbol(symbol); + // run subsequent checks only if first set succeeded + if (checkInheritedPropertiesAreIdentical(type, node.name)) { + forEach(type.baseTypes, baseType => { + checkTypeAssignableTo(type, baseType, node.name, Diagnostics.Interface_0_incorrectly_extends_interface_1_Colon, Diagnostics.Interface_0_incorrectly_extends_interface_1); + }); + checkIndexConstraints(type); + } } } forEach(node.baseTypes, checkTypeReference); forEach(node.members, checkSourceElement); - checkTypeForDuplicateIndexSignatures(node); + if (fullTypeCheck) { + checkTypeForDuplicateIndexSignatures(node); + } } function getConstantValue(node: Expression): number { @@ -5995,6 +6037,9 @@ module ts { } function checkEnumDeclaration(node: EnumDeclaration) { + if (!fullTypeCheck) { + return; + } checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0); checkCollisionWithCapturedThisVariable(node, node.name); checkExportsOnMergedDeclarations(node); @@ -6068,26 +6113,28 @@ module ts { } function checkModuleDeclaration(node: ModuleDeclaration) { - checkCollisionWithCapturedThisVariable(node, node.name); - checkExportsOnMergedDeclarations(node); - var symbol = getSymbolOfNode(node); - if (symbol.flags & SymbolFlags.ValueModule && symbol.declarations.length > 1 && !isInAmbientContext(node)) { - var classOrFunc = getFirstNonAmbientClassOrFunctionDeclaration(symbol); - if (classOrFunc) { - if (getSourceFileOfNode(node) !== getSourceFileOfNode(classOrFunc)) { - error(node.name, Diagnostics.A_module_declaration_cannot_be_in_a_different_file_from_a_class_or_function_with_which_it_is_merged); - } - else if (node.pos < classOrFunc.pos) { - error(node.name, Diagnostics.A_module_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged); + if (fullTypeCheck) { + checkCollisionWithCapturedThisVariable(node, node.name); + checkExportsOnMergedDeclarations(node); + var symbol = getSymbolOfNode(node); + if (symbol.flags & SymbolFlags.ValueModule && symbol.declarations.length > 1 && !isInAmbientContext(node)) { + var classOrFunc = getFirstNonAmbientClassOrFunctionDeclaration(symbol); + if (classOrFunc) { + if (getSourceFileOfNode(node) !== getSourceFileOfNode(classOrFunc)) { + error(node.name, Diagnostics.A_module_declaration_cannot_be_in_a_different_file_from_a_class_or_function_with_which_it_is_merged); + } + else if (node.pos < classOrFunc.pos) { + error(node.name, Diagnostics.A_module_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged); + } } } - } - if (node.name.kind === SyntaxKind.StringLiteral) { - if (!isGlobalSourceFile(node.parent)) { - error(node.name, Diagnostics.Ambient_external_modules_cannot_be_nested_in_other_modules); - } - if (isExternalModuleNameRelative(node.name.text)) { - error(node.name, Diagnostics.Ambient_external_module_declaration_cannot_specify_relative_module_name); + if (node.name.kind === SyntaxKind.StringLiteral) { + if (!isGlobalSourceFile(node.parent)) { + error(node.name, Diagnostics.Ambient_external_modules_cannot_be_nested_in_other_modules); + } + if (isExternalModuleNameRelative(node.name.text)) { + error(node.name, Diagnostics.Ambient_external_module_declaration_cannot_specify_relative_module_name); + } } } checkSourceElement(node.body); @@ -6248,8 +6295,7 @@ module ts { } } - // Fully type check a source file and collect the relevant diagnostics. The fullTypeCheck flag is true during this - // operation, but otherwise is false when the compiler is used by the language service. + // Fully type check a source file and collect the relevant diagnostics. function checkSourceFile(node: SourceFile) { var links = getNodeLinks(node); if (!(links.flags & NodeCheckFlags.TypeChecked)) { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 91a30143047..a4cad6f5c17 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3582,7 +3582,7 @@ module ts { getCompilerHost: () => host, getDiagnostics: getDiagnostics, getGlobalDiagnostics: getGlobalDiagnostics, - getTypeChecker: () => createTypeChecker(program), + getTypeChecker: fullTypeCheckMode => createTypeChecker(program, fullTypeCheckMode), getCommonSourceDirectory: () => commonSourceDirectory, }; return program; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 054b76c55b2..4d945566506 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -326,7 +326,7 @@ module ts { var reportStart = bindStart; } else { - var checker = program.getTypeChecker(); + var checker = program.getTypeChecker(/*fullTypeCheckMode*/ true); var checkStart = new Date().getTime(); var semanticErrors = checker.getDiagnostics(); var emitStart = new Date().getTime(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5cb80687d80..ab232c2217b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -537,7 +537,7 @@ module ts { getCompilerHost(): CompilerHost; getDiagnostics(sourceFile?: SourceFile): Diagnostic[]; getGlobalDiagnostics(): Diagnostic[]; - getTypeChecker(): TypeChecker; + getTypeChecker(fullTypeCheckMode: boolean): TypeChecker; getCommonSourceDirectory(): string; } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index da649f77dd9..0fa27f0d1fa 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1904,7 +1904,7 @@ module FourSlash { var host = Harness.Compiler.createCompilerHost(files, (fn, contents) => result = contents); var program = ts.createProgram([fourslashFilename, fileName], { out: "fourslashTestOutput.js" }, host); - var checker = ts.createTypeChecker(program); + var checker = ts.createTypeChecker(program, /*fullTypeCheckMode*/ true); checker.checkProgram(); var errs = checker.getDiagnostics(files[fileName]); diff --git a/src/harness/harness.ts b/src/harness/harness.ts index dce80544222..0a1876b2678 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -736,7 +736,7 @@ module Harness { var hadParseErrors = program.getDiagnostics().length > 0; - var checker = program.getTypeChecker(); + var checker = program.getTypeChecker(/*fullTypeCheckMode*/ true); checker.checkProgram(); // only emit if there weren't parse errors diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index 5b4b1712e8e..59bcc2efc76 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -229,7 +229,7 @@ class ProjectRunner extends RunnerBase { var program = ts.createProgram(testCase.inputFiles, createCompilerOptions(), createCompilerHost()); var errors = program.getDiagnostics(); if (!errors.length) { - var checker = program.getTypeChecker(); + var checker = program.getTypeChecker(/*fullTypeCheckMode*/ true); errors = checker.getDiagnostics(); var emitResult = checker.emitFiles(); errors = ts.concatenate(errors, emitResult.errors); diff --git a/src/services/services.ts b/src/services/services.ts index 06bfbd80e05..d426c7c7905 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1256,7 +1256,10 @@ module ts { var formattingRulesProvider: TypeScript.Services.Formatting.RulesProvider; var hostCache: HostCache; // A cache of all the information about the files on the host side. var program: Program; - var typeChecker: TypeChecker; + // this checker is used to answer all LS questions except errors + var typeInfoResolver: TypeChecker; + // the sole purpose of this checkes is to reutrn semantic diagnostics + var fullTypeCheckChecker: TypeChecker; var useCaseSensitivefilenames = false; var sourceFilesByName: Map = {}; var documentRegistry = documentRegistry; @@ -1403,7 +1406,8 @@ module ts { // Now create a new compiler program = createProgram(hostfilenames, compilationSettings, createCompilerHost()); - typeChecker = program.getTypeChecker(); + typeInfoResolver = program.getTypeChecker(/*fullTypeCheckMode*/ false); + fullTypeCheckChecker = program.getTypeChecker(/*fullTypeCheckMode*/ true); } /// Clean up any semantic caches that are not needed. @@ -1411,7 +1415,8 @@ module ts { /// We will just dump the typeChecker and recreate a new one. this should have the effect of destroying all the semantic caches. function cleanupSemanticCache(): void { if (program) { - typeChecker = program.getTypeChecker(); + typeInfoResolver = program.getTypeChecker(/*fullTypeCheckMode*/ false); + fullTypeCheckChecker = program.getTypeChecker(/*fullTypeCheckMode*/ true); } } @@ -1436,7 +1441,7 @@ module ts { filename = TypeScript.switchToForwardSlashes(filename) - return typeChecker.getDiagnostics(getSourceFile(filename).getSourceFile()); + return fullTypeCheckChecker.getDiagnostics(getSourceFile(filename)); } function getCompilerOptionsDiagnostics() { @@ -1678,12 +1683,12 @@ module ts { entries: [], symbols: {}, location: mappedNode, - typeChecker: typeChecker + typeChecker: typeInfoResolver }; // Right of dot member completion list if (isRightOfDot) { - var type: Type = typeChecker.getTypeOfExpression(mappedNode); + var type: Type = typeInfoResolver.getTypeOfExpression(mappedNode); if (!type) { return undefined; } @@ -1731,7 +1736,7 @@ module ts { isMemberCompletion = false; /// TODO filter meaning based on the current context var symbolMeanings = SymbolFlags.Type | SymbolFlags.Value | SymbolFlags.Namespace; - var symbols = typeChecker.getSymbolsInScope(mappedNode, symbolMeanings); + var symbols = typeInfoResolver.getSymbolsInScope(mappedNode, symbolMeanings); getCompletionEntriesFromSymbols(symbols, activeCompletionSession); } @@ -1770,7 +1775,7 @@ module ts { kind: completionEntry.kind, kindModifiers: completionEntry.kindModifiers, type: session.typeChecker.typeToString(type, session.location), - fullSymbolName: typeChecker.symbolToString(symbol, session.location), + fullSymbolName: typeInfoResolver.symbolToString(symbol, session.location), docComment: "" }; } @@ -1882,13 +1887,13 @@ module ts { var node = getNodeAtPosition(sourceFile.getSourceFile(), position); if (!node) return undefined; - var symbol = typeChecker.getSymbolInfo(node); - var type = symbol && typeChecker.getTypeOfSymbol(symbol); + var symbol = typeInfoResolver.getSymbolInfo(node); + var type = symbol && typeInfoResolver.getTypeOfSymbol(symbol); if (type) { return { - memberName: new TypeScript.MemberNameString(typeChecker.typeToString(type)), + memberName: new TypeScript.MemberNameString(typeInfoResolver.typeToString(type)), docComment: "", - fullSymbolName: typeChecker.symbolToString(symbol, getContainerNode(node)), + fullSymbolName: typeInfoResolver.symbolToString(symbol, getContainerNode(node)), kind: getSymbolKind(symbol), minChar: node.pos, limChar: node.end @@ -2034,7 +2039,7 @@ module ts { return undefined; } - var symbol = typeChecker.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolInfo(node); // Could not find a symbol e.g. node is string or number keyword, // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol @@ -2045,10 +2050,10 @@ module ts { var result: DefinitionInfo[] = []; var declarations = symbol.getDeclarations(); - var symbolName = typeChecker.symbolToString(symbol, node); + var symbolName = typeInfoResolver.symbolToString(symbol, node); var symbolKind = getSymbolKind(symbol); var containerSymbol = symbol.parent; - var containerName = containerSymbol ? typeChecker.symbolToString(containerSymbol, node) : ""; + var containerName = containerSymbol ? typeInfoResolver.symbolToString(containerSymbol, node) : ""; var containerKind = containerSymbol ? getSymbolKind(symbol) : ""; if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && From 8475a33bc68dad1c026dfc4b52e429ba58b5d316 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Wed, 13 Aug 2014 16:22:56 -0700 Subject: [PATCH 2/3] added comment to fullTypeCheck parameter --- src/compiler/checker.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7f5ea91631c..2daeab457ee 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11,6 +11,10 @@ module ts { var nextNodeId = 1; var nextMergeId = 1; + /// fullTypeCheck denotes if this instance of the typechecker will be used to get semantic diagnostics. + /// If fullTypeCheck === true - then typechecker should do every possible check to produce all errors + /// If fullTypeCheck === false - typechecker can shortcut and skip checks that only produce errors. + /// NOTE: checks that somehow affects decisions being made during typechecking should be executed in both cases. export function createTypeChecker(program: Program, fullTypeCheck: boolean): TypeChecker { var Symbol = objectAllocator.getSymbolConstructor(); @@ -6322,7 +6326,9 @@ module ts { forEach(program.getSourceFiles(), checkSourceFile); } - function getSortedDiagnostics(): Diagnostic[] { + function getSortedDiagnostics(): Diagnostic[]{ + Debug.assert(fullTypeCheck, "diagnostics are available only in the full typecheck mode"); + if (diagnosticsModified) { diagnostics.sort(compareDiagnostics); diagnostics = deduplicateSortedDiagnostics(diagnostics); @@ -6331,7 +6337,8 @@ module ts { return diagnostics; } - function getDiagnostics(sourceFile?: SourceFile): Diagnostic[] { + function getDiagnostics(sourceFile?: SourceFile): Diagnostic[]{ + if (sourceFile) { checkSourceFile(sourceFile); return filter(getSortedDiagnostics(), d => d.file === sourceFile); From 848e5db646e6b0f994cb2c7520c2b5ab1ec25aa1 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Thu, 14 Aug 2014 17:57:17 -0700 Subject: [PATCH 3/3] defer creation of fullTypeCheckChecker --- src/services/services.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index d426c7c7905..f1a8960c54b 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1259,7 +1259,8 @@ module ts { // this checker is used to answer all LS questions except errors var typeInfoResolver: TypeChecker; // the sole purpose of this checkes is to reutrn semantic diagnostics - var fullTypeCheckChecker: TypeChecker; + // creation is deferred - use getFullTypeCheckChecker to get instance + var fullTypeCheckChecker_doNotAccessDirectly: TypeChecker; var useCaseSensitivefilenames = false; var sourceFilesByName: Map = {}; var documentRegistry = documentRegistry; @@ -1275,6 +1276,10 @@ module ts { return lookUp(sourceFilesByName, filename); } + function getFullTypeCheckChecker() { + return fullTypeCheckChecker_doNotAccessDirectly || (fullTypeCheckChecker_doNotAccessDirectly = program.getTypeChecker(/*fullTypeCheck*/ true)); + } + function createCompilerHost(): CompilerHost { return { getSourceFile: (filename, languageVersion) => { @@ -1407,7 +1412,7 @@ module ts { // Now create a new compiler program = createProgram(hostfilenames, compilationSettings, createCompilerHost()); typeInfoResolver = program.getTypeChecker(/*fullTypeCheckMode*/ false); - fullTypeCheckChecker = program.getTypeChecker(/*fullTypeCheckMode*/ true); + fullTypeCheckChecker_doNotAccessDirectly = undefined; } /// Clean up any semantic caches that are not needed. @@ -1416,7 +1421,7 @@ module ts { function cleanupSemanticCache(): void { if (program) { typeInfoResolver = program.getTypeChecker(/*fullTypeCheckMode*/ false); - fullTypeCheckChecker = program.getTypeChecker(/*fullTypeCheckMode*/ true); + fullTypeCheckChecker_doNotAccessDirectly = undefined; } } @@ -1441,7 +1446,7 @@ module ts { filename = TypeScript.switchToForwardSlashes(filename) - return fullTypeCheckChecker.getDiagnostics(getSourceFile(filename)); + return getFullTypeCheckChecker().getDiagnostics(getSourceFile(filename)); } function getCompilerOptionsDiagnostics() {