abi_stable_derive/sabi_trait/
lifetime_unelider.rs

1use syn::{
2    visit_mut::{self, VisitMut},
3    Lifetime, Type, TypeReference,
4};
5
6/// Used to unelide the lifetimes in the `&self` parameter of methods.
7pub(crate) struct LifetimeUnelider<'a, 'b> {
8    self_lifetime: &'b mut Option<&'a syn::Lifetime>,
9    contains_self_borrow: bool,
10    pub(crate) additional_lifetime_def: Option<&'a syn::LifetimeDef>,
11}
12
13pub(crate) struct TypeProperties<'a> {
14    pub(crate) additional_lifetime_def: Option<&'a syn::LifetimeDef>,
15    pub(crate) found_borrow_kind: Option<BorrowKind>,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub(crate) enum BorrowKind {
20    Reference,
21    MutReference,
22    Other,
23}
24
25impl<'a, 'b> LifetimeUnelider<'a, 'b> {
26    pub(crate) fn new(self_lifetime: &'b mut Option<&'a syn::Lifetime>) -> Self {
27        Self {
28            self_lifetime,
29            contains_self_borrow: false,
30            additional_lifetime_def: None,
31        }
32    }
33
34    /// Unelide the lifetimes the `&self` parameter of methods.
35    pub(crate) fn visit_type(mut self, ty: &mut Type) -> TypeProperties<'a> {
36        self.contains_self_borrow = false;
37
38        visit_mut::visit_type_mut(&mut self, ty);
39
40        let found_borrow_kind = if self.contains_self_borrow {
41            let bk = match &*ty {
42                Type::Reference(tr) => {
43                    if tr.mutability.is_some() {
44                        BorrowKind::MutReference
45                    } else {
46                        BorrowKind::Reference
47                    }
48                }
49                _ => BorrowKind::Other,
50            };
51
52            Some(bk)
53        } else {
54            None
55        };
56
57        TypeProperties {
58            additional_lifetime_def: self.additional_lifetime_def,
59            found_borrow_kind,
60        }
61    }
62}
63
64impl<'a, 'b> LifetimeUnelider<'a, 'b> {
65    fn setup_lifetime(&mut self) -> Lifetime {
66        let additional_lifetime_def = &mut self.additional_lifetime_def;
67        let x = self.self_lifetime.get_or_insert_with(|| {
68            let ret = syn::parse_str::<syn::LifetimeDef>("'_self").unwrap();
69            let ret: &'a syn::LifetimeDef = Box::leak(Box::new(ret));
70            *additional_lifetime_def = Some(ret);
71            &ret.lifetime
72        });
73        (*x).clone()
74    }
75}
76impl<'a, 'b> VisitMut for LifetimeUnelider<'a, 'b> {
77    fn visit_type_reference_mut(&mut self, ref_: &mut TypeReference) {
78        if is_self_borrow(self.self_lifetime, ref_) {
79            self.contains_self_borrow = true;
80        }
81
82        let is_elided = ref_.lifetime.as_ref().map_or(true, |x| x.ident == "_");
83
84        if is_elided {
85            ref_.lifetime = Some(self.setup_lifetime());
86        }
87
88        visit_mut::visit_type_mut(self, &mut ref_.elem)
89    }
90
91    fn visit_lifetime_mut(&mut self, lt: &mut Lifetime) {
92        if is_self_lifetime(self.self_lifetime, lt) {
93            self.contains_self_borrow = true;
94        }
95
96        if lt.ident == "_" {
97            *lt = self.setup_lifetime();
98        }
99    }
100}
101
102fn is_self_lifetime(self_lifetime: &Option<&Lifetime>, lt: &Lifetime) -> bool {
103    match self_lifetime {
104        Some(sl) if sl.ident == lt.ident => true,
105        _ => lt.ident == "_",
106    }
107}
108
109fn is_self_borrow(self_lifetime: &Option<&Lifetime>, tr: &TypeReference) -> bool {
110    match (self_lifetime, &tr.lifetime) {
111        (Some(sl), Some(lt)) if sl.ident == lt.ident => true,
112        (_, Some(lt)) => lt.ident == "_",
113        (_, None) => true,
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    fn get_self_borrow_kind<'a, 'b>(
122        mut self_lifetime: Option<&'a syn::Lifetime>,
123        mut ty: Type,
124    ) -> Option<BorrowKind> {
125        let mut this = LifetimeUnelider::new(&mut self_lifetime);
126        let ret = this.visit_type(&mut ty.clone());
127        ret.found_borrow_kind
128    }
129
130    fn assert_elided(self_lifetime: &Lifetime, ty: Type, expected: BorrowKind) {
131        assert_eq!(
132            get_self_borrow_kind(Some(self_lifetime), ty.clone()),
133            Some(expected.clone())
134        );
135        assert_eq!(get_self_borrow_kind(None, ty.clone()), Some(expected));
136    }
137
138    fn assert_unelided(self_lifetime: &Lifetime, ty: Type, expected: BorrowKind) {
139        assert_eq!(
140            get_self_borrow_kind(Some(self_lifetime), ty.clone()),
141            Some(expected.clone())
142        );
143        assert_eq!(get_self_borrow_kind(None, ty.clone()), None);
144    }
145
146    fn parse_ty(s: &str) -> syn::Type {
147        syn::parse_str(s).unwrap()
148    }
149
150    #[test]
151    fn borrow_self_detection() {
152        let lifetime_a = &syn::parse_str::<Lifetime>("'a").unwrap();
153
154        // Implicitly borrowing from self
155        {
156            assert_elided(lifetime_a, parse_ty("&()"), BorrowKind::Reference);
157            assert_elided(lifetime_a, parse_ty("&mut ()"), BorrowKind::MutReference);
158            assert_elided(lifetime_a, parse_ty("Option<&()>"), BorrowKind::Other);
159            assert_elided(lifetime_a, parse_ty("Option<&mut ()>"), BorrowKind::Other);
160            assert_elided(lifetime_a, parse_ty("Foo<'_>"), BorrowKind::Other);
161
162            assert_elided(lifetime_a, parse_ty("&'_ &'b ()"), BorrowKind::Reference);
163            assert_elided(
164                lifetime_a,
165                parse_ty("&'_ mut &'b ()"),
166                BorrowKind::MutReference,
167            );
168            assert_elided(lifetime_a, parse_ty("&'b &'_ ()"), BorrowKind::Reference);
169            assert_elided(
170                lifetime_a,
171                parse_ty("&'b mut &'_ ()"),
172                BorrowKind::MutReference,
173            );
174
175            assert_elided(
176                lifetime_a,
177                parse_ty("Option<&'_ &'b ()>"),
178                BorrowKind::Other,
179            );
180            assert_elided(
181                lifetime_a,
182                parse_ty("Option<&'_ mut &'b ()>"),
183                BorrowKind::Other,
184            );
185            assert_elided(
186                lifetime_a,
187                parse_ty("Option<&'b &'_ ()>"),
188                BorrowKind::Other,
189            );
190            assert_elided(
191                lifetime_a,
192                parse_ty("Option<&'b mut &'_ ()>"),
193                BorrowKind::Other,
194            );
195        }
196
197        // Explicitly borrowing from self
198        {
199            assert_unelided(lifetime_a, parse_ty("&'a ()"), BorrowKind::Reference);
200            assert_unelided(lifetime_a, parse_ty("&'a mut ()"), BorrowKind::MutReference);
201            assert_unelided(lifetime_a, parse_ty("Option<&'a ()>"), BorrowKind::Other);
202            assert_unelided(
203                lifetime_a,
204                parse_ty("Option<&'a mut ()>"),
205                BorrowKind::Other,
206            );
207            assert_unelided(lifetime_a, parse_ty("Foo<'a>"), BorrowKind::Other);
208
209            assert_unelided(lifetime_a, parse_ty("&'a &'b ()"), BorrowKind::Reference);
210            assert_unelided(
211                lifetime_a,
212                parse_ty("&'a mut &'b ()"),
213                BorrowKind::MutReference,
214            );
215            assert_unelided(lifetime_a, parse_ty("&'b &'a ()"), BorrowKind::Reference);
216            assert_unelided(
217                lifetime_a,
218                parse_ty("&'b mut &'a ()"),
219                BorrowKind::MutReference,
220            );
221
222            assert_unelided(
223                lifetime_a,
224                parse_ty("Option<&'a &'b ()>"),
225                BorrowKind::Other,
226            );
227            assert_unelided(
228                lifetime_a,
229                parse_ty("Option<&'a mut &'b ()>"),
230                BorrowKind::Other,
231            );
232            assert_unelided(
233                lifetime_a,
234                parse_ty("Option<&'b &'a ()>"),
235                BorrowKind::Other,
236            );
237            assert_unelided(
238                lifetime_a,
239                parse_ty("Option<&'b mut &'a ()>"),
240                BorrowKind::Other,
241            );
242        }
243
244        {
245            let gsbk = get_self_borrow_kind;
246            let lt_a = Some(lifetime_a);
247            assert_eq!(gsbk(lt_a, parse_ty("&'b ()")), None);
248            assert_eq!(gsbk(lt_a, parse_ty("&'b mut ()")), None);
249            assert_eq!(gsbk(lt_a, parse_ty("Option<&'b ()>")), None);
250            assert_eq!(gsbk(lt_a, parse_ty("Option<&'b mut ()>")), None);
251            assert_eq!(gsbk(lt_a, parse_ty("Foo<'b>")), None);
252        }
253    }
254}