MCPcopy Index your code
hub / github.com/microsoft/typescript-go / getTypeAtFlowLoopLabel

Method getTypeAtFlowLoopLabel

internal/checker/flow.go:1304–1381  ·  view source on GitHub ↗
(f *FlowState, flow *ast.FlowNode)

Source from the content-addressed store, hash-verified

1302}
1303
1304func (c *Checker) getTypeAtFlowLoopLabel(f *FlowState, flow *ast.FlowNode) FlowType {
1305 if f.refKey.IsZero() {
1306 f.refKey = c.getFlowReferenceKey(f)
1307 }
1308 if f.refKey == nonDottedNameCacheKey {
1309 // No cache key is generated when binding patterns are in unnarrowable situations
1310 return FlowType{t: f.declaredType}
1311 }
1312 key := FlowLoopKey{flowNode: flow, refKey: f.refKey}
1313 // If we have previously computed the control flow type for the reference at
1314 // this flow loop junction, return the cached type.
1315 if cached := c.flowLoopCache[key]; cached != nil {
1316 return FlowType{t: cached}
1317 }
1318 // If this flow loop junction and reference are already being processed, return
1319 // the union of the types computed for each branch so far, marked as incomplete.
1320 // It is possible to see an empty array in cases where loops are nested and the
1321 // back edge of the outer loop reaches an inner loop that is already being analyzed.
1322 // In such cases we restart the analysis of the inner loop, which will then see
1323 // a non-empty in-process array for the outer loop and eventually terminate because
1324 // the first antecedent of a loop junction is always the non-looping control flow
1325 // path that leads to the top.
1326 for _, loopInfo := range c.flowLoopStack {
1327 if loopInfo.key == key && len(loopInfo.types) != 0 {
1328 return c.newFlowType(c.getUnionOrEvolvingArrayType(f, loopInfo.types, UnionReductionLiteral), true /*incomplete*/)
1329 }
1330 }
1331 // Add the flow loop junction and reference to the in-process stack and analyze
1332 // each antecedent code path.
1333 antecedentTypes := make([]*Type, 0, 4)
1334 subtypeReduction := false
1335 var firstAntecedentType FlowType
1336 for list := flow.Antecedents; list != nil; list = list.Next {
1337 var flowType FlowType
1338 if firstAntecedentType.isNil() {
1339 // The first antecedent of a loop junction is always the non-looping control
1340 // flow path that leads to the top.
1341 firstAntecedentType = c.getTypeAtFlowNode(f, list.Flow)
1342 flowType = firstAntecedentType
1343 } else {
1344 // All but the first antecedent are the looping control flow paths that lead
1345 // back to the loop junction. We track these on the flow loop stack.
1346 c.flowLoopStack = append(c.flowLoopStack, FlowLoopInfo{key: key, types: antecedentTypes})
1347 saveFlowTypeCache := c.flowTypeCache
1348 c.flowTypeCache = nil
1349 flowType = c.getTypeAtFlowNode(f, list.Flow)
1350 c.flowTypeCache = saveFlowTypeCache
1351 c.flowLoopStack = c.flowLoopStack[:len(c.flowLoopStack)-1]
1352 // If we see a value appear in the cache it is a sign that control flow analysis
1353 // was restarted and completed by checkExpressionCached. We can simply pick up
1354 // the resulting type and bail out.
1355 if cached := c.flowLoopCache[key]; cached != nil {
1356 return FlowType{t: cached}
1357 }
1358 }
1359 antecedentTypes = core.AppendIfUnique(antecedentTypes, flowType.t)
1360 // If an antecedent type is not a subset of the declared type, we need to perform
1361 // subtype reduction. This happens when a "foreign" type is injected into the control

Callers 1

getTypeAtFlowNodeMethod · 0.95

Calls 12

getFlowReferenceKeyMethod · 0.95
newFlowTypeMethod · 0.95
isNilMethod · 0.95
getTypeAtFlowNodeMethod · 0.95
isTypeSubsetOfMethod · 0.95
AppendIfUniqueFunction · 0.92
IfElseFunction · 0.92
lenFunction · 0.85
makeFunction · 0.50
appendFunction · 0.50
IsZeroMethod · 0.45

Tested by

no test coverage detected