bon_macros/normalization/
lifetimes.rsuse super::GenericsNamespace;
use crate::util::prelude::*;
use syn::visit::Visit;
use syn::visit_mut::VisitMut;
pub(crate) struct NormalizeLifetimes<'a> {
namespace: &'a GenericsNamespace,
}
impl<'a> NormalizeLifetimes<'a> {
pub(crate) fn new(namespace: &'a GenericsNamespace) -> Self {
Self { namespace }
}
}
impl VisitMut for NormalizeLifetimes<'_> {
fn visit_item_impl_mut(&mut self, impl_block: &mut syn::ItemImpl) {
for item in &mut impl_block.items {
self.visit_impl_item_mut(item);
}
AssignLifetimes::new(self, "i", &mut impl_block.generics)
.visit_type_mut(&mut impl_block.self_ty);
}
fn visit_impl_item_mut(&mut self, item: &mut syn::ImplItem) {
if let syn::ImplItem::Fn(fn_item) = item {
self.visit_signature_mut(&mut fn_item.sig);
}
}
fn visit_item_fn_mut(&mut self, fn_item: &mut syn::ItemFn) {
self.visit_signature_mut(&mut fn_item.sig);
}
fn visit_signature_mut(&mut self, signature: &mut syn::Signature) {
let mut visitor = AssignLifetimes::new(self, "f", &mut signature.generics);
for arg in &mut signature.inputs {
visitor.visit_fn_arg_mut(arg);
}
let return_type = match &mut signature.output {
syn::ReturnType::Type(_, return_type) => return_type,
syn::ReturnType::Default => return,
};
let elided_output_lifetime = signature
.inputs
.first()
.and_then(|arg| {
let receiver = arg.as_receiver()?;
receiver.lifetime().or_else(|| match receiver.ty.as_ref() {
syn::Type::Reference(reference) => reference.lifetime.as_ref(),
_ => None,
})
})
.or_else(|| {
let lifetime = signature
.inputs
.iter()
.filter_map(syn::FnArg::as_typed)
.fold(LifetimeCollector::None, |mut acc, pat_type| {
acc.visit_pat_type(pat_type);
acc
});
match lifetime {
LifetimeCollector::Single(lifetime) => Some(lifetime),
_ => None,
}
});
let elided_lifetime = match elided_output_lifetime {
Some(elided_lifetime) => elided_lifetime,
_ => return,
};
ElideOutputLifetime { elided_lifetime }.visit_type_mut(return_type);
}
}
struct AssignLifetimes<'a> {
base: &'a NormalizeLifetimes<'a>,
prefix: &'static str,
generics: &'a mut syn::Generics,
next_lifetime_index: usize,
}
impl<'a> AssignLifetimes<'a> {
fn new(
base: &'a NormalizeLifetimes<'a>,
prefix: &'static str,
generics: &'a mut syn::Generics,
) -> Self {
Self {
base,
prefix,
generics,
next_lifetime_index: 1,
}
}
}
impl VisitMut for AssignLifetimes<'_> {
fn visit_item_mut(&mut self, _item: &mut syn::Item) {
}
fn visit_type_bare_fn_mut(&mut self, _bare_fn: &mut syn::TypeBareFn) {
}
fn visit_parenthesized_generic_arguments_mut(
&mut self,
_args: &mut syn::ParenthesizedGenericArguments,
) {
}
fn visit_lifetime_mut(&mut self, lifetime: &mut syn::Lifetime) {
if lifetime.ident == "_" {
*lifetime = self.next_lifetime();
}
}
fn visit_type_reference_mut(&mut self, reference: &mut syn::TypeReference) {
syn::visit_mut::visit_type_reference_mut(self, reference);
reference
.lifetime
.get_or_insert_with(|| self.next_lifetime());
}
fn visit_receiver_mut(&mut self, receiver: &mut syn::Receiver) {
if receiver.colon_token.is_some() {
syn::visit_mut::visit_type_mut(self, &mut receiver.ty);
return;
}
let lifetime = match &mut receiver.reference {
Some((_and, lifetime)) => lifetime,
_ => return,
};
if matches!(lifetime, Some(lifetime) if lifetime.ident != "_") {
return;
}
let receiver_ty = match receiver.ty.as_mut() {
syn::Type::Reference(receiver_ty) => receiver_ty,
_ => return,
};
let new_lifetime = self.next_lifetime();
*lifetime = Some(new_lifetime.clone());
receiver_ty.lifetime = Some(new_lifetime);
}
}
impl AssignLifetimes<'_> {
fn next_lifetime(&mut self) -> syn::Lifetime {
let index = self.next_lifetime_index;
self.next_lifetime_index += 1;
let mut lifetime = self
.base
.namespace
.unique_lifetime(format!("{}{index}", self.prefix));
lifetime.insert(0, '\'');
let lifetime = syn::Lifetime::new(&lifetime, Span::call_site());
let lifetime_param = syn::LifetimeParam::new(lifetime.clone());
let lifetime_param = syn::GenericParam::Lifetime(lifetime_param);
self.generics.params.insert(index - 1, lifetime_param);
lifetime
}
}
enum LifetimeCollector<'a> {
None,
Single(&'a syn::Lifetime),
Multiple,
}
impl<'a> Visit<'a> for LifetimeCollector<'a> {
fn visit_item(&mut self, _item: &syn::Item) {
}
fn visit_type_bare_fn(&mut self, _bare_fn: &syn::TypeBareFn) {
}
fn visit_parenthesized_generic_arguments(
&mut self,
_args: &syn::ParenthesizedGenericArguments,
) {
}
fn visit_lifetime(&mut self, lifetime: &'a syn::Lifetime) {
match self {
Self::None => *self = Self::Single(lifetime),
Self::Single(_) => *self = Self::Multiple,
Self::Multiple => {}
}
}
}
struct ElideOutputLifetime<'a> {
elided_lifetime: &'a syn::Lifetime,
}
impl VisitMut for ElideOutputLifetime<'_> {
fn visit_item_mut(&mut self, _item: &mut syn::Item) {
}
fn visit_type_bare_fn_mut(&mut self, _bare_fn: &mut syn::TypeBareFn) {
}
fn visit_parenthesized_generic_arguments_mut(
&mut self,
_args: &mut syn::ParenthesizedGenericArguments,
) {
}
fn visit_lifetime_mut(&mut self, lifetime: &mut syn::Lifetime) {
if lifetime.ident == "_" {
*lifetime = self.elided_lifetime.clone();
}
}
fn visit_type_reference_mut(&mut self, reference: &mut syn::TypeReference) {
syn::visit_mut::visit_type_reference_mut(self, reference);
reference
.lifetime
.get_or_insert_with(|| self.elided_lifetime.clone());
}
}