(f *FlowState, flow *ast.FlowNode)
| 115 | } |
| 116 | |
| 117 | func (c *Checker) getTypeAtFlowNode(f *FlowState, flow *ast.FlowNode) FlowType { |
| 118 | if f.depth == 2000 { |
| 119 | // We have made 2000 recursive invocations. To avoid overflowing the call stack we report an error |
| 120 | // and disable further control flow analysis in the containing function or module body. |
| 121 | if tr := c.tracer; tr != nil { |
| 122 | tr.Instant(tracing.PhaseCheckTypes, "getTypeAtFlowNode_DepthLimit", map[string]any{"depth": f.depth}) |
| 123 | } |
| 124 | c.flowAnalysisDisabled = true |
| 125 | c.reportFlowControlError(f.reference) |
| 126 | return FlowType{t: c.errorType} |
| 127 | } |
| 128 | f.depth++ |
| 129 | var sharedFlow *ast.FlowNode |
| 130 | for { |
| 131 | flags := flow.Flags |
| 132 | if flags&ast.FlowFlagsShared != 0 { |
| 133 | // We cache results of flow type resolution for shared nodes that were previously visited in |
| 134 | // the same getFlowTypeOfReference invocation. A node is considered shared when it is the |
| 135 | // antecedent of more than one node. |
| 136 | for i := f.sharedFlowStart; i < len(c.sharedFlows); i++ { |
| 137 | if c.sharedFlows[i].flow == flow { |
| 138 | f.depth-- |
| 139 | return c.sharedFlows[i].flowType |
| 140 | } |
| 141 | } |
| 142 | sharedFlow = flow |
| 143 | } |
| 144 | var t FlowType |
| 145 | switch { |
| 146 | case flags&ast.FlowFlagsAssignment != 0: |
| 147 | t = c.getTypeAtFlowAssignment(f, flow) |
| 148 | if t.isNil() { |
| 149 | flow = flow.Antecedent |
| 150 | continue |
| 151 | } |
| 152 | case flags&ast.FlowFlagsCall != 0: |
| 153 | t = c.getTypeAtFlowCall(f, flow) |
| 154 | if t.isNil() { |
| 155 | flow = flow.Antecedent |
| 156 | continue |
| 157 | } |
| 158 | case flags&ast.FlowFlagsCondition != 0: |
| 159 | t = c.getTypeAtFlowCondition(f, flow) |
| 160 | case flags&ast.FlowFlagsSwitchClause != 0: |
| 161 | t = c.getTypeAtSwitchClause(f, flow) |
| 162 | case flags&ast.FlowFlagsBranchLabel != 0: |
| 163 | antecedents := getBranchLabelAntecedents(flow, f.reduceLabels) |
| 164 | if antecedents.Next == nil { |
| 165 | flow = antecedents.Flow |
| 166 | continue |
| 167 | } |
| 168 | t = c.getTypeAtFlowBranchLabel(f, flow, antecedents) |
| 169 | case flags&ast.FlowFlagsLoopLabel != 0: |
| 170 | if flow.Antecedents.Next == nil { |
| 171 | flow = flow.Antecedents.Flow |
| 172 | continue |
| 173 | } |
| 174 | t = c.getTypeAtFlowLoopLabel(f, flow) |
no test coverage detected