diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 7b16c785833..bae4b6abb3c 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -252,7 +252,7 @@ module ts { function bindCatchVariableDeclaration(node: CatchBlock) { var symbol = createSymbol(SymbolFlags.Variable, node.variable.text || "__missing"); - addDeclarationToSymbol(symbol, node.variable, SymbolFlags.Variable); + addDeclarationToSymbol(symbol, node, SymbolFlags.Variable); var saveParent = parent; parent = node; forEachChild(node, bind); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9b014e744a7..8f76127af76 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -41,6 +41,9 @@ module ts { var anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); var noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); + var anySignature = createSignature(undefined, undefined, emptyArray, anyType, 0, false, false); + var unknownSignature = createSignature(undefined, undefined, emptyArray, unknownType, 0, false, false); + var globals: SymbolTable = {}; var globalObjectType: ObjectType; @@ -53,6 +56,7 @@ module ts { var stringLiteralTypes: Map = {}; + var fullTypeCheck = false; var emitExtends = false; var mergedSymbols: Symbol[] = []; @@ -327,7 +331,7 @@ module ts { case SyntaxKind.CatchBlock: var id = (location).variable; if (name === id.text) { - return returnResolvedSymbol((location).variable.symbol); + return returnResolvedSymbol(location.symbol); } break; } @@ -1104,129 +1108,99 @@ module ts { } function getTypeOfVariableDeclaration(declaration: VariableDeclaration): Type { - var type: Type; - - if (declaration.parent.kind === SyntaxKind.CatchBlock || declaration.parent.kind === SyntaxKind.ForInStatement) { - type = anyType; + // A variable declared in a for..in statement is always of type any + if (declaration.parent.kind === SyntaxKind.ForInStatement) { + return anyType; } - else if (declaration.type) { - type = getTypeFromTypeNode(declaration.type); + // Use type from type annotation if one is present + if (declaration.type) { + return getTypeFromTypeNode(declaration.type); } - else { - // TypeScript 1.0 spec (April 2014): - // If only one accessor includes a type annotation, the other behaves as if it had the same type annotation. - // If neither accessor includes a type annotation, the inferred return type of the get accessor becomes the parameter type of the set accessor. - if (declaration.kind === SyntaxKind.Parameter && declaration.parent.kind === SyntaxKind.SetAccessor) { + if (declaration.kind === SyntaxKind.Parameter) { + var func = declaration.parent; + // For a parameter of a set accessor, use the type of the get accessor if one is present + if (func.kind === SyntaxKind.SetAccessor) { var getter = getDeclarationOfKind(declaration.parent.symbol, SyntaxKind.GetAccessor); if (getter) { - //getReturnTypeOfSignature will check both type annotation and return type inferred from body - type = getReturnTypeOfSignature(getSignatureFromDeclaration(getter)); + return getReturnTypeOfSignature(getSignatureFromDeclaration(getter)); } } - - var unwidenedType: Type; - - if (!type) { - if (declaration.initializer) { - unwidenedType = checkAndMarkExpression(declaration.initializer); - type = getWidenedType(unwidenedType); - } - else if (declaration.flags & NodeFlags.Rest) { - type = createArrayType(anyType); - } - else { - type = anyType; - } - } - - if (program.getCompilerOptions().noImplicitAny && shouldReportNoImplicitAnyOnVariableOrParameterOrProperty(declaration, type, unwidenedType)) { - reportNoImplicitAnyOnVariableOrParameterOrProperty(declaration, type); + // Use contextual parameter type if one is available + var type = getContextuallyTypedParameterType(declaration); + if (type) { + return type; } } - + // Use the type of the initializer expression if one is present + if (declaration.initializer) { + var unwidenedType = checkAndMarkExpression(declaration.initializer); + var type = getWidenedType(unwidenedType); + if (type !== unwidenedType) { + checkImplicitAny(type); + } + return type; + } + // Rest parameters default to type any[], other parameters default to type any + var type = declaration.flags & NodeFlags.Rest ? createArrayType(anyType) : anyType; + checkImplicitAny(type); return type; - function shouldReportNoImplicitAnyOnVariableOrParameterOrProperty(declaration: VariableDeclaration, type: Type, unwidenedType: Type): boolean { - // If we attempted to widen, the resulting type has to be a different. - if (type === unwidenedType) { - return false; + function checkImplicitAny(type: Type) { + if (!program.getCompilerOptions().noImplicitAny) { + return; } - // We need to have ended up with 'any', 'any[]', 'any[][]', etc. if (getInnermostTypeOfNestedArrayTypes(type) !== anyType) { - return false; + return; } - // 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(declaration) || (declaration.kind === SyntaxKind.Parameter && isPrivateWithinAmbient(declaration.parent))) { - return false; + return; } - - return true; - } - - function reportNoImplicitAnyOnVariableOrParameterOrProperty(declaration: VariableDeclaration, type: Type): void { - var varName = identifierToString(declaration.name); - var typeName = typeToString(type); - switch (declaration.kind) { - case SyntaxKind.VariableDeclaration: - error(declaration, Diagnostics.Variable_0_implicitly_has_an_1_type, varName, typeName) - break; - case SyntaxKind.Property: - error(declaration, Diagnostics.Member_0_implicitly_has_an_1_type, varName, typeName) + var diagnostic = Diagnostics.Member_0_implicitly_has_an_1_type; break; - case SyntaxKind.Parameter: - var funcDeclaration = declaration.parent; - - // If this is a rest parameter, we should have widened specifically to 'any[]'. - if (declaration.flags & NodeFlags.Rest) { - error(declaration, Diagnostics.Rest_parameter_0_implicitly_has_an_any_type, varName) - } - else { - error(declaration, Diagnostics.Parameter_0_implicitly_has_an_1_type, varName, typeName) - } - + var diagnostic = declaration.flags & NodeFlags.Rest ? + Diagnostics.Rest_parameter_0_implicitly_has_an_any_type : + Diagnostics.Parameter_0_implicitly_has_an_1_type; break; - default: - Debug.fail("Received a '" + SyntaxKind[declaration.kind] + "', but expected '" + - SyntaxKind[SyntaxKind.VariableDeclaration] + "', '" + - SyntaxKind[SyntaxKind.Property] + "', or '" + - SyntaxKind[SyntaxKind.Parameter] + "'.\r\n"); + var diagnostic = Diagnostics.Variable_0_implicitly_has_an_1_type; } + error(declaration, diagnostic, identifierToString(declaration.name), typeToString(type)); } - } function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type { var links = getSymbolLinks(symbol); if (!links.type) { + // Handle prototype property if (symbol.flags & SymbolFlags.Prototype) { - links.type = getTypeOfPrototypeProperty(symbol); + return links.type = getTypeOfPrototypeProperty(symbol); } - else { - links.type = resolvingType; - var type = getTypeOfVariableDeclaration(symbol.valueDeclaration); - if (links.type === resolvingType) { - links.type = type; - } + // Handle catch clause variables + var declaration = symbol.valueDeclaration; + if (declaration.kind === SyntaxKind.CatchBlock) { + return links.type = anyType; + } + // Handle variable, parameter or property + links.type = resolvingType; + var type = getTypeOfVariableDeclaration(declaration); + if (links.type === resolvingType) { + links.type = type; } } - else if (links.type === resolvingType) { links.type = anyType; } - return links.type; - } function getSetAccessorTypeAnnotationNode(accessor: AccessorDeclaration): TypeNode { - return accessor && accessor.parameters.length > 0 && accessor.parameters[0].type; + return accessor && accessor.parameters.length > 0 && accessor.parameters[0].type; } function getAnnotatedAccessorType(accessor: AccessorDeclaration): Type { @@ -2296,15 +2270,17 @@ module ts { } function instantiateType(type: Type, mapper: TypeMapper): Type { - if (type.flags & TypeFlags.TypeParameter) { - return mapper(type); - } - if (type.flags & TypeFlags.Anonymous) { - return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) ? - instantiateAnonymousType(type, mapper) : type; - } - if (type.flags & TypeFlags.Reference) { - return createTypeReference((type).target, instantiateList((type).typeArguments, mapper, instantiateType)); + if (mapper !== identityMapper) { + if (type.flags & TypeFlags.TypeParameter) { + return mapper(type); + } + if (type.flags & TypeFlags.Anonymous) { + return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) ? + instantiateAnonymousType(type, mapper) : type; + } + if (type.flags & TypeFlags.Reference) { + return createTypeReference((type).target, instantiateList((type).typeArguments, mapper, instantiateType)); + } } return type; } @@ -3461,22 +3437,171 @@ module ts { return unknownType; } + function getTypeOfExpression(node: Expression): Type { + // TODO: Optimize by caching type in NodeLinks? + return checkExpression(node); + } + + // Return contextual type of parameter or undefined if no contextual type is available + function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type { + var func = parameter.parent; + if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) { + if (isContextSensitiveExpression(func)) { + var signature = getContextualSignature(func); + if (signature) { + return getTypeAtPosition(signature, indexOf(func.parameters, parameter)); + } + } + } + return undefined; + } + + function getContextualTypeForInitializerExpression(node: Expression): Type { + var declaration = node.parent; + if (node === declaration.initializer) { + if (declaration.type) { + return getTypeFromTypeNode(declaration.type); + } + if (declaration.kind === SyntaxKind.Parameter) { + return getContextuallyTypedParameterType(declaration); + } + } + return undefined; + } + + function getContextualTypeForReturnExpression(node: Expression): Type { + var func = getContainingFunction(node); + if (func) { + // If the containing function has a return type annotation, is a constructor, or is a get accessor whose + // corresponding set accessor has a type annotation, return statements in the function are contextually typed + if (func.type || func.kind === SyntaxKind.Constructor || func.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind(func.symbol, SyntaxKind.SetAccessor))) { + return getReturnTypeOfSignature(getSignatureFromDeclaration(func)); + } + // Otherwise, if the containing function is contextually typed by a function type with exactly one call signature + // and that call signature is non-generic, return statements are contextually typed by the return type of the signature + var signature = getContextualSignature(func); + if (signature) { + return getReturnTypeOfSignature(signature); + } + } + return undefined; + } + + function getContextualTypeForArgument(node: Expression): Type { + var callExpression = node.parent; + var argIndex = indexOf(callExpression.arguments, node); + if (argIndex >= 0) { + var signature = getResolvedSignature(callExpression); + return getTypeAtPosition(signature, argIndex); + } + return undefined; + } + + function getContextualTypeForBinaryOperand(node: Expression): Type { + var binaryExpression = node.parent; + var operator = binaryExpression.operator; + if (operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) { + if (node === binaryExpression.right) { + return getTypeOfExpression(binaryExpression.left); + } + } + else if (operator === SyntaxKind.BarBarToken) { + var type = getContextualType(binaryExpression); + if (!type && node === binaryExpression.right) { + type = getTypeOfExpression(binaryExpression.left); + } + return type; + } + return undefined; + } + + function getContextualTypeForPropertyExpression(node: Expression): Type { + var declaration = node.parent; + var objectLiteral = declaration.parent; + var type = getContextualType(objectLiteral); + var name = declaration.name.text; + if (type && name) { + var prop = getPropertyOfType(type, name); + if (prop) { + return getTypeOfSymbol(prop); + } + return isNumericName(name) && getIndexTypeOfType(type, IndexKind.Number) || getIndexTypeOfType(type, IndexKind.String); + } + return undefined; + } + + function getContextualTypeForElementExpression(node: Expression): Type { + var arrayLiteral = node.parent; + var type = getContextualType(arrayLiteral); + return type ? getIndexTypeOfType(type, IndexKind.Number) : undefined; + } + + function getContextualTypeForConditionalOperand(node: Expression): Type { + var conditional = node.parent; + return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional) : undefined; + } + + function getContextualType(node: Expression): Type { + if (node.contextualType) { + return node.contextualType; + } + var parent = node.parent; + switch (parent.kind) { + case SyntaxKind.VariableDeclaration: + case SyntaxKind.Parameter: + case SyntaxKind.Property: + return getContextualTypeForInitializerExpression(node); + case SyntaxKind.ArrowFunction: + case SyntaxKind.ReturnStatement: + return getContextualTypeForReturnExpression(node); + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + return getContextualTypeForArgument(node); + case SyntaxKind.TypeAssertion: + return getTypeFromTypeNode((parent).type); + case SyntaxKind.BinaryExpression: + return getContextualTypeForBinaryOperand(node); + case SyntaxKind.PropertyAssignment: + return getContextualTypeForPropertyExpression(node); + case SyntaxKind.ArrayLiteral: + return getContextualTypeForElementExpression(node); + case SyntaxKind.ConditionalExpression: + return getContextualTypeForConditionalOperand(node); + } + return undefined; + } + + function getContextualSignature(node: Expression): Signature { + var type = getContextualType(node); + if (type) { + var signatures = getSignaturesOfType(type, SignatureKind.Call); + if (signatures.length === 1) { + var signature = signatures[0]; + if (!signature.typeParameters) { + return signature; + } + } + } + return undefined; + } + // Presence of a contextual type mapper indicates inferential typing, except the identityMapper object is // used as a special marker for other purposes. function isInferentialContext(mapper: TypeMapper) { return mapper && mapper !== identityMapper; } - function checkArrayLiteral(node: ArrayLiteral, contextualType?: Type, contextualMapper?: TypeMapper): Type { - var contextualElementType = contextualType && getIndexTypeOfType(contextualType, IndexKind.Number); + function checkArrayLiteral(node: ArrayLiteral, contextualMapper?: TypeMapper): Type { var elementTypes: Type[] = []; forEach(node.elements, element => { if (element.kind !== SyntaxKind.OmittedExpression) { - var type = checkExpression(element, contextualElementType, contextualMapper); + var type = checkExpression(element, contextualMapper); if (!contains(elementTypes, type)) elementTypes.push(type); } }); - var elementType = getBestCommonType(elementTypes, isInferentialContext(contextualMapper) ? undefined : contextualElementType, true); + var contextualType = isInferentialContext(contextualMapper) ? undefined : getContextualType(node); + var contextualElementType = contextualType && getIndexTypeOfType(contextualType, IndexKind.Number); + var elementType = getBestCommonType(elementTypes, contextualElementType, true); if (!elementType) elementType = elementTypes.length ? emptyObjectType : undefinedType; return createArrayType(elementType); } @@ -3485,22 +3610,16 @@ module ts { return !isNaN(name); } - function getContextualTypeForProperty(type: Type, name: string) { - var prop = getPropertyOfType(type, name); - if (prop) return getTypeOfSymbol(prop); - return isNumericName(name) && getIndexTypeOfType(type, IndexKind.Number) || getIndexTypeOfType(type, IndexKind.String); - } - - function checkObjectLiteral(node: ObjectLiteral, contextualType?: Type, contextualMapper?: TypeMapper): Type { + function checkObjectLiteral(node: ObjectLiteral, contextualMapper?: TypeMapper): Type { var members = node.symbol.members; var properties: SymbolTable = {}; + var contextualType = getContextualType(node); for (var id in members) { if (hasProperty(members, id)) { var member = members[id]; if (member.flags & SymbolFlags.Property) { - var contextualPropType = contextualType && getContextualTypeForProperty(contextualType, member.name); - var type = checkExpression((member.declarations[0]).initializer, contextualPropType, contextualMapper); + var type = checkExpression((member.declarations[0]).initializer, contextualMapper); var prop = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, member.name); prop.declarations = member.declarations; prop.parent = member.parent; @@ -3527,11 +3646,11 @@ module ts { properties[member.name] = member; } } - var stringIndexType = getIndexType(properties, IndexKind.String); - var numberIndexType = getIndexType(properties, IndexKind.Number); + var stringIndexType = getIndexType(IndexKind.String); + var numberIndexType = getIndexType(IndexKind.Number); return createAnonymousType(node.symbol, properties, emptyArray, emptyArray, stringIndexType, numberIndexType); - function getIndexType(properties: SymbolTable, kind: IndexKind) { + function getIndexType(kind: IndexKind) { if (contextualType) { var indexType = getIndexTypeOfType(contextualType, kind); if (indexType) { @@ -3544,7 +3663,6 @@ module ts { } } } - return getBestCommonType(propTypes, isInferentialContext(contextualMapper) ? undefined : indexType); } } @@ -3659,14 +3777,16 @@ module ts { return unknownType; } - function checkUntypedCall(node: CallExpression): Type { - forEach(node.arguments, argument => { checkExpression(argument); }); - return anyType; + function resolveUntypedCall(node: CallExpression): Signature { + forEach(node.arguments, argument => { + checkExpression(argument); + }); + return anySignature; } - function checkErrorCall(node: CallExpression): Type { - checkUntypedCall(node); - return unknownType; + function resolveErrorCall(node: CallExpression): Signature { + resolveUntypedCall(node); + return unknownSignature; } function isCandidateSignature(node: CallExpression, signature: Signature) { @@ -3725,7 +3845,7 @@ module ts { // Inferentially type an expression by a contextual parameter type (section 4.12.2 in TypeScript spec) function inferentiallyTypeExpession(expr: Expression, contextualType: Type, contextualMapper: TypeMapper): Type { - var type = checkExpression(expr, contextualType, contextualMapper); + var type = checkExpressionWithContextualType(expr, contextualType, contextualMapper); var signature = getSingleCallSignature(type); if (signature && signature.typeParameters) { var contextualSignature = getSingleCallSignature(contextualType); @@ -3782,7 +3902,7 @@ module ts { // String literals get string literal types unless we're reporting errors var argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors ? getStringLiteralType(arg) : - checkExpression(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); + checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); // Use argument expression as error location when reporting errors var isValidArgument = checkTypeRelatedTo(argType, paramType, relation, reportErrors ? arg : undefined, Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1, @@ -3795,12 +3915,12 @@ module ts { return true; } - function checkCall(node: CallExpression, signatures: Signature[]): Type { + function resolveCall(node: CallExpression, signatures: Signature[]): Signature { forEach(node.typeArguments, checkSourceElement); var candidates = collectCandidates(node, signatures); if (!candidates.length) { error(node, Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target); - return checkErrorCall(node); + return resolveErrorCall(node); } var args = node.arguments || emptyArray; var excludeArgument: boolean[]; @@ -3826,7 +3946,7 @@ module ts { } var index = excludeArgument ? indexOf(excludeArgument, true) : -1; if (index < 0) { - return getReturnTypeOfSignature(candidate); + return candidate; } excludeArgument[index] = false; } @@ -3839,35 +3959,29 @@ module ts { // No signatures were applicable. Now report errors based on the last applicable signature with // no arguments excluded from assignability checks. checkApplicableSignature(node, candidate, relation, undefined, /*reportErrors*/ true); - return checkErrorCall(node); + return resolveErrorCall(node); } - function checkCallExpression(node: CallExpression): Type { + function resolveCallExpression(node: CallExpression): Signature { if (node.func.kind === SyntaxKind.SuperKeyword) { var superType = checkSuperExpression(node.func, true); if (superType !== unknownType) { - checkCall(node, getSignaturesOfType(superType, SignatureKind.Construct)); + return resolveCall(node, getSignaturesOfType(superType, SignatureKind.Construct)); } - else { - checkUntypedCall(node); - } - - // TS 1.0 spec: 4.8.1 - // The type of a super call expression is Void. - return voidType; + return resolveUntypedCall(node); } var funcType = checkExpression(node.func); if (funcType === unknownType) { // Another error has already been reported - return checkErrorCall(node); + return resolveErrorCall(node); } var apparentType = getApparentType(funcType) if (apparentType === unknownType) { // handler cases when funcType is type parameter with invalid constraint // Another error was already reported - return checkErrorCall(node); + return resolveErrorCall(node); } // Technically, this signatures list may be incomplete. We are taking the apparent type, @@ -3886,7 +4000,7 @@ module ts { if (node.typeArguments) { error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments); } - return checkUntypedCall(node); + return resolveUntypedCall(node); } // If FuncExpr's apparent type(section 3.8.1) is a function type, the call is a typed function call. // TypeScript employs overload resolution in typed function calls in order to support functions @@ -3898,16 +4012,16 @@ module ts { else { error(node, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature); } - return checkErrorCall(node); + return resolveErrorCall(node); } - return checkCall(node, callSignatures); + return resolveCall(node, callSignatures); } - function checkNewExpression(node: NewExpression): Type { + function resolveNewExpression(node: NewExpression): Signature { var expressionType = checkExpression(node.func); if (expressionType === unknownType) { // Another error has already been reported - return checkErrorCall(node); + return resolveErrorCall(node); } // TS 1.0 spec: 4.11 // If ConstructExpr is of type Any, Args can be any argument @@ -3917,7 +4031,7 @@ module ts { error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments); } - return checkUntypedCall(node); + return resolveUntypedCall(node); } // If ConstructExpr's apparent type(section 3.8.1) is an object type with one or @@ -3929,7 +4043,7 @@ module ts { if (expressionType === unknownType) { // handler cases when original expressionType is a type parameter with invalid constraint // another error has already been reported - return checkErrorCall(node); + return resolveErrorCall(node); } // Technically, this signatures list may be incomplete. We are taking the apparent type, @@ -3938,7 +4052,7 @@ module ts { // that the user will not add any. var constructSignatures = getSignaturesOfType(expressionType, SignatureKind.Construct); if (constructSignatures.length) { - return checkCall(node, constructSignatures); + return resolveCall(node, constructSignatures); } // If ConstructExpr's apparent type is an object type with no construct signatures but @@ -3947,45 +4061,54 @@ module ts { // operation is Any. var callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call); if (callSignatures.length) { - var type = checkCall(node, callSignatures); - - if (type !== voidType) { + var signature = resolveCall(node, callSignatures); + if (getReturnTypeOfSignature(signature) !== voidType) { error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword); } - - // Since we found no constructor signature, we (implicitly) return any. - if (program.getCompilerOptions().noImplicitAny) { - error(node, Diagnostics.new_expression_whose_target_lacks_a_construct_signature_implicitly_has_an_any_type); - } - - return anyType; + return signature; } error(node, Diagnostics.Cannot_use_new_with_an_expression_whose_type_lacks_a_call_or_construct_signature); - return checkErrorCall(node); + return resolveErrorCall(node); + } + + function getResolvedSignature(node: CallExpression): Signature { + var links = getNodeLinks(node); + if (!links.resolvedSignature) { + links.resolvedSignature = anySignature; + links.resolvedSignature = node.kind === SyntaxKind.CallExpression ? resolveCallExpression(node) : resolveNewExpression(node); + } + return links.resolvedSignature; + } + + function checkCallExpression(node: CallExpression): Type { + var signature = getResolvedSignature(node); + if (node.func.kind === SyntaxKind.SuperKeyword) { + return voidType; + } + if (node.kind === SyntaxKind.NewExpression) { + var declaration = signature.declaration; + if (declaration && (declaration.kind !== SyntaxKind.Constructor && declaration.kind !== SyntaxKind.ConstructSignature)) { + // When resolved signature is a call signature (and not a construct signature) the result type is any + if (program.getCompilerOptions().noImplicitAny) { + error(node, Diagnostics.new_expression_whose_target_lacks_a_construct_signature_implicitly_has_an_any_type); + } + return anyType; + } + } + return getReturnTypeOfSignature(signature); } function checkTypeAssertion(node: TypeAssertion): Type { + var exprType = checkExpression(node.operand); var targetType = getTypeFromTypeNode(node.type); - if (targetType === unknownType) return unknownType; - var exprType = checkExpression(node.operand, targetType); - 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); - } - return targetType; - } - - function getContextualSignature(contextualType: Type) { - if (contextualType) { - var signatures = getSignaturesOfType(contextualType, SignatureKind.Call); - if (signatures.length === 1) { - var signature = signatures[0]; - if (!signature.typeParameters) { - return signature; - } + if (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); } } + return targetType; } function getTypeAtPosition(signature: Signature, pos: number): Type { @@ -4012,9 +4135,9 @@ module ts { } } - function getReturnTypeFromBody(func: FunctionDeclaration, contextualType?: Type, contextualMapper?: TypeMapper): Type { + function getReturnTypeFromBody(func: FunctionDeclaration, contextualMapper?: TypeMapper): Type { if (func.body.kind !== SyntaxKind.FunctionBlock) { - var unwidenedType = checkAndMarkExpression(func.body, contextualType, contextualMapper); + var unwidenedType = checkAndMarkExpression(func.body, contextualMapper); var widenedType = getWidenedType(unwidenedType); if (program.getCompilerOptions().noImplicitAny && widenedType !== unwidenedType && getInnermostTypeOfNestedArrayTypes(widenedType) === anyType) { @@ -4025,7 +4148,7 @@ module ts { } // Aggregate the types of expressions within all the return statements. - var types = checkAndAggregateReturnExpressionTypes(func.body, contextualType, contextualMapper); + var types = checkAndAggregateReturnExpressionTypes(func.body, contextualMapper); // Try to return the best common type if we have any return expressions. if (types.length > 0) { @@ -4088,13 +4211,13 @@ module ts { } /// Returns a set of types relating to every return expression relating to a function block. - function checkAndAggregateReturnExpressionTypes(body: Block, contextualType?: Type, contextualMapper?: TypeMapper): Type[] { + function checkAndAggregateReturnExpressionTypes(body: Block, contextualMapper?: TypeMapper): Type[] { var aggregatedTypes: Type[] = []; forEachReturnStatement(body, returnStatement => { var expr = returnStatement.expression; if (expr) { - var type = checkAndMarkExpression(expr, contextualType, contextualMapper); + var type = checkAndMarkExpression(expr, contextualMapper); if (!contains(aggregatedTypes, type)) { aggregatedTypes.push(type); } @@ -4147,42 +4270,48 @@ module ts { error(func.type, Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value_or_consist_of_a_single_throw_statement); } - function checkFunctionExpression(node: FunctionExpression, contextualType?: Type, contextualMapper?: TypeMapper): Type { + function checkFunctionExpression(node: FunctionExpression, contextualMapper?: TypeMapper): Type { // The identityMapper object is used to indicate that function expressions are wildcards if (contextualMapper === identityMapper) { return anyFunctionType; } - - var type = getTypeOfSymbol(node.symbol); var links = getNodeLinks(node); - - if (!(links.flags & NodeCheckFlags.TypeChecked)) { - var signature = getSignaturesOfType(type, SignatureKind.Call)[0]; - var contextualSignature = getContextualSignature(contextualType); - if (contextualSignature) { - if (!node.typeParameters && !forEach(node.parameters, p => p.type)) { - assignContextualParameterTypes(signature, contextualSignature, contextualMapper || identityMapper); - } - if (!node.type) { - signature.resolvedReturnType = resolvingType; - var returnType = getReturnTypeFromBody(node, getReturnTypeOfSignature(contextualSignature), contextualMapper); - if (signature.resolvedReturnType === resolvingType) { - signature.resolvedReturnType = returnType; + var type = getTypeOfSymbol(node.symbol); + // Check if function expression is contextually typed and assign parameter types if so + if (!(links.flags & NodeCheckFlags.ContextChecked)) { + var contextualSignature = getContextualSignature(node); + // If a type check is started at a function expression that is an argument of a function call, obtaining the + // contextual type may recursively get back to here during overload resolution of the call. If so, we will have + // already assigned contextual types. + if (!(links.flags & NodeCheckFlags.ContextChecked)) { + links.flags |= NodeCheckFlags.ContextChecked; + if (contextualSignature) { + var signature = getSignaturesOfType(type, SignatureKind.Call)[0]; + if (isContextSensitiveExpression(node)) { + assignContextualParameterTypes(signature, contextualSignature, contextualMapper || identityMapper); + } + if (!node.type) { + signature.resolvedReturnType = resolvingType; + var returnType = getReturnTypeFromBody(node, contextualMapper); + if (signature.resolvedReturnType === resolvingType) { + signature.resolvedReturnType = returnType; + } } } - else { - checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type)); - } } + } + if (fullTypeCheck && !(links.flags & NodeCheckFlags.TypeChecked)) { checkSignatureDeclaration(node); + if (node.type) { + checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type)); + } if (node.body.kind === SyntaxKind.FunctionBlock) { checkSourceElement(node.body); } else { - var returnType = getReturnTypeOfSignature(signature); + var exprType = checkExpression(node.body); if (node.type) { - // Use default error messages for this check - checkTypeAssignableTo(checkExpression(node.body, returnType), returnType, node.body, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); + checkTypeAssignableTo(exprType, getTypeFromTypeNode(node.type), node.body, undefined, undefined); } } links.flags |= NodeCheckFlags.TypeChecked; @@ -4312,13 +4441,10 @@ module ts { return booleanType; } - function checkBinaryExpression(node: BinaryExpression, contextualType?: Type, contextualMapper?: TypeMapper) { + function checkBinaryExpression(node: BinaryExpression, contextualMapper?: TypeMapper) { var operator = node.operator; - var leftContextualType = operator === SyntaxKind.BarBarToken ? contextualType : undefined - var leftType = checkExpression(node.left, leftContextualType, contextualMapper); - var rightContextualType = operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment ? leftType : - operator === SyntaxKind.BarBarToken ? contextualType || leftType : undefined; - var rightType = checkExpression(node.right, rightContextualType, contextualMapper); + var leftType = checkExpression(node.left, contextualMapper); + var rightType = checkExpression(node.right, contextualMapper); switch (operator) { case SyntaxKind.AsteriskToken: case SyntaxKind.AsteriskEqualsToken: @@ -4410,7 +4536,7 @@ module ts { case SyntaxKind.AmpersandAmpersandToken: return rightType; case SyntaxKind.BarBarToken: - return getBestCommonType([leftType, rightType], isInferentialContext(contextualMapper) ? undefined : contextualType); + return getBestCommonType([leftType, rightType], isInferentialContext(contextualMapper) ? undefined : getContextualType(node)); case SyntaxKind.EqualsToken: checkAssignmentOperator(rightType); return rightType; @@ -4440,40 +4566,46 @@ module ts { } } - function checkConditionalExpression(node: ConditionalExpression, contextualType?: Type, contextualMapper?: TypeMapper): Type { + function checkConditionalExpression(node: ConditionalExpression, contextualMapper?: TypeMapper): Type { checkExpression(node.condition); - var type1 = checkExpression(node.whenTrue, contextualType, contextualMapper); - var type2 = checkExpression(node.whenFalse, contextualType, contextualMapper); - var resultType = getBestCommonType([type1, type2], isInferentialContext(contextualMapper) ? undefined : contextualType, true); + var type1 = checkExpression(node.whenTrue, contextualMapper); + var type2 = checkExpression(node.whenFalse, contextualMapper); + var contextualType = isInferentialContext(contextualMapper) ? undefined : getContextualType(node); + var resultType = getBestCommonType([type1, type2], contextualType, true); if (!resultType) { - - if (contextualType && !isInferentialContext(contextualMapper)) { + if (contextualType) { error(node, Diagnostics.No_best_common_type_exists_between_0_1_and_2, typeToString(contextualType), typeToString(type1), typeToString(type2)); } else { error(node, Diagnostics.No_best_common_type_exists_between_0_and_1, typeToString(type1), typeToString(type2)); } - resultType = emptyObjectType; } return resultType; } - function checkAndMarkExpression(node: Expression, contextualType?: Type, contextualMapper?: TypeMapper): Type { - var result = checkExpression(node, contextualType, contextualMapper); + function checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper?: TypeMapper): Type { + var saveContextualType = node.contextualType; + node.contextualType = contextualType; + var result = checkExpression(node, contextualMapper); + node.contextualType = saveContextualType; + return result; + } + + function checkAndMarkExpression(node: Expression, contextualMapper?: TypeMapper): Type { + var result = checkExpression(node, contextualMapper); getNodeLinks(node).flags |= NodeCheckFlags.TypeChecked; return result; } - // Checks an expression and returns its type. The contextualType parameter provides a contextual type for - // the check or is undefined if there is no contextual type. The contextualMapper parameter serves two - // purposes: When contextualMapper is not undefined and not equal to the identityMapper function object - // it provides a type mapper to use during inferential typing (the contextual type is then a generic type). - // When contextualMapper is equal to the identityMapper function object, it serves as an indicator that all - // contained function and arrow expressions should be considered to have the wildcard function type; this - // form of type check is used during overload resolution to exclude contextually typed function and arrow - // expressions in the initial phase. - function checkExpression(node: Expression, contextualType?: Type, contextualMapper?: TypeMapper): Type { + // Checks an expression and returns its type. The contextualMapper parameter serves two purposes: When + // contextualMapper is not undefined and not equal to the identityMapper function object it indicates that the + // expression is being inferentially typed (section 4.12.2 in spec) and provides the type mapper to use in + // conjuction with the generic contextual type. When contextualMapper is equal to the identityMapper function + // object, it serves as an indicator that all contained function and arrow expressions should be considered to + // have the wildcard function type; this form of type check is used during overload resolution to exclude + // contextually typed function and arrow expressions in the initial phase. + function checkExpression(node: Expression, contextualMapper?: TypeMapper): Type { switch (node.kind) { case SyntaxKind.Identifier: return checkIdentifier(node); @@ -4495,32 +4627,31 @@ module ts { case SyntaxKind.QualifiedName: return checkPropertyAccess(node); case SyntaxKind.ArrayLiteral: - return checkArrayLiteral(node, contextualType, contextualMapper); + return checkArrayLiteral(node, contextualMapper); case SyntaxKind.ObjectLiteral: - return checkObjectLiteral(node, contextualType, contextualMapper); + return checkObjectLiteral(node, contextualMapper); case SyntaxKind.PropertyAccess: return checkPropertyAccess(node); case SyntaxKind.IndexedAccess: return checkIndexedAccess(node); case SyntaxKind.CallExpression: - return checkCallExpression(node); case SyntaxKind.NewExpression: - return checkNewExpression(node); + return checkCallExpression(node); case SyntaxKind.TypeAssertion: return checkTypeAssertion(node); case SyntaxKind.ParenExpression: return checkExpression((node).expression); case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: - return checkFunctionExpression(node, contextualType, contextualMapper); + return checkFunctionExpression(node, contextualMapper); case SyntaxKind.PrefixOperator: return checkPrefixExpression(node); case SyntaxKind.PostfixOperator: return checkPostfixExpression(node); case SyntaxKind.BinaryExpression: - return checkBinaryExpression(node, contextualType, contextualMapper); + return checkBinaryExpression(node, contextualMapper); case SyntaxKind.ConditionalExpression: - return checkConditionalExpression(node, contextualType, contextualMapper); + return checkConditionalExpression(node, contextualMapper); } return unknownType; } @@ -5325,7 +5456,7 @@ module ts { if (node.initializer) { if (!(getNodeLinks(node.initializer).flags & NodeCheckFlags.TypeChecked)) { // Use default messages - checkTypeAssignableTo(checkAndMarkExpression(node.initializer, type), type, node, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); + checkTypeAssignableTo(checkAndMarkExpression(node.initializer), type, node, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); } } @@ -5446,12 +5577,12 @@ module ts { func.type || (func.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind(func.symbol, SyntaxKind.SetAccessor))); if (checkAssignability) { - checkTypeAssignableTo(checkExpression(node.expression, returnType), returnType, node.expression, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); + checkTypeAssignableTo(checkExpression(node.expression), returnType, node.expression, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); } else if (func.kind == SyntaxKind.Constructor) { // constructor doesn't have explicit return type annontation and yet its return type is known - declaring type // handle constructors and issue specialized error message for them. - if (!isTypeAssignableTo(checkExpression(node.expression, returnType), returnType)) { + if (!isTypeAssignableTo(checkExpression(node.expression), returnType)) { error(node.expression, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class); } } @@ -6048,9 +6179,12 @@ 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. function checkSourceFile(node: SourceFile) { var links = getNodeLinks(node); if (!(links.flags & NodeCheckFlags.TypeChecked)) { + fullTypeCheck = true; emitExtends = false; potentialThisCollisions.length = 0; forEach(node.statements, checkSourceElement); @@ -6067,6 +6201,7 @@ module ts { } if (emitExtends) links.flags |= NodeCheckFlags.EmitExtends; links.flags |= NodeCheckFlags.TypeChecked; + fullTypeCheck = false; } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 600fb5bdad4..10a358f379d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -320,7 +320,9 @@ module ts { text: string; } - export interface Expression extends Node { } + export interface Expression extends Node { + contextualType?: Type; // Used to temporarily assign a contextual type during overload resolution + } export interface UnaryExpression extends Expression { operator: SyntaxKind; @@ -736,17 +738,18 @@ module ts { } export enum NodeCheckFlags { - TypeChecked = 0x00000001, // Node has been type checked - LexicalThis = 0x00000002, // Lexical 'this' reference - CaptureThis = 0x00000004, // Lexical 'this' used in body - EmitExtends = 0x00000008, // Emit __extends - SuperInstance = 0x00000010, // Instance 'super' reference - SuperStatic = 0x00000020, // Static 'super' reference + TypeChecked = 0x00000001, // Node has been type checked + LexicalThis = 0x00000002, // Lexical 'this' reference + CaptureThis = 0x00000004, // Lexical 'this' used in body + EmitExtends = 0x00000008, // Emit __extends + SuperInstance = 0x00000010, // Instance 'super' reference + SuperStatic = 0x00000020, // Static 'super' reference + ContextChecked = 0x00000040, // Contextual types have been assigned } export interface NodeLinks { resolvedType?: Type; // Cached type of type node - resolvedSignature?: Signature; // Cached signature of signature node + resolvedSignature?: Signature; // Cached signature of signature node or call expression resolvedSymbol?: Symbol; // Cached name resolution result flags?: NodeCheckFlags; // Set of flags specific to Node enumMemberValue?: number; // Constant value of enum member diff --git a/tests/baselines/reference/functionImplementationErrors.errors.txt b/tests/baselines/reference/functionImplementationErrors.errors.txt index 3da56bd6818..5c2d678840e 100644 --- a/tests/baselines/reference/functionImplementationErrors.errors.txt +++ b/tests/baselines/reference/functionImplementationErrors.errors.txt @@ -1,4 +1,4 @@ -==== tests/cases/conformance/functions/functionImplementationErrors.ts (7 errors) ==== +==== tests/cases/conformance/functions/functionImplementationErrors.ts (8 errors) ==== // FunctionExpression with no return type annotation with multiple return statements with unrelated types var f1 = function () { ~~~~~~~~~~~~~ @@ -68,6 +68,8 @@ // FunctionExpression with non -void return type annotation with a throw, no return, and other code // Should be error but isn't undefined === function (): number { + ~~~~~~ +!!! A function whose declared type is neither 'void' nor 'any' must return a value or consist of a single 'throw' statement. throw undefined; var x = 4; }; diff --git a/tests/baselines/reference/implicitAnyGetAndSetAccessorWithAnyReturnType.errors.txt b/tests/baselines/reference/implicitAnyGetAndSetAccessorWithAnyReturnType.errors.txt index d6439688597..c3484202590 100644 --- a/tests/baselines/reference/implicitAnyGetAndSetAccessorWithAnyReturnType.errors.txt +++ b/tests/baselines/reference/implicitAnyGetAndSetAccessorWithAnyReturnType.errors.txt @@ -1,4 +1,4 @@ -==== tests/cases/compiler/implicitAnyGetAndSetAccessorWithAnyReturnType.ts (9 errors) ==== +==== tests/cases/compiler/implicitAnyGetAndSetAccessorWithAnyReturnType.ts (8 errors) ==== // these should be errors class GetAndSet { getAndSet = null; // error at "getAndSet" @@ -14,8 +14,6 @@ public set haveGetAndSet(value) { // error at "value" ~~~~~~~~~~~~~ !!! Accessors are only available when targeting ECMAScript 5 and higher. - ~~~~~ -!!! Parameter 'value' implicitly has an 'any' type. this.getAndSet = value; } } diff --git a/tests/baselines/reference/parserCastVersusArrowFunction1.errors.txt b/tests/baselines/reference/parserCastVersusArrowFunction1.errors.txt index b9ae061ed69..2686c59da36 100644 --- a/tests/baselines/reference/parserCastVersusArrowFunction1.errors.txt +++ b/tests/baselines/reference/parserCastVersusArrowFunction1.errors.txt @@ -1,8 +1,10 @@ -==== tests/cases/conformance/parser/ecmascript5/Generics/parserCastVersusArrowFunction1.ts (7 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/Generics/parserCastVersusArrowFunction1.ts (13 errors) ==== var v = () => 1; var v = a; ~ !!! Cannot find name 'T'. + ~ +!!! Cannot find name 'a'. var v = (a) => 1; ~ @@ -17,9 +19,19 @@ var v = (a); ~ !!! Cannot find name 'T'. + ~ +!!! Cannot find name 'a'. var v = (a, b); ~ !!! Cannot find name 'T'. + ~ +!!! Cannot find name 'a'. + ~ +!!! Cannot find name 'b'. var v = (a = 1, b = 2); ~ -!!! Cannot find name 'T'. \ No newline at end of file +!!! Cannot find name 'T'. + ~ +!!! Cannot find name 'a'. + ~ +!!! Cannot find name 'b'. \ No newline at end of file diff --git a/tests/baselines/reference/parserRealSource8.errors.txt b/tests/baselines/reference/parserRealSource8.errors.txt index 42dca350919..d4f6467fc10 100644 --- a/tests/baselines/reference/parserRealSource8.errors.txt +++ b/tests/baselines/reference/parserRealSource8.errors.txt @@ -1,4 +1,4 @@ -==== tests/cases/conformance/parser/ecmascript5/parserRealSource8.ts (133 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/parserRealSource8.ts (134 errors) ==== // Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. // See LICENSE.txt in the project root for complete license information. @@ -383,6 +383,8 @@ outerFnc.type.members = ((container).type.memberScope).valueMembers; ~~~~~~~~~~~~~~~~~~ !!! Cannot find name 'SymbolScopeBuilder'. + ~~~~~~~~~~ +!!! Cannot find name 'TypeSymbol'. } funcScope = context.scopeChain.fnc.type.memberScope; outerFnc.innerStaticFuncs[outerFnc.innerStaticFuncs.length] = funcDecl; diff --git a/tests/baselines/reference/parserVariableDeclaration3.errors.txt b/tests/baselines/reference/parserVariableDeclaration3.errors.txt index b2dfcb1c4e9..8d10b4ba5de 100644 --- a/tests/baselines/reference/parserVariableDeclaration3.errors.txt +++ b/tests/baselines/reference/parserVariableDeclaration3.errors.txt @@ -1,4 +1,4 @@ -==== tests/cases/conformance/parser/ecmascript5/VariableDeclarations/parserVariableDeclaration3.ts (3 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/VariableDeclarations/parserVariableDeclaration3.ts (4 errors) ==== function runTests() { var outfile = new Harness.Compiler.WriterAggregator() ~~~~~~~ @@ -8,6 +8,8 @@ !!! Cannot find name 'Harness'. , compiler = new TypeScript.TypeScriptCompiler(outerr) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! Cannot find name 'TypeScript'. + ~~~~~~~~~~ !!! Cannot find name 'TypeScript'. , code; } \ No newline at end of file