/// A named lifetime, e.g. `'a`. /// /// # Invariants /// /// Cannot be `'static` or `'_`, use [`Lifetime`] to represent those instead. #[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, PartialOrd, Ord)] #[serde(transparent)] pubstruct NamedLifetime(Ident);
impl<'de> Deserialize<'de> for NamedLifetime { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where
D: serde::Deserializer<'de>,
{ // Special `Deserialize` impl to ensure invariants. let named = Ident::deserialize(deserializer)?; if named.as_str() == "static" {
panic!("cannot be static");
}
Ok(NamedLifetime(named))
}
}
impl From<&syn::Lifetime> for NamedLifetime { fn from(lt: &syn::Lifetime) -> Self {
Lifetime::from(lt).to_named().expect("cannot be static")
}
}
impl ToTokens for NamedLifetime { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { use proc_macro2::{Punct, Spacing};
Punct::new('\'', Spacing::Joint).to_tokens(tokens); self.0.to_tokens(tokens);
}
}
/// A lifetime dependency graph used for tracking which lifetimes outlive, /// and are outlived by, other lifetimes. /// /// It is similar to [`syn::LifetimeDef`], except it can also track lifetime /// bounds defined in the `where` clause. #[derive(Clone, PartialEq, Eq, Hash, Debug)] pubstruct LifetimeEnv { pub(crate) nodes: Vec<LifetimeNode>,
}
impl LifetimeEnv { /// Construct an empty [`LifetimeEnv`]. /// /// To create one outside of this module, use `LifetimeEnv::from_method_item` /// or `LifetimeEnv::from` on `&syn::Generics`. fn new() -> Self { Self { nodes: vec![] }
}
/// Iterate through the names of the lifetimes in scope. pubfn names(&self) -> impl Iterator<Item = &NamedLifetime> + Clone { self.nodes.iter().map(|node| &node.lifetime)
}
/// Returns a [`LifetimeEnv`] for a method, accounting for lifetimes and bounds /// defined in both the impl block and the method, as well as implicit lifetime /// bounds in the optional `self` param, other param, and optional return type. /// For example, the type `&'a Foo<'b>` implies `'b: 'a`. pubfn from_method_item(
method: &syn::ImplItemFn,
impl_generics: Option<&syn::Generics>,
self_param: Option<&SelfParam>,
params: &[Param],
return_type: Option<&TypeName>,
) -> Self { letmut this = LifetimeEnv::new(); // The impl generics _must_ be loaded into the env first, since the method // generics might use lifetimes defined in the impl, and `extend_generics` // panics if `'a: 'b` where `'b` isn't declared by the time it finishes. iflet Some(generics) = impl_generics {
this.extend_generics(generics);
}
this.extend_generics(&method.sig.generics);
/// Returns a [`LifetimeEnv`] for a struct, accounding for lifetimes and bounds /// defined in the struct generics, as well as implicit lifetime bounds in /// the struct's fields. For example, the field `&'a Foo<'b>` implies `'b: 'a`. pubfn from_struct_item(strct: &syn::ItemStruct, fields: &[(Ident, TypeName, Docs)]) -> Self { letmut this = LifetimeEnv::new();
this.extend_generics(&strct.generics); for (_, typ, _) in fields {
this.extend_implicit_lifetime_bounds(typ, None);
}
this
}
/// Traverse a type, adding any implicit lifetime bounds that arise from /// having a reference to an opaque containing a lifetime. /// For example, the type `&'a Foo<'b>` implies `'b: 'a`. fn extend_implicit_lifetime_bounds(
&mutself,
typ: &TypeName,
behind_ref: Option<&NamedLifetime>,
) { match typ {
TypeName::Named(path_type) => { iflet Some(borrow_lifetime) = behind_ref { let explicit_longer_than_borrow =
LifetimeTransitivity::longer_than(self, borrow_lifetime); letmut implicit_longer_than_borrow = vec![];
for path_lifetime in path_type.lifetimes.iter() { iflet Lifetime::Named(path_lifetime) = path_lifetime { if !explicit_longer_than_borrow.contains(&path_lifetime) {
implicit_longer_than_borrow.push(path_lifetime);
}
}
}
/// Add the lifetimes from generic parameters and where bounds. fn extend_generics(&mutself, generics: &syn::Generics) { let generic_bounds = generics.params.iter().map(|generic| match generic {
syn::GenericParam::Type(_) => panic!("generic types are unsupported"),
syn::GenericParam::Lifetime(def) => (&def.lifetime, &def.bounds),
syn::GenericParam::Const(_) => panic!("const generics are unsupported"),
});
let generic_defs = generic_bounds.clone().map(|(lifetime, _)| lifetime);
iflet Some(ref where_clause) = generics.where_clause { self.extend_bounds(where_clause.predicates.iter().map(|pred| match pred {
syn::WherePredicate::Type(_) => panic!("trait bounds are unsupported"),
syn::WherePredicate::Lifetime(pred) => (&pred.lifetime, &pred.bounds),
_ => panic!("Found unknown kind of where predicate"),
}));
}
}
/// Returns the number of lifetimes in the graph. pubfn len(&self) -> usize { self.nodes.len()
}
/// Returns `true` if the graph contains no lifetimes. pubfn is_empty(&self) -> bool { self.nodes.is_empty()
}
/// `<'a, 'b, 'c>` /// /// Write the existing lifetimes, excluding bounds, as generic parameters. /// /// To include lifetime bounds, use [`LifetimeEnv::lifetime_defs_to_tokens`]. pubfn lifetimes_to_tokens(&self) -> proc_macro2::TokenStream { ifself.is_empty() { return quote! {};
}
let lifetimes = self.nodes.iter().map(|node| &node.lifetime);
quote! { <#(#lifetimes),*> }
}
/// Returns the index of a lifetime in the graph, or `None` if the lifetime /// isn't in the graph. pub(crate) fn id<L>(&self, lifetime: &L) -> Option<usize> where
NamedLifetime: PartialEq<L>,
{ self.nodes
.iter()
.position(|node| &node.lifetime == lifetime)
}
/// Add isolated lifetimes to the graph. fn extend_lifetimes<'a, L, I>(&mut self, iter: I) where
NamedLifetime: PartialEq<L> + From<&'a L>,
L: 'a,
I: IntoIterator<Item = &'a L>,
{ for lifetime in iter { ifself.id(lifetime).is_some() {
panic!( "lifetime name `{}` declared twice in the same scope",
NamedLifetime::from(lifetime)
);
}
/// Add edges to the lifetime graph. /// /// This method is decoupled from [`LifetimeEnv::extend_lifetimes`] because /// generics can define new lifetimes, while `where` clauses cannot. /// /// # Panics /// /// This method panics if any of the lifetime bounds aren't already defined /// in the graph. This isn't allowed by rustc in the first place, so it should /// only ever occur when deserializing an invalid [`LifetimeEnv`]. fn 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)>,
{ for (lifetime, bounds) in iter { let long = self.id(lifetime).expect("use of undeclared lifetime, this is a bug: try calling `LifetimeEnv::extend_lifetimes` first"); for bound in bounds { let short = self
.id(bound)
.expect("cannot use undeclared lifetime as a bound"); self.nodes[short].longer.push(long); self.nodes[long].shorter.push(short);
}
}
}
}
impl ToTokens for LifetimeEnv { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { for node inself.nodes.iter() { let lifetime = &node.lifetime; if node.shorter.is_empty() {
tokens.extend(quote! { #lifetime, });
} else { let bounds = node.shorter.iter().map(|&id| &self.nodes[id].lifetime);
tokens.extend(quote! { #lifetime: #(#bounds)+*, });
}
}
}
}
/// Serialize a [`LifetimeEnv`] as a map from lifetimes to their bounds. impl Serialize for LifetimeEnv { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
S: serde::Serializer,
{ use serde::ser::SerializeMap; letmut seq = serializer.serialize_map(Some(self.len()))?;
for node inself.nodes.iter() { /// Helper type for serializing bounds. struct Bounds<'a> {
ids: &'a [usize],
nodes: &'a [LifetimeNode],
}
impl<'a> Serialize for Bounds<'a> { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
S: serde::Serializer,
{ use serde::ser::SerializeSeq; letmut seq = serializer.serialize_seq(Some(self.ids.len()))?; for &id inself.ids {
seq.serialize_element(&self.nodes[id].lifetime)?;
}
seq.end()
}
}
impl<'de> Deserialize<'de> for LifetimeEnv { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where
D: serde::Deserializer<'de>,
{ use std::collections::BTreeMap;
let m: BTreeMap<NamedLifetime, Vec<NamedLifetime>> =
Deserialize::deserialize(deserializer)?;
letmut this = LifetimeEnv::new();
this.extend_lifetimes(m.keys());
this.extend_bounds(m.iter());
Ok(this)
}
}
/// A lifetime, along with ptrs to all lifetimes that are explicitly /// shorter/longer than it. /// /// This type is internal to [`LifetimeGraph`]- the ptrs are stored as `usize`s, /// meaning that they may be invalid if a `LifetimeEdges` is created in one /// `LifetimeGraph` and then used in another. #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct LifetimeNode { /// The name of the lifetime. pub(crate) lifetime: NamedLifetime,
/// Pointers to all lifetimes that this lives _at least_ as long as. /// /// Note: This doesn't account for transitivity. pub(crate) shorter: Vec<usize>,
/// Pointers to all lifetimes that live _at least_ as long as this. /// /// Note: This doesn't account for transitivity. pub(crate) longer: Vec<usize>,
}
/// A lifetime, analogous to [`syn::Lifetime`]. #[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pubenum Lifetime { /// The `'static` lifetime. Static,
/// A named lifetime, like `'a`.
Named(NamedLifetime),
/// An elided lifetime.
Anonymous,
}
impl Lifetime { /// Returns the inner `NamedLifetime` if the lifetime is the `Named` variant, /// otherwise `None`. pubfn to_named(self) -> Option<NamedLifetime> { iflet Lifetime::Named(named) = self { return Some(named);
}
None
}
/// Returns a reference to the inner `NamedLifetime` if the lifetime is the /// `Named` variant, otherwise `None`. pubfn as_named(&self) -> Option<&NamedLifetime> { iflet Lifetime::Named(named) = self { return Some(named);
}
None
}
}
impl Lifetime { /// Converts the [`Lifetime`] back into an AST node that can be spliced into a program. pubfn to_syn(&self) -> Option<syn::Lifetime> { match *self { Self::Static => Some(syn::Lifetime::new("'static", Span::call_site())), Self::Anonymous => None, Self::Named(ref s) => Some(syn::Lifetime::new(&s.to_string(), Span::call_site())),
}
}
}
/// Collect all lifetimes that are either longer_or_shorter pubstruct LifetimeTransitivity<'env> {
env: &'env LifetimeEnv,
visited: Vec<bool>,
out: Vec<&'env NamedLifetime>,
longer_or_shorter: LongerOrShorter,
}
impl<'env> LifetimeTransitivity<'env> { /// Returns a new [`LifetimeTransitivity`] that finds all longer lifetimes. pubfn longer(env: &'env LifetimeEnv) -> Self { Self::new(env, LongerOrShorter::Longer)
}
/// Returns a new [`LifetimeTransitivity`] that finds all shorter lifetimes. pubfn shorter(env: &'env LifetimeEnv) -> Self { Self::new(env, LongerOrShorter::Shorter)
}
/// Returns all the lifetimes longer than a provided `NamedLifetime`. pubfn longer_than(env: &'env LifetimeEnv, named: &NamedLifetime) -> Vec<&'n>env NamedLifetime> { letmut this = Self::new(env, LongerOrShorter::Longer);
this.visit(named);
this.finish()
}
/// Returns all the lifetimes shorter than the provided `NamedLifetime`. pubfn shorter_than(env: &'env LifetimeEnv, named: &NamedLifetime) -> Vec<&'an>env NamedLifetime> { letmut this = Self::new(env, LongerOrShorter::Shorter);
this.visit(named);
this.finish()
}
/// Visits a lifetime, as well as all the nodes it's transitively longer or /// shorter than, depending on how the `LifetimeTransitivity` was constructed. pubfn visit(&mutself, named: &NamedLifetime) { iflet Some(id) = self
.env
.nodes
.iter()
.position(|node| node.lifetime == *named)
{ self.dfs(id);
}
}
/// Performs depth-first search through the `LifetimeEnv` created at construction /// for all nodes longer or shorter than the node at the provided index, /// depending on how the `LifetimeTransitivity` was constructed. fn dfs(&mutself, index: usize) { // Note: all of these indexings SHOULD be valid because // `visited.len() == nodes.len()`, and the ids come from // calling `Iterator::position` on `nodes`, which never shrinks. // So we should be able to change these to `get_unchecked`... if !self.visited[index] { self.visited[index] = true;
let node = &self.env.nodes[index]; self.out.push(&node.lifetime); for &edge_index inself.longer_or_shorter.edges(node).iter() { self.dfs(edge_index);
}
}
}
/// A helper type for [`LifetimeTransitivity`] determining whether to find the /// transitively longer or transitively shorter lifetimes. enum LongerOrShorter {
Longer,
Shorter,
}
impl LongerOrShorter { /// Returns either the indices of the longer or shorter lifetimes, depending /// on `self`. fn edges<'node>(&self, node: &'node LifetimeNode) -> &'node [usize] { matchself {
LongerOrShorter::Longer => &node.longer[..],
LongerOrShorter::Shorter => &node.shorter[..],
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-06-23)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.