backpropAcrossTypeSwitch handles type switches (e.g., "switch v := a.(*type)"), it is designed to be called from backpropAcrossAssignment as a finer-grained handler for special assignment cases. The main reason that this case has to be handled separately is that it introduces a "symbolic" variable t
(rootNode *RootAssertionNode, lhs *ast.Ident, rhs ast.Expr)
| 534 | // without being able to compare the identity of `types.Var` instances as we usually do. |
| 535 | // nonnil(lhs, rhs) |
| 536 | func backpropAcrossTypeSwitch(rootNode *RootAssertionNode, lhs *ast.Ident, rhs ast.Expr) error { |
| 537 | // First, make a copy of the children array to iterate over, as we will mutate it. |
| 538 | children := slices.Clone(rootNode.Children()) |
| 539 | |
| 540 | // For each variable in the assertion tree, check if it's equal to the symbolic variable |
| 541 | // being instantiated by this type switch, and, if so, assign to it. |
| 542 | // we (annonyingly) have to handle this as a separate case and continue after the first child |
| 543 | // is found because there is no canonical instance of types.Object for symbolic type switch |
| 544 | // variables |
| 545 | for _, child := range children { |
| 546 | if varChild, ok := child.(*varAssertionNode); ok { |
| 547 | if varChild.decl.Pos() == lhs.Pos() && |
| 548 | varChild.decl.Name() == lhs.Name { |
| 549 | // even though we can't do the matching through a types.Object instance, we conclude |
| 550 | // that the *types.Var in the assertion nodes matches the lhs of this assignment |
| 551 | |
| 552 | liftedChild, _ := rootNode.LiftFromPath([]AssertionNode{varChild}) |
| 553 | if liftedChild == nil { |
| 554 | // this nil check reflects programmer logic |
| 555 | return errors.New("liftedChild variable is nil") |
| 556 | } |
| 557 | rhsPath, rhsProducers := rootNode.ParseExprAsProducer(rhs, false) |
| 558 | if rhsPath != nil { |
| 559 | // rhs is trackable, so move assertions as we would in the vanilla assignment case |
| 560 | rootNode.LandAtPath(rhsPath, liftedChild) |
| 561 | // assignment complete |
| 562 | } else { |
| 563 | liftedChild.SetParent(rootNode) |
| 564 | switch len(rhsProducers) { |
| 565 | case 0: |
| 566 | // lhsVal expression will never be nil here because rhsVal will never be nil |
| 567 | rootNode.triggerProductions(liftedChild, &annotation.ProduceTrigger{ |
| 568 | Annotation: &annotation.ProduceTriggerNever{}, |
| 569 | Expr: lhs, |
| 570 | }) |
| 571 | case 1: |
| 572 | rootNode.triggerProductions(liftedChild, &annotation.ProduceTrigger{ |
| 573 | Annotation: rhsProducers[0].GetShallow().Annotation, |
| 574 | Expr: lhs, |
| 575 | }, rhsProducers[0].GetDeepSlice()...) |
| 576 | default: |
| 577 | return errors.New("expression e in a e.(type) switch was multiply returning - " + |
| 578 | "this should be a type error") |
| 579 | } |
| 580 | } |
| 581 | } |
| 582 | } |
| 583 | } |
| 584 | // no current assertion matches the type switch lhs variable here, so it's a no-op |
| 585 | return nil |
| 586 | } |
| 587 | |
| 588 | // backpropAcrossOneToOneAssignment handles normal one-to-one assignment (e.g, "var a *int = b", or |
| 589 | // "var a, b, c *int = d, e, f"), it is designed to be called from backpropAcrossAssignment as a |
no test coverage detected