@@ -952,6 +952,10 @@ namespace ts {
952952 return initFlowNode ( { flags : FlowFlags . LoopLabel , antecedents : undefined } ) ;
953953 }
954954
955+ function createReduceLabel ( target : FlowLabel , antecedents : FlowNode [ ] , antecedent : FlowNode ) : FlowReduceLabel {
956+ return initFlowNode ( { flags : FlowFlags . ReduceLabel , target, antecedents, antecedent } ) ;
957+ }
958+
955959 function setFlowNodeReferenced ( flow : FlowNode ) {
956960 // On first reference we set the Referenced flag, thereafter we set the Shared flag
957961 flow . flags |= flow . flags & FlowFlags . Referenced ? FlowFlags . Shared : FlowFlags . Referenced ;
@@ -1209,35 +1213,36 @@ namespace ts {
12091213 }
12101214
12111215 function bindTryStatement ( node : TryStatement ) : void {
1212- const preFinallyLabel = createBranchLabel ( ) ;
12131216 // We conservatively assume that *any* code in the try block can cause an exception, but we only need
12141217 // to track code that causes mutations (because only mutations widen the possible control flow type of
1215- // a variable). The currentExceptionTarget is the target label for control flows that result from
1216- // exceptions. We add all mutation flow nodes as antecedents of this label such that we can analyze them
1217- // as possible antecedents of the start of catch or finally blocks. Furthermore, we add the current
1218- // control flow to represent exceptions that occur before any mutations.
1218+ // a variable). The exceptionLabel is the target label for control flows that result from exceptions.
1219+ // We add all mutation flow nodes as antecedents of this label such that we can analyze them as possible
1220+ // antecedents of the start of catch or finally blocks. Furthermore, we add the current control flow to
1221+ // represent exceptions that occur before any mutations.
12191222 const saveReturnTarget = currentReturnTarget ;
12201223 const saveExceptionTarget = currentExceptionTarget ;
1221- currentReturnTarget = createBranchLabel ( ) ;
1222- currentExceptionTarget = node . catchClause ? createBranchLabel ( ) : currentReturnTarget ;
1223- addAntecedent ( currentExceptionTarget , currentFlow ) ;
1224+ const normalExitLabel = createBranchLabel ( ) ;
1225+ const returnLabel = createBranchLabel ( ) ;
1226+ let exceptionLabel = createBranchLabel ( ) ;
1227+ if ( node . finallyBlock ) {
1228+ currentReturnTarget = returnLabel ;
1229+ }
1230+ addAntecedent ( exceptionLabel , currentFlow ) ;
1231+ currentExceptionTarget = exceptionLabel ;
12241232 bind ( node . tryBlock ) ;
1225- addAntecedent ( preFinallyLabel , currentFlow ) ;
1226- const flowAfterTry = currentFlow ;
1227- let flowAfterCatch = unreachableFlow ;
1233+ addAntecedent ( normalExitLabel , currentFlow ) ;
12281234 if ( node . catchClause ) {
12291235 // Start of catch clause is the target of exceptions from try block.
1230- currentFlow = finishFlowLabel ( currentExceptionTarget ) ;
1236+ currentFlow = finishFlowLabel ( exceptionLabel ) ;
12311237 // The currentExceptionTarget now represents control flows from exceptions in the catch clause.
12321238 // Effectively, in a try-catch-finally, if an exception occurs in the try block, the catch block
12331239 // acts like a second try block.
1234- currentExceptionTarget = currentReturnTarget ;
1235- addAntecedent ( currentExceptionTarget , currentFlow ) ;
1240+ exceptionLabel = createBranchLabel ( ) ;
1241+ addAntecedent ( exceptionLabel , currentFlow ) ;
1242+ currentExceptionTarget = exceptionLabel ;
12361243 bind ( node . catchClause ) ;
1237- addAntecedent ( preFinallyLabel , currentFlow ) ;
1238- flowAfterCatch = currentFlow ;
1244+ addAntecedent ( normalExitLabel , currentFlow ) ;
12391245 }
1240- const exceptionTarget = finishFlowLabel ( currentExceptionTarget ) ;
12411246 currentReturnTarget = saveReturnTarget ;
12421247 currentExceptionTarget = saveExceptionTarget ;
12431248 if ( node . finallyBlock ) {
@@ -1250,35 +1255,33 @@ namespace ts {
12501255 // When analyzing a control flow graph that starts inside a finally block we want to consider all
12511256 // five possibilities above. However, when analyzing a control flow graph that starts outside (past)
12521257 // the finally block, we only want to consider the first two (if we're past a finally block then it
1253- // must have completed normally). To make this possible, we inject two extra nodes into the control
1254- // flow graph: An after-finally with an antecedent of the control flow at the end of the finally
1255- // block, and a pre-finally with an antecedent that represents all exceptional control flows. The
1256- // 'lock' property of the pre-finally references the after-finally, and the after-finally has a
1257- // boolean 'locked' property that we set to true when analyzing a control flow that contained the
1258- // the after-finally node. When the lock associated with a pre-finally is locked, the antecedent of
1259- // the pre-finally (i.e. the exceptional control flows) are skipped.
1260- const preFinallyFlow : PreFinallyFlow = initFlowNode ( { flags : FlowFlags . PreFinally , antecedent : exceptionTarget , lock : { } } ) ;
1261- addAntecedent ( preFinallyLabel , preFinallyFlow ) ;
1262- currentFlow = finishFlowLabel ( preFinallyLabel ) ;
1258+ // must have completed normally). Likewise, when analyzing a control flow graph from return statements
1259+ // in try or catch blocks in an IIFE, we only want to consider the third. To make this possible, we
1260+ // inject a ReduceLabel node into the control flow graph. This node contains an alternate reduced
1261+ // set of antecedents for the pre-finally label. As control flow analysis passes by a ReduceLabel
1262+ // node, the pre-finally label is temporarily switched to the reduced antecedent set.
1263+ const finallyLabel = createBranchLabel ( ) ;
1264+ finallyLabel . antecedents = concatenate ( concatenate ( normalExitLabel . antecedents , exceptionLabel . antecedents ) , returnLabel . antecedents ) ;
1265+ currentFlow = finallyLabel ;
12631266 bind ( node . finallyBlock ) ;
1264- // If the end of the finally block is reachable, but the end of the try and catch blocks are not,
1265- // convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should
1266- // result in an unreachable current control flow.
1267- if ( ! ( currentFlow . flags & FlowFlags . Unreachable ) ) {
1268- if ( ( flowAfterTry . flags & FlowFlags . Unreachable ) && ( flowAfterCatch . flags & FlowFlags . Unreachable ) ) {
1269- currentFlow = flowAfterTry === reportedUnreachableFlow || flowAfterCatch === reportedUnreachableFlow
1270- ? reportedUnreachableFlow
1271- : unreachableFlow ;
1272- }
1267+ if ( currentFlow . flags & FlowFlags . Unreachable ) {
1268+ // If the end of the finally block is unreachable, the end of the entire try statement is unreachable.
1269+ currentFlow = unreachableFlow ;
12731270 }
1274- if ( ! ( currentFlow . flags & FlowFlags . Unreachable ) ) {
1275- const afterFinallyFlow : AfterFinallyFlow = initFlowNode ( { flags : FlowFlags . AfterFinally , antecedent : currentFlow } ) ;
1276- preFinallyFlow . lock = afterFinallyFlow ;
1277- currentFlow = afterFinallyFlow ;
1271+ else {
1272+ // If we have an IIFE return target and return statements in the try or catch blocks, add a control
1273+ // flow that goes back through the finally block and back through only the return statements.
1274+ if ( currentReturnTarget && returnLabel . antecedents ) {
1275+ addAntecedent ( currentReturnTarget , createReduceLabel ( finallyLabel , returnLabel . antecedents , currentFlow ) ) ;
1276+ }
1277+ // If the end of the finally block is reachable, but the end of the try and catch blocks are not,
1278+ // convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should
1279+ // result in an unreachable current control flow.
1280+ currentFlow = normalExitLabel . antecedents ? createReduceLabel ( finallyLabel , normalExitLabel . antecedents , currentFlow ) : unreachableFlow ;
12781281 }
12791282 }
12801283 else {
1281- currentFlow = finishFlowLabel ( preFinallyLabel ) ;
1284+ currentFlow = finishFlowLabel ( normalExitLabel ) ;
12821285 }
12831286 }
12841287
0 commit comments