From 921efec6b8a30629352d7e36c5251f09087a8888 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 16 Apr 2016 15:11:34 -0700 Subject: [PATCH] Improved handing of evolving types in iteration statements --- src/compiler/checker.ts | 58 ++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ac8c375dc99..1eeec55db8d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -180,6 +180,7 @@ namespace ts { let jsxElementClassType: Type; let deferredNodes: Node[]; + let inFlowCheck = false; const tupleTypes: Map = {}; const unionTypes: Map = {}; @@ -7528,7 +7529,11 @@ namespace ts { function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType: Type) { let key: string; - return reference.flowNode ? getTypeAtFlowNode(reference.flowNode) : declaredType; + const saveInFlowCheck = inFlowCheck; + inFlowCheck = true; + const result = reference.flowNode ? getTypeAtFlowNode(reference.flowNode) : declaredType; + inFlowCheck = saveInFlowCheck; + return result; function getTypeAtFlowNode(flow: FlowNode): Type { while (true) { @@ -8537,7 +8542,7 @@ namespace ts { const args = getEffectiveCallArguments(callTarget); const argIndex = indexOf(args, arg); if (argIndex >= 0) { - const signature = getResolvedSignature(callTarget); + const signature = getResolvedOrAnySignature(callTarget); return getTypeAtPosition(signature, argIndex); } return undefined; @@ -11071,6 +11076,20 @@ namespace ts { return resolveCall(node, callSignatures, candidatesOutArray, headMessage); } + function resolveSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature { + switch (node.kind) { + case SyntaxKind.CallExpression: + return resolveCallExpression(node, candidatesOutArray); + case SyntaxKind.NewExpression: + return resolveNewExpression(node, candidatesOutArray); + case SyntaxKind.TaggedTemplateExpression: + return resolveTaggedTemplateExpression(node, candidatesOutArray); + case SyntaxKind.Decorator: + return resolveDecorator(node, candidatesOutArray); + } + Debug.fail("Branch in 'resolveSignature' should be unreachable."); + } + // candidatesOutArray is passed by signature help in the language service, and collectCandidates // must fill it up with the appropriate candidate signatures function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature { @@ -11079,26 +11098,23 @@ namespace ts { // However, it is possible that either candidatesOutArray was not passed in the first time, // or that a different candidatesOutArray was passed in. Therefore, we need to redo the work // to correctly fill the candidatesOutArray. - if (!links.resolvedSignature || candidatesOutArray) { - links.resolvedSignature = anySignature; - - if (node.kind === SyntaxKind.CallExpression) { - links.resolvedSignature = resolveCallExpression(node, candidatesOutArray); - } - else if (node.kind === SyntaxKind.NewExpression) { - links.resolvedSignature = resolveNewExpression(node, candidatesOutArray); - } - else if (node.kind === SyntaxKind.TaggedTemplateExpression) { - links.resolvedSignature = resolveTaggedTemplateExpression(node, candidatesOutArray); - } - else if (node.kind === SyntaxKind.Decorator) { - links.resolvedSignature = resolveDecorator(node, candidatesOutArray); - } - else { - Debug.fail("Branch in 'getResolvedSignature' should be unreachable."); - } + const cached = links.resolvedSignature; + if (cached && cached !== anySignature && !candidatesOutArray) { + return cached; } - return links.resolvedSignature; + links.resolvedSignature = anySignature; + const result = resolveSignature(node, candidatesOutArray); + // If signature resolution originated in control flow type analysis (for example to compute the + // assigned type in a flow assignment) we don't cache the result as it may be based on temporary + // types from the control flow analysis. + links.resolvedSignature = inFlowCheck ? cached : result; + return result; + } + + function getResolvedOrAnySignature(node: CallLikeExpression) { + // If we're already in the process of resolving the given signature, don't resolve again as + // that could cause infinite recursion. Instead, return anySignature. + return getNodeLinks(node).resolvedSignature === anySignature ? anySignature : getResolvedSignature(node); } function getInferredClassType(symbol: Symbol) {