@@ -1045,7 +1045,35 @@ namespace ts {
10451045 if ( node . finallyBlock ) {
10461046 // in finally flow is combined from pre-try/flow from try/flow from catch
10471047 // pre-flow is necessary to make sure that finally is reachable even if finally flows in both try and finally blocks are unreachable
1048- addAntecedent ( preFinallyLabel , preTryFlow ) ;
1048+
1049+ // also for finally blocks we inject two extra edges into the flow graph.
1050+ // first -> edge that connects pre-try flow with the label at the beginning of the finally block, it has lock associated with it
1051+ // second -> edge that represents post-finally flow.
1052+ // these edges are used in following scenario:
1053+ // let a; (1)
1054+ // try { a = someOperation(); (2)}
1055+ // finally { (3) console.log(a) } (4)
1056+ // (5) a
1057+
1058+ // flow graph for this case looks roughly like this (arrows show ):
1059+ // (1-pre-try-flow) <--.. <-- (2-post-try-flow)
1060+ // ^ ^
1061+ // |*****(3-pre-finally-label) -----|
1062+ // ^
1063+ // |-- ... <-- (4-post-finally-label) <--- (5)
1064+ // In case when we walk the flow starting from inside the finally block we want to take edge '*****' into account
1065+ // since it ensures that finally is always reachable. However when we start outside the finally block and go through label (5)
1066+ // then edge '*****' should be discarded because label 4 is only reachable if post-finally label-4 is reachable
1067+ // Simply speaking code inside finally block is treated as reachable as pre-try-flow
1068+ // since we conservatively assume that any line in try block can throw or return in which case we'll enter finally.
1069+ // However code after finally is reachable only if control flow was not abrupted in try/catch or finally blocks - it should be composed from
1070+ // final flows of these blocks without taking pre-try flow into account.
1071+ //
1072+ // extra edges that we inject allows to control this behavior
1073+ // if when walking the flow we step on post-finally edge - we can mark matching pre-finally edge as locked so it will be skipped.
1074+ const preFinallyFlow : PreFinallyFlow = { flags : FlowFlags . PreFinally , antecedent : preTryFlow , lock : { } } ;
1075+ addAntecedent ( preFinallyLabel , preFinallyFlow ) ;
1076+
10491077 currentFlow = finishFlowLabel ( preFinallyLabel ) ;
10501078 bind ( node . finallyBlock ) ;
10511079 // if flow after finally is unreachable - keep it
@@ -1061,6 +1089,11 @@ namespace ts {
10611089 : unreachableFlow ;
10621090 }
10631091 }
1092+ if ( ! ( currentFlow . flags & FlowFlags . Unreachable ) ) {
1093+ const afterFinallyFlow : AfterFinallyFlow = { flags : FlowFlags . AfterFinally , antecedent : currentFlow } ;
1094+ preFinallyFlow . lock = afterFinallyFlow ;
1095+ currentFlow = afterFinallyFlow ;
1096+ }
10641097 }
10651098 else {
10661099 currentFlow = finishFlowLabel ( preFinallyLabel ) ;
0 commit comments