(node *ast.Node)
| 1991 | } |
| 1992 | |
| 1993 | func (b *Binder) bindTryStatement(node *ast.Node) { |
| 1994 | // We conservatively assume that *any* code in the try block can cause an exception, but we only need |
| 1995 | // to track code that causes mutations (because only mutations widen the possible control flow type of |
| 1996 | // a variable). The exceptionLabel is the target label for control flows that result from exceptions. |
| 1997 | // We add all mutation flow nodes as antecedents of this label such that we can analyze them as possible |
| 1998 | // antecedents of the start of catch or finally blocks. Furthermore, we add the current control flow to |
| 1999 | // represent exceptions that occur before any mutations. |
| 2000 | stmt := node.AsTryStatement() |
| 2001 | saveReturnTarget := b.currentReturnTarget |
| 2002 | saveExceptionTarget := b.currentExceptionTarget |
| 2003 | normalExitLabel := b.createBranchLabel() |
| 2004 | returnLabel := b.createBranchLabel() |
| 2005 | exceptionLabel := b.createBranchLabel() |
| 2006 | if stmt.FinallyBlock != nil { |
| 2007 | b.currentReturnTarget = returnLabel |
| 2008 | } |
| 2009 | b.addAntecedent(exceptionLabel, b.currentFlow) |
| 2010 | b.currentExceptionTarget = exceptionLabel |
| 2011 | b.bind(stmt.TryBlock) |
| 2012 | b.addAntecedent(normalExitLabel, b.currentFlow) |
| 2013 | if stmt.CatchClause != nil { |
| 2014 | // Start of catch clause is the target of exceptions from try block. |
| 2015 | b.currentFlow = b.finishFlowLabel(exceptionLabel) |
| 2016 | // The currentExceptionTarget now represents control flows from exceptions in the catch clause. |
| 2017 | // Effectively, in a try-catch-finally, if an exception occurs in the try block, the catch block |
| 2018 | // acts like a second try block. |
| 2019 | exceptionLabel = b.createBranchLabel() |
| 2020 | b.addAntecedent(exceptionLabel, b.currentFlow) |
| 2021 | b.currentExceptionTarget = exceptionLabel |
| 2022 | b.bind(stmt.CatchClause) |
| 2023 | b.addAntecedent(normalExitLabel, b.currentFlow) |
| 2024 | } |
| 2025 | b.currentReturnTarget = saveReturnTarget |
| 2026 | b.currentExceptionTarget = saveExceptionTarget |
| 2027 | if stmt.FinallyBlock != nil { |
| 2028 | // Possible ways control can reach the finally block: |
| 2029 | // 1) Normal completion of try block of a try-finally or try-catch-finally |
| 2030 | // 2) Normal completion of catch block (following exception in try block) of a try-catch-finally |
| 2031 | // 3) Return in try or catch block of a try-finally or try-catch-finally |
| 2032 | // 4) Exception in try block of a try-finally |
| 2033 | // 5) Exception in catch block of a try-catch-finally |
| 2034 | // When analyzing a control flow graph that starts inside a finally block we want to consider all |
| 2035 | // five possibilities above. However, when analyzing a control flow graph that starts outside (past) |
| 2036 | // the finally block, we only want to consider the first two (if we're past a finally block then it |
| 2037 | // must have completed normally). Likewise, when analyzing a control flow graph from return statements |
| 2038 | // in try or catch blocks in an IIFE, we only want to consider the third. To make this possible, we |
| 2039 | // inject a ReduceLabel node into the control flow graph. This node contains an alternate reduced |
| 2040 | // set of antecedents for the pre-finally label. As control flow analysis passes by a ReduceLabel |
| 2041 | // node, the pre-finally label is temporarily switched to the reduced antecedent set. |
| 2042 | finallyLabel := b.createBranchLabel() |
| 2043 | finallyLabel.Antecedents = b.combineFlowLists(normalExitLabel.Antecedents, b.combineFlowLists(exceptionLabel.Antecedents, returnLabel.Antecedents)) |
| 2044 | b.currentFlow = finallyLabel |
| 2045 | b.bind(stmt.FinallyBlock) |
| 2046 | if b.currentFlow.Flags&ast.FlowFlagsUnreachable != 0 { |
| 2047 | // If the end of the finally block is unreachable, the end of the entire try statement is unreachable. |
| 2048 | b.currentFlow = b.unreachableFlow |
| 2049 | } else { |
| 2050 | // If we have an IIFE return target and return statements in the try or catch blocks, add a control |
no test coverage detected