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(¶m.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(¶m.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((<.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