diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c3e3473e3f8..06e21e18501 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11486,6 +11486,13 @@ namespace ts { return false; } + function reportFlowControlError(node: Node) { + const block = findAncestor(node, isFunctionOrModuleBlock); + const sourceFile = getSourceFileOfNode(node); + const span = getSpanOfTokenAtPosition(sourceFile, block.statements.pos); + diagnostics.add(createFileDiagnostic(sourceFile, span.start, span.length, Diagnostics.The_containing_function_or_module_body_is_too_large_for_control_flow_analysis)); + } + function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) { let key: string; let flowLength = 0; @@ -11509,15 +11516,15 @@ namespace ts { return resultType; function getTypeAtFlowNode(flow: FlowNode): FlowType { - const saveFlowLength = flowLength; + flowLength++; while (true) { flowLength++; - if (flowLength === 5000) { - // The length of this particular control flow path is 5000 nodes or more. Rather than spending an - // excessive amount of time and possibly overflowing the call stack, we report an error and disable - // further control flow analysis in the containing function or module body. + if (flowLength >= 5000) { + // We have visited as many as 5000 nodes through as many as 2500 recursive invocations. Rather than + // spending an excessive amount of time and possibly overflowing the call stack, we report an error + // and disable further control flow analysis in the containing function or module body. flowAnalysisDisabled = true; - error(reference, Diagnostics.The_body_of_the_containing_function_or_module_is_too_large_for_control_flow_analysis); + reportFlowControlError(reference); return unknownType; } const flags = flow.flags; @@ -11527,7 +11534,6 @@ namespace ts { // antecedent of more than one node. for (let i = visitedFlowStart; i < visitedFlowCount; i++) { if (visitedFlowNodes[i] === flow) { - flowLength = saveFlowLength; return visitedFlowTypes[i]; } } @@ -11595,7 +11601,6 @@ namespace ts { visitedFlowTypes[visitedFlowCount] = type; visitedFlowCount++; } - flowLength = saveFlowLength; return type; } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index b3668cc5acf..5cfa5f12731 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1920,7 +1920,7 @@ "category": "Error", "code": 2562 }, - "The body of the containing function or module is too large for control flow analysis.": { + "The containing function or module body is too large for control flow analysis.": { "category": "Error", "code": 2563 }, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2a07d2b6560..7c3dce15448 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4790,6 +4790,11 @@ namespace ts { return false; } + /* @internal */ + export function isFunctionOrModuleBlock(node: Node): boolean { + return isSourceFile(node) || isModuleBlock(node) || isBlock(node) && isFunctionLike(node.parent); + } + // Classes export function isClassElement(node: Node): node is ClassElement { const kind = node.kind;