backpropAcrossRange handles range expression (e.g., "for i, v := range lst"), it is designed to be called from backpropAcrossAssignment as a finer-grained handler for special assignment cases.
(rootNode *RootAssertionNode, lhs []ast.Expr, rhs ast.Expr)
| 412 | // backpropAcrossRange handles range expression (e.g., "for i, v := range lst"), it is designed to |
| 413 | // be called from backpropAcrossAssignment as a finer-grained handler for special assignment cases. |
| 414 | func backpropAcrossRange(rootNode *RootAssertionNode, lhs []ast.Expr, rhs ast.Expr) error { |
| 415 | // produceAsIndex(i) marks the ith lhs expression as a range index, producing it as non-nil |
| 416 | // because it necessarily has basic type (int or char) |
| 417 | produceAsIndex := func(i int) { |
| 418 | // if nonempty, produce the index as definitely non-nil |
| 419 | if !asthelper.IsEmptyExpr(lhs[i]) { |
| 420 | rootNode.AddProduction(&annotation.ProduceTrigger{ |
| 421 | Annotation: &annotation.RangeIndexAssignment{ProduceTriggerNever: &annotation.ProduceTriggerNever{}}, |
| 422 | Expr: lhs[i], |
| 423 | }) |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | // produceAsDeepRHS(i) marks the ith lhs expression as flowing deeply from the rhs |
| 428 | produceAsDeepRHS := func(i int) { |
| 429 | // if nonempty, produce the ranging value from the deep nilability of the rhs |
| 430 | // we can't track the rhs of ranges since we would need to discover non-nil assignments |
| 431 | // to an unbounded number of indices to conclude anything other than the annotation-based |
| 432 | // deep nilability of rhs |
| 433 | if !asthelper.IsEmptyExpr(lhs[i]) { |
| 434 | producer := exprAsDeepProducer(rootNode, rhs) |
| 435 | producer.SetNeedsGuard(false) |
| 436 | |
| 437 | rootNode.AddProduction(&annotation.ProduceTrigger{ |
| 438 | // we remove the guard on any deep types read from a range because reading |
| 439 | // them through a range guarantees they exist, removing the need for an ok check |
| 440 | Annotation: producer, |
| 441 | Expr: lhs[i], |
| 442 | }) |
| 443 | } |
| 444 | } |
| 445 | |
| 446 | // produceNonNil marks the ith lhs expression as nonnil due to limitations of NilAway. |
| 447 | produceNonNil := func(i int) { |
| 448 | if !asthelper.IsEmptyExpr(lhs[i]) { |
| 449 | rootNode.AddProduction(&annotation.ProduceTrigger{ |
| 450 | Annotation: &annotation.ProduceTriggerNever{}, |
| 451 | Expr: lhs[i], |
| 452 | }) |
| 453 | } |
| 454 | } |
| 455 | |
| 456 | rhsType := types.Unalias(rootNode.Pass().TypesInfo.Types[rhs].Type) |
| 457 | |
| 458 | // Go 1.23 introduced [range-over-func] language feature, where the `range` statement can |
| 459 | // now take the following types: |
| 460 | // |
| 461 | // 1. `func(func() bool)` |
| 462 | // 2. `func(func(K) bool)` |
| 463 | // 3. `func(func(K, V) bool)` |
| 464 | // |
| 465 | // We currently do not handle these types yet, so here we assume that they are deeply non-nil |
| 466 | // (by adding nonnil producers to both K and V if given). |
| 467 | // |
| 468 | // Note that the `iter` package provides `iter.Seq` and `iter.Seq2` generic types for 2 and 3 |
| 469 | // specifically. Therefore, we need to `.Underlying()` on the rhsType to find the underlying |
| 470 | // func type for simplicity. |
| 471 | // |
no test coverage detected