• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use proc_macro2::Span;
2 use quote::{quote, ToTokens};
3 use serde::{Deserialize, Serialize};
4 use std::fmt;
5 
6 use super::{Attrs, Docs, Ident, Param, SelfParam, TraitSelfParam, TypeName};
7 
8 /// A named lifetime, e.g. `'a`.
9 ///
10 /// # Invariants
11 ///
12 /// Cannot be `'static` or `'_`, use [`Lifetime`] to represent those instead.
13 #[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, PartialOrd, Ord)]
14 #[serde(transparent)]
15 pub struct NamedLifetime(Ident);
16 
17 impl NamedLifetime {
name(&self) -> &Ident18     pub fn name(&self) -> &Ident {
19         &self.0
20     }
21 }
22 
23 impl<'de> Deserialize<'de> for NamedLifetime {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>,24     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
25     where
26         D: serde::Deserializer<'de>,
27     {
28         // Special `Deserialize` impl to ensure invariants.
29         let named = Ident::deserialize(deserializer)?;
30         if named.as_str() == "static" {
31             panic!("cannot be static");
32         }
33         Ok(NamedLifetime(named))
34     }
35 }
36 
37 impl From<&syn::Lifetime> for NamedLifetime {
from(lt: &syn::Lifetime) -> Self38     fn from(lt: &syn::Lifetime) -> Self {
39         Lifetime::from(lt).to_named().expect("cannot be static")
40     }
41 }
42 
43 impl From<&NamedLifetime> for NamedLifetime {
from(this: &NamedLifetime) -> Self44     fn from(this: &NamedLifetime) -> Self {
45         this.clone()
46     }
47 }
48 
49 impl PartialEq<syn::Lifetime> for NamedLifetime {
eq(&self, other: &syn::Lifetime) -> bool50     fn eq(&self, other: &syn::Lifetime) -> bool {
51         other.ident == self.0.as_str()
52     }
53 }
54 
55 impl fmt::Display for NamedLifetime {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result56     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57         write!(f, "'{}", self.0)
58     }
59 }
60 
61 impl ToTokens for NamedLifetime {
to_tokens(&self, tokens: &mut proc_macro2::TokenStream)62     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
63         use proc_macro2::{Punct, Spacing};
64         Punct::new('\'', Spacing::Joint).to_tokens(tokens);
65         self.0.to_tokens(tokens);
66     }
67 }
68 
69 /// A lifetime dependency graph used for tracking which lifetimes outlive,
70 /// and are outlived by, other lifetimes.
71 ///
72 /// It is similar to [`syn::LifetimeDef`], except it can also track lifetime
73 /// bounds defined in the `where` clause.
74 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
75 pub struct LifetimeEnv {
76     pub(crate) nodes: Vec<LifetimeNode>,
77 }
78 
79 impl LifetimeEnv {
80     /// Construct an empty [`LifetimeEnv`].
81     ///
82     /// To create one outside of this module, use `LifetimeEnv::from_method_item`
83     /// or `LifetimeEnv::from` on `&syn::Generics`.
new() -> Self84     fn new() -> Self {
85         Self { nodes: vec![] }
86     }
87 
88     /// Iterate through the names of the lifetimes in scope.
names(&self) -> impl Iterator<Item = &NamedLifetime> + Clone89     pub fn names(&self) -> impl Iterator<Item = &NamedLifetime> + Clone {
90         self.nodes.iter().map(|node| &node.lifetime)
91     }
92 
93     /// Returns a [`LifetimeEnv`] for a method, accounting for lifetimes and bounds
94     /// defined in both the impl block and the method, as well as implicit lifetime
95     /// bounds in the optional `self` param, other param, and optional return type.
96     /// For example, the type `&'a Foo<'b>` implies `'b: 'a`.
from_method_item( method: &syn::ImplItemFn, impl_generics: Option<&syn::Generics>, self_param: Option<&SelfParam>, params: &[Param], return_type: Option<&TypeName>, ) -> Self97     pub fn from_method_item(
98         method: &syn::ImplItemFn,
99         impl_generics: Option<&syn::Generics>,
100         self_param: Option<&SelfParam>,
101         params: &[Param],
102         return_type: Option<&TypeName>,
103     ) -> Self {
104         let mut this = LifetimeEnv::new();
105         // The impl generics _must_ be loaded into the env first, since the method
106         // generics might use lifetimes defined in the impl, and `extend_generics`
107         // panics if `'a: 'b` where `'b` isn't declared by the time it finishes.
108         if let Some(generics) = impl_generics {
109             this.extend_generics(generics);
110         }
111         this.extend_generics(&method.sig.generics);
112 
113         if let Some(self_param) = self_param {
114             this.extend_implicit_lifetime_bounds(&self_param.to_typename(), None);
115         }
116         for param in params {
117             this.extend_implicit_lifetime_bounds(&param.ty, None);
118         }
119         if let Some(return_type) = return_type {
120             this.extend_implicit_lifetime_bounds(return_type, None);
121         }
122 
123         this
124     }
125 
from_trait_item( trait_fct_item: &syn::TraitItem, self_param: Option<&TraitSelfParam>, params: &[Param], return_type: Option<&TypeName>, ) -> Self126     pub fn from_trait_item(
127         trait_fct_item: &syn::TraitItem,
128         self_param: Option<&TraitSelfParam>,
129         params: &[Param],
130         return_type: Option<&TypeName>,
131     ) -> Self {
132         let mut this = LifetimeEnv::new();
133         if let syn::TraitItem::Fn(_) = trait_fct_item {
134             if let Some(self_param) = self_param {
135                 this.extend_implicit_lifetime_bounds(&self_param.to_typename(), None);
136             }
137             for param in params {
138                 this.extend_implicit_lifetime_bounds(&param.ty, None);
139             }
140             if let Some(return_type) = return_type {
141                 this.extend_implicit_lifetime_bounds(return_type, None);
142             }
143         } else {
144             panic!(
145                 "Diplomat traits can only have associated methods and no other associated items."
146             )
147         }
148         this
149     }
150 
from_trait(trt: &syn::ItemTrait) -> Self151     pub fn from_trait(trt: &syn::ItemTrait) -> Self {
152         if trt.generics.lifetimes().next().is_some() {
153             panic!("Diplomat traits are not allowed to have any lifetime parameters")
154         }
155         LifetimeEnv::new()
156     }
157 
from_enum_item( enm: &syn::ItemEnum, variant_fields: &[(Option<Ident>, TypeName, Docs, Attrs)], ) -> Self158     pub fn from_enum_item(
159         enm: &syn::ItemEnum,
160         variant_fields: &[(Option<Ident>, TypeName, Docs, Attrs)],
161     ) -> Self {
162         let mut this = LifetimeEnv::new();
163         this.extend_generics(&enm.generics);
164         for (_, typ, _, _) in variant_fields {
165             this.extend_implicit_lifetime_bounds(typ, None);
166         }
167         this
168     }
169 
170     /// Returns a [`LifetimeEnv`] for a struct, accounding for lifetimes and bounds
171     /// defined in the struct generics, as well as implicit lifetime bounds in
172     /// the struct's fields. For example, the field `&'a Foo<'b>` implies `'b: 'a`.
from_struct_item( strct: &syn::ItemStruct, fields: &[(Ident, TypeName, Docs, Attrs)], ) -> Self173     pub fn from_struct_item(
174         strct: &syn::ItemStruct,
175         fields: &[(Ident, TypeName, Docs, Attrs)],
176     ) -> Self {
177         let mut this = LifetimeEnv::new();
178         this.extend_generics(&strct.generics);
179         for (_, typ, _, _) in fields {
180             this.extend_implicit_lifetime_bounds(typ, None);
181         }
182         this
183     }
184 
185     /// Traverse a type, adding any implicit lifetime bounds that arise from
186     /// having a reference to an opaque containing a lifetime.
187     /// For example, the type `&'a Foo<'b>` implies `'b: 'a`.
extend_implicit_lifetime_bounds( &mut self, typ: &TypeName, behind_ref: Option<&NamedLifetime>, )188     fn extend_implicit_lifetime_bounds(
189         &mut self,
190         typ: &TypeName,
191         behind_ref: Option<&NamedLifetime>,
192     ) {
193         match typ {
194             TypeName::Named(path_type) => {
195                 if let Some(borrow_lifetime) = behind_ref {
196                     let explicit_longer_than_borrow =
197                         LifetimeTransitivity::longer_than(self, borrow_lifetime);
198                     let mut implicit_longer_than_borrow = vec![];
199 
200                     for path_lifetime in path_type.lifetimes.iter() {
201                         if let Lifetime::Named(path_lifetime) = path_lifetime {
202                             if !explicit_longer_than_borrow.contains(&path_lifetime) {
203                                 implicit_longer_than_borrow.push(path_lifetime);
204                             }
205                         }
206                     }
207 
208                     self.extend_bounds(
209                         implicit_longer_than_borrow
210                             .into_iter()
211                             .map(|path_lifetime| (path_lifetime, Some(borrow_lifetime))),
212                     );
213                 }
214             }
215             TypeName::Reference(lifetime, _, typ) => {
216                 let behind_ref = if let Lifetime::Named(named) = lifetime {
217                     Some(named)
218                 } else {
219                     None
220                 };
221                 self.extend_implicit_lifetime_bounds(typ, behind_ref);
222             }
223             TypeName::Option(typ, _) => self.extend_implicit_lifetime_bounds(typ, None),
224             TypeName::Result(ok, err, _) => {
225                 self.extend_implicit_lifetime_bounds(ok, None);
226                 self.extend_implicit_lifetime_bounds(err, None);
227             }
228             _ => {}
229         }
230     }
231 
232     /// Add the lifetimes from generic parameters and where bounds.
extend_generics(&mut self, generics: &syn::Generics)233     fn extend_generics(&mut self, generics: &syn::Generics) {
234         let generic_bounds = generics.params.iter().map(|generic| match generic {
235             syn::GenericParam::Type(_) => panic!("generic types are unsupported"),
236             syn::GenericParam::Lifetime(def) => (&def.lifetime, &def.bounds),
237             syn::GenericParam::Const(_) => panic!("const generics are unsupported"),
238         });
239 
240         let generic_defs = generic_bounds.clone().map(|(lifetime, _)| lifetime);
241 
242         self.extend_lifetimes(generic_defs);
243         self.extend_bounds(generic_bounds);
244 
245         if let Some(ref where_clause) = generics.where_clause {
246             self.extend_bounds(where_clause.predicates.iter().map(|pred| match pred {
247                 syn::WherePredicate::Type(_) => panic!("trait bounds are unsupported"),
248                 syn::WherePredicate::Lifetime(pred) => (&pred.lifetime, &pred.bounds),
249                 _ => panic!("Found unknown kind of where predicate"),
250             }));
251         }
252     }
253 
254     /// Returns the number of lifetimes in the graph.
len(&self) -> usize255     pub fn len(&self) -> usize {
256         self.nodes.len()
257     }
258 
259     /// Returns `true` if the graph contains no lifetimes.
is_empty(&self) -> bool260     pub fn is_empty(&self) -> bool {
261         self.nodes.is_empty()
262     }
263 
264     /// `<'a, 'b, 'c>`
265     ///
266     /// Write the existing lifetimes, excluding bounds, as generic parameters.
267     ///
268     /// To include lifetime bounds, use [`LifetimeEnv::lifetime_defs_to_tokens`].
lifetimes_to_tokens(&self) -> proc_macro2::TokenStream269     pub fn lifetimes_to_tokens(&self) -> proc_macro2::TokenStream {
270         if self.is_empty() {
271             return quote! {};
272         }
273 
274         let lifetimes = self.nodes.iter().map(|node| &node.lifetime);
275         quote! { <#(#lifetimes),*> }
276     }
277 
278     /// Returns the index of a lifetime in the graph, or `None` if the lifetime
279     /// isn't in the graph.
id<L>(&self, lifetime: &L) -> Option<usize> where NamedLifetime: PartialEq<L>,280     pub(crate) fn id<L>(&self, lifetime: &L) -> Option<usize>
281     where
282         NamedLifetime: PartialEq<L>,
283     {
284         self.nodes
285             .iter()
286             .position(|node| &node.lifetime == lifetime)
287     }
288 
289     /// Add isolated lifetimes to the graph.
extend_lifetimes<'a, L, I>(&mut self, iter: I) where NamedLifetime: PartialEq<L> + From<&'a L>, L: 'a, I: IntoIterator<Item = &'a L>,290     fn extend_lifetimes<'a, L, I>(&mut self, iter: I)
291     where
292         NamedLifetime: PartialEq<L> + From<&'a L>,
293         L: 'a,
294         I: IntoIterator<Item = &'a L>,
295     {
296         for lifetime in iter {
297             if self.id(lifetime).is_some() {
298                 panic!(
299                     "lifetime name `{}` declared twice in the same scope",
300                     NamedLifetime::from(lifetime)
301                 );
302             }
303 
304             self.nodes.push(LifetimeNode {
305                 lifetime: lifetime.into(),
306                 shorter: vec![],
307                 longer: vec![],
308             });
309         }
310     }
311 
312     /// Add edges to the lifetime graph.
313     ///
314     /// This method is decoupled from [`LifetimeEnv::extend_lifetimes`] because
315     /// generics can define new lifetimes, while `where` clauses cannot.
316     ///
317     /// # Panics
318     ///
319     /// This method panics if any of the lifetime bounds aren't already defined
320     /// in the graph. This isn't allowed by rustc in the first place, so it should
321     /// only ever occur when deserializing an invalid [`LifetimeEnv`].
extend_bounds<'a, L, B, I>(&mut self, iter: I) where NamedLifetime: PartialEq<L> + From<&'a L>, L: 'a, B: IntoIterator<Item = &'a L>, I: IntoIterator<Item = (&'a L, B)>,322     fn extend_bounds<'a, L, B, I>(&mut self, iter: I)
323     where
324         NamedLifetime: PartialEq<L> + From<&'a L>,
325         L: 'a,
326         B: IntoIterator<Item = &'a L>,
327         I: IntoIterator<Item = (&'a L, B)>,
328     {
329         for (lifetime, bounds) in iter {
330             let long = self.id(lifetime).expect("use of undeclared lifetime, this is a bug: try calling `LifetimeEnv::extend_lifetimes` first");
331             for bound in bounds {
332                 let short = self
333                     .id(bound)
334                     .expect("cannot use undeclared lifetime as a bound");
335                 self.nodes[short].longer.push(long);
336                 self.nodes[long].shorter.push(short);
337             }
338         }
339     }
340 }
341 
342 impl fmt::Display for LifetimeEnv {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result343     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
344         self.to_token_stream().fmt(f)
345     }
346 }
347 
348 impl ToTokens for LifetimeEnv {
to_tokens(&self, tokens: &mut proc_macro2::TokenStream)349     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
350         for node in self.nodes.iter() {
351             let lifetime = &node.lifetime;
352             if node.shorter.is_empty() {
353                 tokens.extend(quote! { #lifetime, });
354             } else {
355                 let bounds = node.shorter.iter().map(|&id| &self.nodes[id].lifetime);
356                 tokens.extend(quote! { #lifetime: #(#bounds)+*, });
357             }
358         }
359     }
360 }
361 
362 /// Serialize a [`LifetimeEnv`] as a map from lifetimes to their bounds.
363 impl Serialize for LifetimeEnv {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer,364     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
365     where
366         S: serde::Serializer,
367     {
368         use serde::ser::SerializeMap;
369         let mut seq = serializer.serialize_map(Some(self.len()))?;
370 
371         for node in self.nodes.iter() {
372             /// Helper type for serializing bounds.
373             struct Bounds<'a> {
374                 ids: &'a [usize],
375                 nodes: &'a [LifetimeNode],
376             }
377 
378             impl<'a> Serialize for Bounds<'a> {
379                 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
380                 where
381                     S: serde::Serializer,
382                 {
383                     use serde::ser::SerializeSeq;
384                     let mut seq = serializer.serialize_seq(Some(self.ids.len()))?;
385                     for &id in self.ids {
386                         seq.serialize_element(&self.nodes[id].lifetime)?;
387                     }
388                     seq.end()
389                 }
390             }
391 
392             seq.serialize_entry(
393                 &node.lifetime,
394                 &Bounds {
395                     ids: &node.shorter[..],
396                     nodes: &self.nodes,
397                 },
398             )?;
399         }
400         seq.end()
401     }
402 }
403 
404 impl<'de> Deserialize<'de> for LifetimeEnv {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>,405     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
406     where
407         D: serde::Deserializer<'de>,
408     {
409         use std::collections::BTreeMap;
410 
411         let m: BTreeMap<NamedLifetime, Vec<NamedLifetime>> =
412             Deserialize::deserialize(deserializer)?;
413 
414         let mut this = LifetimeEnv::new();
415         this.extend_lifetimes(m.keys());
416         this.extend_bounds(m.iter());
417         Ok(this)
418     }
419 }
420 
421 /// A lifetime, along with ptrs to all lifetimes that are explicitly
422 /// shorter/longer than it.
423 ///
424 /// This type is internal to [`LifetimeGraph`]- the ptrs are stored as `usize`s,
425 /// meaning that they may be invalid if a `LifetimeEdges` is created in one
426 /// `LifetimeGraph` and then used in another.
427 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
428 pub(crate) struct LifetimeNode {
429     /// The name of the lifetime.
430     pub(crate) lifetime: NamedLifetime,
431 
432     /// Pointers to all lifetimes that this lives _at least_ as long as.
433     ///
434     /// Note: This doesn't account for transitivity.
435     pub(crate) shorter: Vec<usize>,
436 
437     /// Pointers to all lifetimes that live _at least_ as long as this.
438     ///
439     /// Note: This doesn't account for transitivity.
440     pub(crate) longer: Vec<usize>,
441 }
442 
443 /// A lifetime, analogous to [`syn::Lifetime`].
444 #[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
445 #[non_exhaustive]
446 pub enum Lifetime {
447     /// The `'static` lifetime.
448     Static,
449 
450     /// A named lifetime, like `'a`.
451     Named(NamedLifetime),
452 
453     /// An elided lifetime.
454     Anonymous,
455 }
456 
457 impl Lifetime {
458     /// Returns the inner `NamedLifetime` if the lifetime is the `Named` variant,
459     /// otherwise `None`.
to_named(self) -> Option<NamedLifetime>460     pub fn to_named(self) -> Option<NamedLifetime> {
461         if let Lifetime::Named(named) = self {
462             return Some(named);
463         }
464         None
465     }
466 
467     /// Returns a reference to the inner `NamedLifetime` if the lifetime is the
468     /// `Named` variant, otherwise `None`.
as_named(&self) -> Option<&NamedLifetime>469     pub fn as_named(&self) -> Option<&NamedLifetime> {
470         if let Lifetime::Named(named) = self {
471             return Some(named);
472         }
473         None
474     }
475 }
476 
477 impl fmt::Display for Lifetime {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result478     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
479         match self {
480             Lifetime::Static => "'static".fmt(f),
481             Lifetime::Named(ref named) => named.fmt(f),
482             Lifetime::Anonymous => "'_".fmt(f),
483         }
484     }
485 }
486 
487 impl ToTokens for Lifetime {
to_tokens(&self, tokens: &mut proc_macro2::TokenStream)488     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
489         match self {
490             Lifetime::Static => syn::Lifetime::new("'static", Span::call_site()).to_tokens(tokens),
491             Lifetime::Named(ref s) => s.to_tokens(tokens),
492             Lifetime::Anonymous => syn::Lifetime::new("'_", Span::call_site()).to_tokens(tokens),
493         };
494     }
495 }
496 
497 impl From<&syn::Lifetime> for Lifetime {
from(lt: &syn::Lifetime) -> Self498     fn from(lt: &syn::Lifetime) -> Self {
499         if lt.ident == "static" {
500             Self::Static
501         } else {
502             Self::Named(NamedLifetime((&lt.ident).into()))
503         }
504     }
505 }
506 
507 impl From<&Option<syn::Lifetime>> for Lifetime {
from(lt: &Option<syn::Lifetime>) -> Self508     fn from(lt: &Option<syn::Lifetime>) -> Self {
509         lt.as_ref().map(Into::into).unwrap_or(Self::Anonymous)
510     }
511 }
512 
513 impl Lifetime {
514     /// Converts the [`Lifetime`] back into an AST node that can be spliced into a program.
to_syn(&self) -> Option<syn::Lifetime>515     pub fn to_syn(&self) -> Option<syn::Lifetime> {
516         match *self {
517             Self::Static => Some(syn::Lifetime::new("'static", Span::call_site())),
518             Self::Anonymous => None,
519             Self::Named(ref s) => Some(syn::Lifetime::new(&s.to_string(), Span::call_site())),
520         }
521     }
522 }
523 
524 /// Collect all lifetimes that are either longer_or_shorter
525 pub struct LifetimeTransitivity<'env> {
526     env: &'env LifetimeEnv,
527     visited: Vec<bool>,
528     out: Vec<&'env NamedLifetime>,
529     longer_or_shorter: LongerOrShorter,
530 }
531 
532 impl<'env> LifetimeTransitivity<'env> {
533     /// Returns a new [`LifetimeTransitivity`] that finds all longer lifetimes.
longer(env: &'env LifetimeEnv) -> Self534     pub fn longer(env: &'env LifetimeEnv) -> Self {
535         Self::new(env, LongerOrShorter::Longer)
536     }
537 
538     /// Returns a new [`LifetimeTransitivity`] that finds all shorter lifetimes.
shorter(env: &'env LifetimeEnv) -> Self539     pub fn shorter(env: &'env LifetimeEnv) -> Self {
540         Self::new(env, LongerOrShorter::Shorter)
541     }
542 
543     /// Returns all the lifetimes longer than a provided `NamedLifetime`.
longer_than(env: &'env LifetimeEnv, named: &NamedLifetime) -> Vec<&'env NamedLifetime>544     pub fn longer_than(env: &'env LifetimeEnv, named: &NamedLifetime) -> Vec<&'env NamedLifetime> {
545         let mut this = Self::new(env, LongerOrShorter::Longer);
546         this.visit(named);
547         this.finish()
548     }
549 
550     /// Returns all the lifetimes shorter than the provided `NamedLifetime`.
shorter_than(env: &'env LifetimeEnv, named: &NamedLifetime) -> Vec<&'env NamedLifetime>551     pub fn shorter_than(env: &'env LifetimeEnv, named: &NamedLifetime) -> Vec<&'env NamedLifetime> {
552         let mut this = Self::new(env, LongerOrShorter::Shorter);
553         this.visit(named);
554         this.finish()
555     }
556 
557     /// Returns a new [`LifetimeTransitivity`].
new(env: &'env LifetimeEnv, longer_or_shorter: LongerOrShorter) -> Self558     fn new(env: &'env LifetimeEnv, longer_or_shorter: LongerOrShorter) -> Self {
559         LifetimeTransitivity {
560             env,
561             visited: vec![false; env.len()],
562             out: vec![],
563             longer_or_shorter,
564         }
565     }
566 
567     /// Visits a lifetime, as well as all the nodes it's transitively longer or
568     /// shorter than, depending on how the `LifetimeTransitivity` was constructed.
visit(&mut self, named: &NamedLifetime)569     pub fn visit(&mut self, named: &NamedLifetime) {
570         if let Some(id) = self
571             .env
572             .nodes
573             .iter()
574             .position(|node| node.lifetime == *named)
575         {
576             self.dfs(id);
577         }
578     }
579 
580     /// Performs depth-first search through the `LifetimeEnv` created at construction
581     /// for all nodes longer or shorter than the node at the provided index,
582     /// depending on how the `LifetimeTransitivity` was constructed.
dfs(&mut self, index: usize)583     fn dfs(&mut self, index: usize) {
584         // Note: all of these indexings SHOULD be valid because
585         // `visited.len() == nodes.len()`, and the ids come from
586         // calling `Iterator::position` on `nodes`, which never shrinks.
587         // So we should be able to change these to `get_unchecked`...
588         if !self.visited[index] {
589             self.visited[index] = true;
590 
591             let node = &self.env.nodes[index];
592             self.out.push(&node.lifetime);
593             for &edge_index in self.longer_or_shorter.edges(node).iter() {
594                 self.dfs(edge_index);
595             }
596         }
597     }
598 
599     /// Returns the transitively reachable lifetimes.
finish(self) -> Vec<&'env NamedLifetime>600     pub fn finish(self) -> Vec<&'env NamedLifetime> {
601         self.out
602     }
603 }
604 
605 /// A helper type for [`LifetimeTransitivity`] determining whether to find the
606 /// transitively longer or transitively shorter lifetimes.
607 enum LongerOrShorter {
608     Longer,
609     Shorter,
610 }
611 
612 impl LongerOrShorter {
613     /// Returns either the indices of the longer or shorter lifetimes, depending
614     /// on `self`.
edges<'node>(&self, node: &'node LifetimeNode) -> &'node [usize]615     fn edges<'node>(&self, node: &'node LifetimeNode) -> &'node [usize] {
616         match self {
617             LongerOrShorter::Longer => &node.longer[..],
618             LongerOrShorter::Shorter => &node.shorter[..],
619         }
620     }
621 }
622