@@ -100,6 +100,8 @@ namespace ts {
100
100
IsObjectLiteralOrClassExpressionMethod = 1 << 7 ,
101
101
}
102
102
103
+ let flowNodeCreated : < T extends FlowNode > ( node : T ) => T = identity ;
104
+
103
105
const binder = createBinder ( ) ;
104
106
105
107
export function bindSourceFile ( file : SourceFile , options : CompilerOptions ) {
@@ -530,6 +532,7 @@ namespace ts {
530
532
blockScopeContainer . locals = undefined ;
531
533
}
532
534
if ( containerFlags & ContainerFlags . IsControlFlowContainer ) {
535
+ const saveFlowNodeCreated = flowNodeCreated ;
533
536
const saveCurrentFlow = currentFlow ;
534
537
const saveBreakTarget = currentBreakTarget ;
535
538
const saveContinueTarget = currentContinueTarget ;
@@ -553,6 +556,7 @@ namespace ts {
553
556
currentContinueTarget = undefined ;
554
557
activeLabels = undefined ;
555
558
hasExplicitReturn = false ;
559
+ flowNodeCreated = identity ;
556
560
bindChildren ( node ) ;
557
561
// Reset all reachability check related flags on node (for incremental scenarios)
558
562
node . flags &= ~ NodeFlags . ReachabilityAndEmitFlags ;
@@ -579,6 +583,7 @@ namespace ts {
579
583
currentReturnTarget = saveReturnTarget ;
580
584
activeLabels = saveActiveLabels ;
581
585
hasExplicitReturn = saveHasExplicitReturn ;
586
+ flowNodeCreated = saveFlowNodeCreated ;
582
587
}
583
588
else if ( containerFlags & ContainerFlags . IsInterface ) {
584
589
seenThisKeyword = false ;
@@ -858,25 +863,25 @@ namespace ts {
858
863
return antecedent ;
859
864
}
860
865
setFlowNodeReferenced ( antecedent ) ;
861
- return { flags, expression, antecedent } ;
866
+ return flowNodeCreated ( { flags, expression, antecedent } ) ;
862
867
}
863
868
864
869
function createFlowSwitchClause ( antecedent : FlowNode , switchStatement : SwitchStatement , clauseStart : number , clauseEnd : number ) : FlowNode {
865
870
if ( ! isNarrowingExpression ( switchStatement . expression ) ) {
866
871
return antecedent ;
867
872
}
868
873
setFlowNodeReferenced ( antecedent ) ;
869
- return { flags : FlowFlags . SwitchClause , switchStatement, clauseStart, clauseEnd, antecedent } ;
874
+ return flowNodeCreated ( { flags : FlowFlags . SwitchClause , switchStatement, clauseStart, clauseEnd, antecedent } ) ;
870
875
}
871
876
872
877
function createFlowAssignment ( antecedent : FlowNode , node : Expression | VariableDeclaration | BindingElement ) : FlowNode {
873
878
setFlowNodeReferenced ( antecedent ) ;
874
- return { flags : FlowFlags . Assignment , antecedent, node } ;
879
+ return flowNodeCreated ( { flags : FlowFlags . Assignment , antecedent, node } ) ;
875
880
}
876
881
877
882
function createFlowArrayMutation ( antecedent : FlowNode , node : CallExpression | BinaryExpression ) : FlowNode {
878
883
setFlowNodeReferenced ( antecedent ) ;
879
- const res : FlowArrayMutation = { flags : FlowFlags . ArrayMutation , antecedent, node } ;
884
+ const res : FlowArrayMutation = flowNodeCreated ( { flags : FlowFlags . ArrayMutation , antecedent, node } ) ;
880
885
return res ;
881
886
}
882
887
@@ -1080,21 +1085,49 @@ namespace ts {
1080
1085
function bindTryStatement ( node : TryStatement ) : void {
1081
1086
const preFinallyLabel = createBranchLabel ( ) ;
1082
1087
const preTryFlow = currentFlow ;
1083
- // TODO: Every statement in try block is potentially an exit point!
1088
+ const tryPriors : FlowNode [ ] = [ ] ;
1089
+ const oldFlowNodeCreated = flowNodeCreated ;
1090
+ // We hook the creation of all flow nodes within the `try` scope and store them so we can add _all_ of them
1091
+ // as possible antecedents of the start of the `catch` or `finally` blocks.
1092
+ // Don't bother intercepting the call if there's no finally or catch block that needs the information
1093
+ if ( node . catchClause || node . finallyBlock ) {
1094
+ flowNodeCreated = node => ( tryPriors . push ( node ) , node ) ;
1095
+ }
1084
1096
bind ( node . tryBlock ) ;
1097
+ flowNodeCreated = oldFlowNodeCreated ;
1085
1098
addAntecedent ( preFinallyLabel , currentFlow ) ;
1086
1099
1087
1100
const flowAfterTry = currentFlow ;
1088
1101
let flowAfterCatch = unreachableFlow ;
1089
1102
1090
1103
if ( node . catchClause ) {
1091
1104
currentFlow = preTryFlow ;
1105
+ if ( tryPriors . length ) {
1106
+ const preCatchFlow = createBranchLabel ( ) ;
1107
+ addAntecedent ( preCatchFlow , currentFlow ) ;
1108
+ for ( const p of tryPriors ) {
1109
+ addAntecedent ( preCatchFlow , p ) ;
1110
+ }
1111
+ currentFlow = finishFlowLabel ( preCatchFlow ) ;
1112
+ }
1113
+
1092
1114
bind ( node . catchClause ) ;
1093
1115
addAntecedent ( preFinallyLabel , currentFlow ) ;
1094
1116
1095
1117
flowAfterCatch = currentFlow ;
1096
1118
}
1097
1119
if ( node . finallyBlock ) {
1120
+ // We add the nodes within the `try` block to the `finally`'s antecedents if there's no catch block
1121
+ // (If there is a `catch` block, it will have all these antecedents instead, and the `finally` will
1122
+ // have the end of the `try` block and the end of the `catch` block)
1123
+ if ( ! node . catchClause ) {
1124
+ if ( tryPriors . length ) {
1125
+ for ( const p of tryPriors ) {
1126
+ addAntecedent ( preFinallyLabel , p ) ;
1127
+ }
1128
+ }
1129
+ }
1130
+
1098
1131
// in finally flow is combined from pre-try/flow from try/flow from catch
1099
1132
// pre-flow is necessary to make sure that finally is reachable even if finally flows in both try and finally blocks are unreachable
1100
1133
@@ -1142,7 +1175,7 @@ namespace ts {
1142
1175
}
1143
1176
}
1144
1177
if ( ! ( currentFlow . flags & FlowFlags . Unreachable ) ) {
1145
- const afterFinallyFlow : AfterFinallyFlow = { flags : FlowFlags . AfterFinally , antecedent : currentFlow } ;
1178
+ const afterFinallyFlow : AfterFinallyFlow = flowNodeCreated ( { flags : FlowFlags . AfterFinally , antecedent : currentFlow } ) ;
1146
1179
preFinallyFlow . lock = afterFinallyFlow ;
1147
1180
currentFlow = afterFinallyFlow ;
1148
1181
}
0 commit comments