Merge pull request #330 from Microsoft/pullContextualTypes

Pull model for contextual types
This commit is contained in:
Anders Hejlsberg 2014-08-04 11:51:23 -07:00
commit 9a5324347e
8 changed files with 400 additions and 246 deletions

View File

@ -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);

View File

@ -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<StringLiteralType> = {};
var fullTypeCheck = false;
var emitExtends = false;
var mergedSymbols: Symbol[] = [];
@ -327,7 +331,7 @@ module ts {
case SyntaxKind.CatchBlock:
var id = (<CatchBlock>location).variable;
if (name === id.text) {
return returnResolvedSymbol((<CatchBlock>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 = <FunctionDeclaration>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 = <AccessorDeclaration>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 = <FunctionDeclaration>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(<VariableDeclaration>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(<VariableDeclaration>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(<ObjectType>type, mapper) : type;
}
if (type.flags & TypeFlags.Reference) {
return createTypeReference((<TypeReference>type).target, instantiateList((<TypeReference>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(<ObjectType>type, mapper) : type;
}
if (type.flags & TypeFlags.Reference) {
return createTypeReference((<TypeReference>type).target, instantiateList((<TypeReference>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 = <FunctionDeclaration>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 = <VariableDeclaration>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(<AccessorDeclaration>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 = <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 = <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 = <PropertyDeclaration>node.parent;
var objectLiteral = <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 = <ArrayLiteral>node.parent;
var type = getContextualType(arrayLiteral);
return type ? getIndexTypeOfType(type, IndexKind.Number) : undefined;
}
function getContextualTypeForConditionalOperand(node: Expression): Type {
var conditional = <ConditionalExpression>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((<TypeAssertion>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(<number><any>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((<PropertyDeclaration>member.declarations[0]).initializer, contextualPropType, contextualMapper);
var type = checkExpression((<PropertyDeclaration>member.declarations[0]).initializer, contextualMapper);
var prop = <TransientSymbol>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(<LiteralExpression>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 (<Type>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 (<Type>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(<Block>func.body, contextualType, contextualMapper);
var types = checkAndAggregateReturnExpressionTypes(<Block>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(<Identifier>node);
@ -4495,32 +4627,31 @@ module ts {
case SyntaxKind.QualifiedName:
return checkPropertyAccess(<QualifiedName>node);
case SyntaxKind.ArrayLiteral:
return checkArrayLiteral(<ArrayLiteral>node, contextualType, contextualMapper);
return checkArrayLiteral(<ArrayLiteral>node, contextualMapper);
case SyntaxKind.ObjectLiteral:
return checkObjectLiteral(<ObjectLiteral>node, contextualType, contextualMapper);
return checkObjectLiteral(<ObjectLiteral>node, contextualMapper);
case SyntaxKind.PropertyAccess:
return checkPropertyAccess(<PropertyAccess>node);
case SyntaxKind.IndexedAccess:
return checkIndexedAccess(<IndexedAccess>node);
case SyntaxKind.CallExpression:
return checkCallExpression(<CallExpression>node);
case SyntaxKind.NewExpression:
return checkNewExpression(<NewExpression>node);
return checkCallExpression(<CallExpression>node);
case SyntaxKind.TypeAssertion:
return checkTypeAssertion(<TypeAssertion>node);
case SyntaxKind.ParenExpression:
return checkExpression((<ParenExpression>node).expression);
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return checkFunctionExpression(<FunctionExpression>node, contextualType, contextualMapper);
return checkFunctionExpression(<FunctionExpression>node, contextualMapper);
case SyntaxKind.PrefixOperator:
return checkPrefixExpression(<UnaryExpression>node);
case SyntaxKind.PostfixOperator:
return checkPostfixExpression(<UnaryExpression>node);
case SyntaxKind.BinaryExpression:
return checkBinaryExpression(<BinaryExpression>node, contextualType, contextualMapper);
return checkBinaryExpression(<BinaryExpression>node, contextualMapper);
case SyntaxKind.ConditionalExpression:
return checkConditionalExpression(<ConditionalExpression>node, contextualType, contextualMapper);
return checkConditionalExpression(<ConditionalExpression>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(<AccessorDeclaration>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;
}
}

View File

@ -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

View File

@ -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;
};

View File

@ -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;
}
}

View File

@ -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 = <T>() => 1;
var v = <T>a;
~
!!! Cannot find name 'T'.
~
!!! Cannot find name 'a'.
var v = <T>(a) => 1;
~
@ -17,9 +19,19 @@
var v = <T>(a);
~
!!! Cannot find name 'T'.
~
!!! Cannot find name 'a'.
var v = <T>(a, b);
~
!!! Cannot find name 'T'.
~
!!! Cannot find name 'a'.
~
!!! Cannot find name 'b'.
var v = <T>(a = 1, b = 2);
~
!!! Cannot find name 'T'.
!!! Cannot find name 'T'.
~
!!! Cannot find name 'a'.
~
!!! Cannot find name 'b'.

View File

@ -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 = (<SymbolScopeBuilder>(<TypeSymbol>container).type.memberScope).valueMembers;
~~~~~~~~~~~~~~~~~~
!!! Cannot find name 'SymbolScopeBuilder'.
~~~~~~~~~~
!!! Cannot find name 'TypeSymbol'.
}
funcScope = context.scopeChain.fnc.type.memberScope;
outerFnc.innerStaticFuncs[outerFnc.innerStaticFuncs.length] = funcDecl;

View File

@ -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 = <TypeScript.TypeScriptCompiler>new TypeScript.TypeScriptCompiler(outerr)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! Cannot find name 'TypeScript'.
~~~~~~~~~~
!!! Cannot find name 'TypeScript'.
, code;
}