Refine paths that reference other paths and canonicalize the refinements. I.e. when a reference is passed to a function that then returns or leaks it back to the caller in the qualifier of a path then we want to dereference the qualifier in order to normalize the path and not have more than one path for the same location.
(&self, environment: &AbstractDomain<DomainType>)
| 544 | /// we want to dereference the qualifier in order to normalize the path |
| 545 | /// and not have more than one path for the same location. |
| 546 | fn refine_paths(&self, environment: &AbstractDomain<DomainType>) -> Rc<Path> { |
| 547 | if let Some(mut val) = environment.value_at(&self) { |
| 548 | // If the environment has self as a key, then self is canonical, since we should only |
| 549 | // use canonical paths as keys. The value at the canonical key, however, could just |
| 550 | // be a reference to another path, which is something that happens during refinement. |
| 551 | if let Expression::Cast { operand, .. } = &val.expression { |
| 552 | val = operand.clone(); |
| 553 | } |
| 554 | return match &val.expression { |
| 555 | Expression::HeapBlock { .. } => Path::get_as_path(val.refine_paths(environment)), |
| 556 | Expression::Variable { path, .. } | Expression::Widen { path, .. } => { |
| 557 | if let PathEnum::QualifiedPath { selector, .. } = &path.value { |
| 558 | if *selector.as_ref() == PathSelector::Deref { |
| 559 | // If the path is a deref, it is not just an alias for self, so keep self |
| 560 | return self.clone(); |
| 561 | } |
| 562 | } |
| 563 | path.clone() |
| 564 | } |
| 565 | _ => self.clone(), // self is canonical |
| 566 | }; |
| 567 | } |
| 568 | // self is a path that is not a key in the environment. This could be because it is not |
| 569 | // canonical, which can only be the case if self is a qualified path. |
| 570 | match &self.value { |
| 571 | // PathEnum::Offset { value } => Path::get_as_path(value.refine_paths(environment)), |
| 572 | PathEnum::QualifiedPath { |
| 573 | qualifier, |
| 574 | selector, |
| 575 | .. |
| 576 | } => { |
| 577 | let refined_selector = selector.refine_paths(environment); |
| 578 | let refined_qualifier = qualifier.refine_paths(environment); |
| 579 | |
| 580 | // The qualifier is now canonical. But in the context of a selector, we |
| 581 | // might be able to simplify the qualifier by dropping an explicit dereference |
| 582 | // or an explicit reference. |
| 583 | if let PathEnum::QualifiedPath { |
| 584 | qualifier: base_qualifier, |
| 585 | selector: base_selector, |
| 586 | .. |
| 587 | } = &refined_qualifier.value |
| 588 | { |
| 589 | if *base_selector.as_ref() == PathSelector::Deref { |
| 590 | // no need for an explicit deref in a qualifier |
| 591 | return Path::new_qualified( |
| 592 | base_qualifier.clone(), |
| 593 | refined_selector.clone(), |
| 594 | ); |
| 595 | } |
| 596 | } |
| 597 | if let Some(val) = environment.value_at(&refined_qualifier) { |
| 598 | match &val.expression { |
| 599 | Expression::Variable { path, .. } => { |
| 600 | // if path is a deref we just drop it because it becomes implicit |
| 601 | if let PathEnum::QualifiedPath { |
| 602 | qualifier, |
| 603 | selector: var_path_selector, |
no test coverage detected