- /// Returns the ID of the innermost containing body.
- pub fn containing_body(&self, mut scope: Scope) -> Option<hir::ItemLocalId> {
- loop {
- if let ScopeData::CallSite = scope.data {
- return Some(scope.item_local_id());
- }
-
- scope = self.opt_encl_scope(scope)?;
- }
- }
-
- /// Finds the nearest common ancestor of two scopes. That is, finds the
- /// smallest scope which is greater than or equal to both `scope_a` and
- /// `scope_b`.
- pub fn nearest_common_ancestor(&self, scope_a: Scope, scope_b: Scope) -> Scope {
- if scope_a == scope_b {
- return scope_a;
- }
-
- let mut a = scope_a;
- let mut b = scope_b;
-
- // Get the depth of each scope's parent. If either scope has no parent,
- // it must be the root, which means we can stop immediately because the
- // root must be the nearest common ancestor. (In practice, this is
- // moderately common.)
- let (parent_a, parent_a_depth) = match self.parent_map.get(&a) {
- Some(pd) => *pd,
- None => return a,
- };
- let (parent_b, parent_b_depth) = match self.parent_map.get(&b) {
- Some(pd) => *pd,
- None => return b,
- };
-
- if parent_a_depth > parent_b_depth {
- // `a` is lower than `b`. Move `a` up until it's at the same depth
- // as `b`. The first move up is trivial because we already found
- // `parent_a` above; the loop does the remaining N-1 moves.
- a = parent_a;
- for _ in 0..(parent_a_depth - parent_b_depth - 1) {
- a = self.parent_map.get(&a).unwrap().0;
- }
- } else if parent_b_depth > parent_a_depth {
- // `b` is lower than `a`.
- b = parent_b;
- for _ in 0..(parent_b_depth - parent_a_depth - 1) {
- b = self.parent_map.get(&b).unwrap().0;
- }
- } else {
- // Both scopes are at the same depth, and we know they're not equal
- // because that case was tested for at the top of this function. So
- // we can trivially move them both up one level now.
- assert!(parent_a_depth != 0);
- a = parent_a;
- b = parent_b;
- }
-
- // Now both scopes are at the same level. We move upwards in lockstep
- // until they match. In practice, this loop is almost always executed
- // zero times because `a` is almost always a direct ancestor of `b` or
- // vice versa.
- while a != b {
- a = self.parent_map.get(&a).unwrap().0;
- b = self.parent_map.get(&b).unwrap().0;
- }
-
- a
- }
-
- /// Assuming that the provided region was defined within this `ScopeTree`,
- /// returns the outermost `Scope` that the region outlives.
- pub fn early_free_scope(&self, tcx: TyCtxt<'tcx>, br: &ty::EarlyBoundRegion) -> Scope {
- let param_owner = tcx.parent(br.def_id).unwrap();
-
- let param_owner_id = tcx.hir().as_local_hir_id(param_owner.expect_local());
- let scope = tcx
- .hir()
- .maybe_body_owned_by(param_owner_id)
- .map(|body_id| tcx.hir().body(body_id).value.hir_id.local_id)
- .unwrap_or_else(|| {
- // The lifetime was defined on node that doesn't own a body,
- // which in practice can only mean a trait or an impl, that
- // is the parent of a method, and that is enforced below.
- if Some(param_owner_id) != self.root_parent {
- tcx.sess.delay_span_bug(
- DUMMY_SP,
- &format!(
- "free_scope: {:?} not recognized by the \
- region scope tree for {:?} / {:?}",
- param_owner,
- self.root_parent.map(|id| tcx.hir().local_def_id(id)),
- self.root_body.map(|hir_id| hir_id.owner)
- ),
- );
- }
-
- // The trait/impl lifetime is in scope for the method's body.
- self.root_body.unwrap().local_id
- });
-
- Scope { id: scope, data: ScopeData::CallSite }
- }
-
- /// Assuming that the provided region was defined within this `ScopeTree`,
- /// returns the outermost `Scope` that the region outlives.
- pub fn free_scope(&self, tcx: TyCtxt<'tcx>, fr: &ty::FreeRegion) -> Scope {
- let param_owner = match fr.bound_region {
- ty::BoundRegion::BrNamed(def_id, _) => tcx.parent(def_id).unwrap(),
- _ => fr.scope,
- };
-
- // Ensure that the named late-bound lifetimes were defined
- // on the same function that they ended up being freed in.
- assert_eq!(param_owner, fr.scope);
-
- let param_owner_id = tcx.hir().as_local_hir_id(param_owner.expect_local());
- let body_id = tcx.hir().body_owned_by(param_owner_id);
- Scope { id: tcx.hir().body(body_id).value.hir_id.local_id, data: ScopeData::CallSite }
- }
-