@@ -334,7 +334,8 @@ namespace ts {
334
334
335
335
let flowLoopStart = 0;
336
336
let flowLoopCount = 0;
337
- let visitedFlowCount = 0;
337
+ let sharedFlowCount = 0;
338
+ let flowAnalysisDisabled = false;
338
339
339
340
const emptyStringType = getLiteralType("");
340
341
const zeroType = getLiteralType(0);
@@ -352,8 +353,8 @@ namespace ts {
352
353
const flowLoopNodes: FlowNode[] = [];
353
354
const flowLoopKeys: string[] = [];
354
355
const flowLoopTypes: Type[][] = [];
355
- const visitedFlowNodes : FlowNode[] = [];
356
- const visitedFlowTypes : FlowType[] = [];
356
+ const sharedFlowNodes : FlowNode[] = [];
357
+ const sharedFlowTypes : FlowType[] = [];
357
358
const potentialThisCollisions: Node[] = [];
358
359
const potentialNewTargetCollisions: Node[] = [];
359
360
const awaitedTypeStack: number[] = [];
@@ -11459,14 +11460,25 @@ namespace ts {
11459
11460
return false;
11460
11461
}
11461
11462
11463
+ function reportFlowControlError(node: Node) {
11464
+ const block = <Block | ModuleBlock | SourceFile>findAncestor(node, isFunctionOrModuleBlock);
11465
+ const sourceFile = getSourceFileOfNode(node);
11466
+ const span = getSpanOfTokenAtPosition(sourceFile, block.statements.pos);
11467
+ diagnostics.add(createFileDiagnostic(sourceFile, span.start, span.length, Diagnostics.The_containing_function_or_module_body_is_too_large_for_control_flow_analysis));
11468
+ }
11469
+
11462
11470
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
11463
11471
let key: string;
11472
+ let flowDepth = 0;
11473
+ if (flowAnalysisDisabled) {
11474
+ return unknownType;
11475
+ }
11464
11476
if (!reference.flowNode || !couldBeUninitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
11465
11477
return declaredType;
11466
11478
}
11467
- const visitedFlowStart = visitedFlowCount ;
11479
+ const sharedFlowStart = sharedFlowCount ;
11468
11480
const evolvedType = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode));
11469
- visitedFlowCount = visitedFlowStart ;
11481
+ sharedFlowCount = sharedFlowStart ;
11470
11482
// When the reference is 'x' in an 'x.length', 'x.push(value)', 'x.unshift(value)' or x[n] = value' operation,
11471
11483
// we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations
11472
11484
// on empty arrays are possible without implicit any errors and new element types can be inferred without
@@ -11478,60 +11490,70 @@ namespace ts {
11478
11490
return resultType;
11479
11491
11480
11492
function getTypeAtFlowNode(flow: FlowNode): FlowType {
11493
+ if (flowDepth === 2500) {
11494
+ // We have made 2500 recursive invocations. To avoid overflowing the call stack we report an error
11495
+ // and disable further control flow analysis in the containing function or module body.
11496
+ flowAnalysisDisabled = true;
11497
+ reportFlowControlError(reference);
11498
+ return unknownType;
11499
+ }
11500
+ flowDepth++;
11481
11501
while (true) {
11482
- if (flow.flags & FlowFlags.Shared) {
11502
+ const flags = flow.flags;
11503
+ if (flags & FlowFlags.Shared) {
11483
11504
// We cache results of flow type resolution for shared nodes that were previously visited in
11484
11505
// the same getFlowTypeOfReference invocation. A node is considered shared when it is the
11485
11506
// antecedent of more than one node.
11486
- for (let i = visitedFlowStart; i < visitedFlowCount; i++) {
11487
- if (visitedFlowNodes[i] === flow) {
11488
- return visitedFlowTypes[i];
11507
+ for (let i = sharedFlowStart; i < sharedFlowCount; i++) {
11508
+ if (sharedFlowNodes[i] === flow) {
11509
+ flowDepth--;
11510
+ return sharedFlowTypes[i];
11489
11511
}
11490
11512
}
11491
11513
}
11492
11514
let type: FlowType;
11493
- if (flow. flags & FlowFlags.AfterFinally) {
11515
+ if (flags & FlowFlags.AfterFinally) {
11494
11516
// block flow edge: finally -> pre-try (for larger explanation check comment in binder.ts - bindTryStatement
11495
11517
(<AfterFinallyFlow>flow).locked = true;
11496
11518
type = getTypeAtFlowNode((<AfterFinallyFlow>flow).antecedent);
11497
11519
(<AfterFinallyFlow>flow).locked = false;
11498
11520
}
11499
- else if (flow. flags & FlowFlags.PreFinally) {
11521
+ else if (flags & FlowFlags.PreFinally) {
11500
11522
// locked pre-finally flows are filtered out in getTypeAtFlowBranchLabel
11501
11523
// so here just redirect to antecedent
11502
11524
flow = (<PreFinallyFlow>flow).antecedent;
11503
11525
continue;
11504
11526
}
11505
- else if (flow. flags & FlowFlags.Assignment) {
11527
+ else if (flags & FlowFlags.Assignment) {
11506
11528
type = getTypeAtFlowAssignment(<FlowAssignment>flow);
11507
11529
if (!type) {
11508
11530
flow = (<FlowAssignment>flow).antecedent;
11509
11531
continue;
11510
11532
}
11511
11533
}
11512
- else if (flow. flags & FlowFlags.Condition) {
11534
+ else if (flags & FlowFlags.Condition) {
11513
11535
type = getTypeAtFlowCondition(<FlowCondition>flow);
11514
11536
}
11515
- else if (flow. flags & FlowFlags.SwitchClause) {
11537
+ else if (flags & FlowFlags.SwitchClause) {
11516
11538
type = getTypeAtSwitchClause(<FlowSwitchClause>flow);
11517
11539
}
11518
- else if (flow. flags & FlowFlags.Label) {
11540
+ else if (flags & FlowFlags.Label) {
11519
11541
if ((<FlowLabel>flow).antecedents.length === 1) {
11520
11542
flow = (<FlowLabel>flow).antecedents[0];
11521
11543
continue;
11522
11544
}
11523
- type = flow. flags & FlowFlags.BranchLabel ?
11545
+ type = flags & FlowFlags.BranchLabel ?
11524
11546
getTypeAtFlowBranchLabel(<FlowLabel>flow) :
11525
11547
getTypeAtFlowLoopLabel(<FlowLabel>flow);
11526
11548
}
11527
- else if (flow. flags & FlowFlags.ArrayMutation) {
11549
+ else if (flags & FlowFlags.ArrayMutation) {
11528
11550
type = getTypeAtFlowArrayMutation(<FlowArrayMutation>flow);
11529
11551
if (!type) {
11530
11552
flow = (<FlowArrayMutation>flow).antecedent;
11531
11553
continue;
11532
11554
}
11533
11555
}
11534
- else if (flow. flags & FlowFlags.Start) {
11556
+ else if (flags & FlowFlags.Start) {
11535
11557
// Check if we should continue with the control flow of the containing function.
11536
11558
const container = (<FlowStart>flow).container;
11537
11559
if (container && container !== flowContainer && reference.kind !== SyntaxKind.PropertyAccessExpression && reference.kind !== SyntaxKind.ThisKeyword) {
@@ -11546,12 +11568,13 @@ namespace ts {
11546
11568
// simply return the non-auto declared type to reduce follow-on errors.
11547
11569
type = convertAutoToAny(declaredType);
11548
11570
}
11549
- if (flow. flags & FlowFlags.Shared) {
11571
+ if (flags & FlowFlags.Shared) {
11550
11572
// Record visited node and the associated type in the cache.
11551
- visitedFlowNodes[visitedFlowCount ] = flow;
11552
- visitedFlowTypes[visitedFlowCount ] = type;
11553
- visitedFlowCount ++;
11573
+ sharedFlowNodes[sharedFlowCount ] = flow;
11574
+ sharedFlowTypes[sharedFlowCount ] = type;
11575
+ sharedFlowCount ++;
11554
11576
}
11577
+ flowDepth--;
11555
11578
return type;
11556
11579
}
11557
11580
}
@@ -11589,29 +11612,31 @@ namespace ts {
11589
11612
}
11590
11613
11591
11614
function getTypeAtFlowArrayMutation(flow: FlowArrayMutation): FlowType {
11592
- const node = flow.node;
11593
- const expr = node.kind === SyntaxKind.CallExpression ?
11594
- (<PropertyAccessExpression>(<CallExpression>node).expression).expression :
11595
- (<ElementAccessExpression>(<BinaryExpression>node).left).expression;
11596
- if (isMatchingReference(reference, getReferenceCandidate(expr))) {
11597
- const flowType = getTypeAtFlowNode(flow.antecedent);
11598
- const type = getTypeFromFlowType(flowType);
11599
- if (getObjectFlags(type) & ObjectFlags.EvolvingArray) {
11600
- let evolvedType = <EvolvingArrayType>type;
11601
- if (node.kind === SyntaxKind.CallExpression) {
11602
- for (const arg of (<CallExpression>node).arguments) {
11603
- evolvedType = addEvolvingArrayElementType(evolvedType, arg);
11615
+ if (declaredType === autoType || declaredType === autoArrayType) {
11616
+ const node = flow.node;
11617
+ const expr = node.kind === SyntaxKind.CallExpression ?
11618
+ (<PropertyAccessExpression>(<CallExpression>node).expression).expression :
11619
+ (<ElementAccessExpression>(<BinaryExpression>node).left).expression;
11620
+ if (isMatchingReference(reference, getReferenceCandidate(expr))) {
11621
+ const flowType = getTypeAtFlowNode(flow.antecedent);
11622
+ const type = getTypeFromFlowType(flowType);
11623
+ if (getObjectFlags(type) & ObjectFlags.EvolvingArray) {
11624
+ let evolvedType = <EvolvingArrayType>type;
11625
+ if (node.kind === SyntaxKind.CallExpression) {
11626
+ for (const arg of (<CallExpression>node).arguments) {
11627
+ evolvedType = addEvolvingArrayElementType(evolvedType, arg);
11628
+ }
11604
11629
}
11605
- }
11606
- else {
11607
- const indexType = getTypeOfExpression((<ElementAccessExpression>(<BinaryExpression>node).left).argumentExpression);
11608
- if (isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) {
11609
- evolvedType = addEvolvingArrayElementType(evolvedType, (<BinaryExpression>node).right);
11630
+ else {
11631
+ const indexType = getTypeOfExpression((<ElementAccessExpression>(<BinaryExpression>node).left).argumentExpression);
11632
+ if (isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) {
11633
+ evolvedType = addEvolvingArrayElementType(evolvedType, (<BinaryExpression>node).right);
11634
+ }
11610
11635
}
11636
+ return evolvedType === type ? flowType : createFlowType(evolvedType, isIncomplete(flowType));
11611
11637
}
11612
- return evolvedType === type ? flowType : createFlowType(evolvedType, isIncomplete(flowType)) ;
11638
+ return flowType;
11613
11639
}
11614
- return flowType;
11615
11640
}
11616
11641
return undefined;
11617
11642
}
@@ -19935,7 +19960,14 @@ namespace ts {
19935
19960
if (node.kind === SyntaxKind.Block) {
19936
19961
checkGrammarStatementInAmbientContext(node);
19937
19962
}
19938
- forEach(node.statements, checkSourceElement);
19963
+ if (isFunctionOrModuleBlock(node)) {
19964
+ const saveFlowAnalysisDisabled = flowAnalysisDisabled;
19965
+ forEach(node.statements, checkSourceElement);
19966
+ flowAnalysisDisabled = saveFlowAnalysisDisabled;
19967
+ }
19968
+ else {
19969
+ forEach(node.statements, checkSourceElement);
19970
+ }
19939
19971
if (node.locals) {
19940
19972
registerForUnusedIdentifiersCheck(node);
19941
19973
}
@@ -22525,6 +22557,7 @@ namespace ts {
22525
22557
22526
22558
deferredNodes = [];
22527
22559
deferredUnusedIdentifierNodes = produceDiagnostics && noUnusedIdentifiers ? [] : undefined;
22560
+ flowAnalysisDisabled = false;
22528
22561
22529
22562
forEach(node.statements, checkSourceElement);
22530
22563
0 commit comments