bon_macros/normalization/
lifetimes.rs

1use super::GenericsNamespace;
2use crate::util::prelude::*;
3use syn::visit::Visit;
4use syn::visit_mut::VisitMut;
5
6pub(crate) struct NormalizeLifetimes<'a> {
7    namespace: &'a GenericsNamespace,
8}
9
10impl<'a> NormalizeLifetimes<'a> {
11    pub(crate) fn new(namespace: &'a GenericsNamespace) -> Self {
12        Self { namespace }
13    }
14}
15
16impl VisitMut for NormalizeLifetimes<'_> {
17    fn visit_item_impl_mut(&mut self, impl_block: &mut syn::ItemImpl) {
18        for item in &mut impl_block.items {
19            self.visit_impl_item_mut(item);
20        }
21
22        AssignLifetimes::new(self, "i", &mut impl_block.generics)
23            .visit_type_mut(&mut impl_block.self_ty);
24    }
25
26    fn visit_impl_item_mut(&mut self, item: &mut syn::ImplItem) {
27        if let syn::ImplItem::Fn(fn_item) = item {
28            self.visit_signature_mut(&mut fn_item.sig);
29        }
30    }
31
32    fn visit_item_fn_mut(&mut self, fn_item: &mut syn::ItemFn) {
33        self.visit_signature_mut(&mut fn_item.sig);
34    }
35
36    fn visit_signature_mut(&mut self, signature: &mut syn::Signature) {
37        let mut visitor = AssignLifetimes::new(self, "f", &mut signature.generics);
38        for arg in &mut signature.inputs {
39            visitor.visit_fn_arg_mut(arg);
40        }
41
42        let return_type = match &mut signature.output {
43            syn::ReturnType::Type(_, return_type) => return_type,
44            syn::ReturnType::Default => return,
45        };
46
47        // Now perform lifetime elision for the lifetimes in the return type.
48        // This code implements the logic described in the Rust reference:
49        // https://doc.rust-lang.org/reference/lifetime-elision.html
50
51        let elided_output_lifetime = signature
52            .inputs
53            .first()
54            .and_then(|arg| {
55                let receiver = arg.as_receiver()?;
56                receiver.lifetime().or_else(|| match receiver.ty.as_ref() {
57                    syn::Type::Reference(reference) => reference.lifetime.as_ref(),
58                    _ => None,
59                })
60            })
61            .or_else(|| {
62                let lifetime = signature
63                    .inputs
64                    .iter()
65                    .filter_map(syn::FnArg::as_typed)
66                    .fold(LifetimeCollector::None, |mut acc, pat_type| {
67                        acc.visit_pat_type(pat_type);
68                        acc
69                    });
70
71                match lifetime {
72                    LifetimeCollector::Single(lifetime) => Some(lifetime),
73                    _ => None,
74                }
75            });
76
77        let elided_lifetime = match elided_output_lifetime {
78            Some(elided_lifetime) => elided_lifetime,
79            _ => return,
80        };
81
82        ElideOutputLifetime { elided_lifetime }.visit_type_mut(return_type);
83    }
84}
85
86struct AssignLifetimes<'a> {
87    base: &'a NormalizeLifetimes<'a>,
88
89    prefix: &'static str,
90
91    /// Generics where the assigned lifetimes should be stored.
92    generics: &'a mut syn::Generics,
93
94    next_lifetime_index: usize,
95}
96
97impl<'a> AssignLifetimes<'a> {
98    fn new(
99        base: &'a NormalizeLifetimes<'a>,
100        prefix: &'static str,
101        generics: &'a mut syn::Generics,
102    ) -> Self {
103        Self {
104            base,
105            prefix,
106            generics,
107            next_lifetime_index: 1,
108        }
109    }
110}
111
112impl VisitMut for AssignLifetimes<'_> {
113    fn visit_item_mut(&mut self, _item: &mut syn::Item) {
114        // Don't recurse into nested items because lifetimes aren't available there.
115    }
116
117    fn visit_type_bare_fn_mut(&mut self, _bare_fn: &mut syn::TypeBareFn) {
118        // Skip function pointers because anon lifetimes that appear in them
119        // don't belong to the surrounding function signature.
120    }
121
122    fn visit_parenthesized_generic_arguments_mut(
123        &mut self,
124        _args: &mut syn::ParenthesizedGenericArguments,
125    ) {
126        // Skip Fn traits for the same reason as function pointers described higher.
127    }
128
129    fn visit_lifetime_mut(&mut self, lifetime: &mut syn::Lifetime) {
130        if lifetime.ident == "_" {
131            *lifetime = self.next_lifetime();
132        }
133    }
134
135    fn visit_type_reference_mut(&mut self, reference: &mut syn::TypeReference) {
136        syn::visit_mut::visit_type_reference_mut(self, reference);
137        reference
138            .lifetime
139            .get_or_insert_with(|| self.next_lifetime());
140    }
141
142    fn visit_receiver_mut(&mut self, receiver: &mut syn::Receiver) {
143        // If this is a `self: Type` syntax, then it's not a special case
144        // and we can just visit the explicit type of the receiver as usual
145        if receiver.colon_token.is_some() {
146            syn::visit_mut::visit_type_mut(self, &mut receiver.ty);
147            return;
148        }
149
150        let lifetime = match &mut receiver.reference {
151            Some((_and, lifetime)) => lifetime,
152            _ => return,
153        };
154
155        if matches!(lifetime, Some(lifetime) if lifetime.ident != "_") {
156            return;
157        }
158
159        let receiver_ty = match receiver.ty.as_mut() {
160            syn::Type::Reference(receiver_ty) => receiver_ty,
161            _ => return,
162        };
163
164        let new_lifetime = self.next_lifetime();
165
166        *lifetime = Some(new_lifetime.clone());
167
168        receiver_ty.lifetime = Some(new_lifetime);
169    }
170}
171
172impl AssignLifetimes<'_> {
173    /// Make a lifetime with the next index. It's used to generate unique
174    /// lifetimes for every occurrence of a reference with the anonymous
175    /// lifetime.
176    fn next_lifetime(&mut self) -> syn::Lifetime {
177        let index = self.next_lifetime_index;
178        self.next_lifetime_index += 1;
179
180        let mut lifetime = self
181            .base
182            .namespace
183            .unique_lifetime(format!("{}{index}", self.prefix));
184
185        // `syn::Lifetime::new` requires the string to start with the `'` character,
186        // which is just discarded in that method's impl 🗿.
187        lifetime.insert(0, '\'');
188
189        let lifetime = syn::Lifetime::new(&lifetime, Span::call_site());
190
191        let lifetime_param = syn::LifetimeParam::new(lifetime.clone());
192        let lifetime_param = syn::GenericParam::Lifetime(lifetime_param);
193        self.generics.params.insert(index - 1, lifetime_param);
194
195        lifetime
196    }
197}
198
199enum LifetimeCollector<'a> {
200    None,
201    Single(&'a syn::Lifetime),
202    Multiple,
203}
204
205impl<'a> Visit<'a> for LifetimeCollector<'a> {
206    fn visit_item(&mut self, _item: &syn::Item) {
207        // Don't recurse into nested items because lifetimes aren't available there.
208    }
209
210    fn visit_type_bare_fn(&mut self, _bare_fn: &syn::TypeBareFn) {
211        // Skip function pointers because anon lifetimes that appear in them
212        // don't belong to the surrounding function signature.
213    }
214
215    fn visit_parenthesized_generic_arguments(
216        &mut self,
217        _args: &syn::ParenthesizedGenericArguments,
218    ) {
219        // Skip Fn traits for the same reason as function pointers described higher.
220    }
221
222    fn visit_lifetime(&mut self, lifetime: &'a syn::Lifetime) {
223        match self {
224            Self::None => *self = Self::Single(lifetime),
225            Self::Single(_) => *self = Self::Multiple,
226            Self::Multiple => {}
227        }
228    }
229}
230
231struct ElideOutputLifetime<'a> {
232    elided_lifetime: &'a syn::Lifetime,
233}
234
235impl VisitMut for ElideOutputLifetime<'_> {
236    fn visit_item_mut(&mut self, _item: &mut syn::Item) {
237        // Don't recurse into nested items because lifetimes aren't available there.
238    }
239
240    fn visit_type_bare_fn_mut(&mut self, _bare_fn: &mut syn::TypeBareFn) {
241        // Skip function pointers because anon lifetimes that appear in them
242        // don't belong to the surrounding function signature.
243    }
244
245    fn visit_parenthesized_generic_arguments_mut(
246        &mut self,
247        _args: &mut syn::ParenthesizedGenericArguments,
248    ) {
249        // Skip Fn traits for the same reason as function pointers described higher.
250    }
251
252    fn visit_lifetime_mut(&mut self, lifetime: &mut syn::Lifetime) {
253        if lifetime.ident == "_" {
254            *lifetime = self.elided_lifetime.clone();
255        }
256    }
257
258    fn visit_type_reference_mut(&mut self, reference: &mut syn::TypeReference) {
259        syn::visit_mut::visit_type_reference_mut(self, reference);
260
261        reference
262            .lifetime
263            .get_or_insert_with(|| self.elided_lifetime.clone());
264    }
265}