Skip to main content

darling_core/usage/
lifetimes.rs

1use std::collections::HashSet;
2
3use syn::punctuated::Punctuated;
4use syn::{Lifetime, Type};
5
6use crate::usage::Options;
7
8/// A set of lifetimes.
9pub type LifetimeSet = HashSet<Lifetime>;
10
11/// A set of references to lifetimes.
12pub type LifetimeRefSet<'a> = HashSet<&'a Lifetime>;
13
14/// Searcher for finding lifetimes in a syntax tree.
15/// This can be used to determine which lifetimes must be emitted in generated code.
16pub trait UsesLifetimes {
17    /// Returns the subset of the queried lifetimes that are used by the implementing syntax element.
18    ///
19    /// This method only accounts for direct usage by the element; indirect usage via bounds or `where`
20    /// predicates are not detected.
21    fn uses_lifetimes<'a>(
22        &self,
23        options: &Options,
24        lifetimes: &'a LifetimeSet,
25    ) -> LifetimeRefSet<'a>;
26
27    /// Find all used lifetimes, then clone them and return that set.
28    fn uses_lifetimes_cloned(&self, options: &Options, lifetimes: &LifetimeSet) -> LifetimeSet {
29        self.uses_lifetimes(options, lifetimes)
30            .into_iter()
31            .cloned()
32            .collect()
33    }
34}
35
36/// Searcher for finding lifetimes in an iterator.
37///
38/// This trait extends iterators, providing a way to turn a filtered list of fields or variants into a set
39/// of lifetimes.
40pub trait CollectLifetimes {
41    /// Consume an iterator, accumulating all lifetimes in the elements which occur in `lifetimes`.
42    fn collect_lifetimes<'a>(
43        self,
44        options: &Options,
45        lifetimes: &'a LifetimeSet,
46    ) -> LifetimeRefSet<'a>;
47
48    /// Consume an iterator using `collect_lifetimes`, then clone all found lifetimes and return that set.
49    fn collect_lifetimes_cloned(self, options: &Options, lifetimes: &LifetimeSet) -> LifetimeSet;
50}
51
52impl<'i, I, T> CollectLifetimes for T
53where
54    T: IntoIterator<Item = &'i I>,
55    I: 'i + UsesLifetimes,
56{
57    fn collect_lifetimes<'a>(
58        self,
59        options: &Options,
60        lifetimes: &'a LifetimeSet,
61    ) -> LifetimeRefSet<'a> {
62        self.into_iter()
63            .fold(Default::default(), |mut state, value| {
64                state.extend(value.uses_lifetimes(options, lifetimes));
65                state
66            })
67    }
68
69    fn collect_lifetimes_cloned(self, options: &Options, lifetimes: &LifetimeSet) -> LifetimeSet {
70        self.collect_lifetimes(options, lifetimes)
71            .into_iter()
72            .cloned()
73            .collect()
74    }
75}
76
77impl<T: UsesLifetimes> UsesLifetimes for Vec<T> {
78    fn uses_lifetimes<'a>(
79        &self,
80        options: &Options,
81        lifetimes: &'a LifetimeSet,
82    ) -> LifetimeRefSet<'a> {
83        self.collect_lifetimes(options, lifetimes)
84    }
85}
86
87impl<T: UsesLifetimes, U> UsesLifetimes for Punctuated<T, U> {
88    fn uses_lifetimes<'a>(
89        &self,
90        options: &Options,
91        lifetimes: &'a LifetimeSet,
92    ) -> LifetimeRefSet<'a> {
93        self.collect_lifetimes(options, lifetimes)
94    }
95}
96
97impl<T: UsesLifetimes> UsesLifetimes for Option<T> {
98    fn uses_lifetimes<'a>(
99        &self,
100        options: &Options,
101        lifetimes: &'a LifetimeSet,
102    ) -> LifetimeRefSet<'a> {
103        self.as_ref()
104            .map(|v| v.uses_lifetimes(options, lifetimes))
105            .unwrap_or_default()
106    }
107}
108
109impl UsesLifetimes for Lifetime {
110    fn uses_lifetimes<'a>(&self, _: &Options, lifetimes: &'a LifetimeSet) -> LifetimeRefSet<'a> {
111        lifetimes.iter().filter(|lt| *lt == self).collect()
112    }
113}
114
115uses_lifetimes!(syn::AngleBracketedGenericArguments, args);
116uses_lifetimes!(syn::AssocType, ty);
117uses_lifetimes!(syn::BareFnArg, ty);
118uses_lifetimes!(syn::BoundLifetimes, lifetimes);
119uses_lifetimes!(syn::ConstParam, ty);
120uses_lifetimes!(syn::Constraint, bounds);
121uses_lifetimes!(syn::DataEnum, variants);
122uses_lifetimes!(syn::DataStruct, fields);
123uses_lifetimes!(syn::DataUnion, fields);
124uses_lifetimes!(syn::Field, ty);
125uses_lifetimes!(syn::FieldsNamed, named);
126uses_lifetimes!(syn::LifetimeParam, lifetime, bounds);
127uses_lifetimes!(syn::ParenthesizedGenericArguments, inputs, output);
128uses_lifetimes!(syn::Path, segments);
129uses_lifetimes!(syn::PathSegment, arguments);
130uses_lifetimes!(syn::PredicateLifetime, lifetime, bounds);
131uses_lifetimes!(syn::PredicateType, lifetimes, bounded_ty, bounds);
132uses_lifetimes!(syn::QSelf, ty);
133uses_lifetimes!(syn::TraitBound, path, lifetimes);
134uses_lifetimes!(syn::TypeArray, elem);
135uses_lifetimes!(syn::TypeBareFn, inputs, output);
136uses_lifetimes!(syn::TypeGroup, elem);
137uses_lifetimes!(syn::TypeImplTrait, bounds);
138uses_lifetimes!(syn::TypeParam, bounds);
139uses_lifetimes!(syn::TypeParen, elem);
140uses_lifetimes!(syn::TypePtr, elem);
141uses_lifetimes!(syn::TypeReference, lifetime, elem);
142uses_lifetimes!(syn::TypeSlice, elem);
143uses_lifetimes!(syn::TypeTuple, elems);
144uses_lifetimes!(syn::TypeTraitObject, bounds);
145uses_lifetimes!(syn::Variant, fields);
146
147impl UsesLifetimes for syn::Data {
148    fn uses_lifetimes<'a>(
149        &self,
150        options: &Options,
151        lifetimes: &'a LifetimeSet,
152    ) -> LifetimeRefSet<'a> {
153        match *self {
154            syn::Data::Struct(ref v) => v.uses_lifetimes(options, lifetimes),
155            syn::Data::Enum(ref v) => v.uses_lifetimes(options, lifetimes),
156            syn::Data::Union(ref v) => v.uses_lifetimes(options, lifetimes),
157        }
158    }
159}
160
161impl UsesLifetimes for Type {
162    fn uses_lifetimes<'a>(
163        &self,
164        options: &Options,
165        lifetimes: &'a LifetimeSet,
166    ) -> LifetimeRefSet<'a> {
167        match *self {
168            Type::Slice(ref v) => v.uses_lifetimes(options, lifetimes),
169            Type::Array(ref v) => v.uses_lifetimes(options, lifetimes),
170            Type::Ptr(ref v) => v.uses_lifetimes(options, lifetimes),
171            Type::Reference(ref v) => v.uses_lifetimes(options, lifetimes),
172            Type::BareFn(ref v) => v.uses_lifetimes(options, lifetimes),
173            Type::Tuple(ref v) => v.uses_lifetimes(options, lifetimes),
174            Type::Path(ref v) => v.uses_lifetimes(options, lifetimes),
175            Type::Paren(ref v) => v.uses_lifetimes(options, lifetimes),
176            Type::Group(ref v) => v.uses_lifetimes(options, lifetimes),
177            Type::TraitObject(ref v) => v.uses_lifetimes(options, lifetimes),
178            Type::ImplTrait(ref v) => v.uses_lifetimes(options, lifetimes),
179            Type::Macro(_) | Type::Verbatim(_) | Type::Infer(_) | Type::Never(_) => {
180                Default::default()
181            }
182            _ => panic!("Unknown syn::Type: {:?}", self),
183        }
184    }
185}
186
187impl UsesLifetimes for syn::Fields {
188    fn uses_lifetimes<'a>(
189        &self,
190        options: &Options,
191        lifetimes: &'a LifetimeSet,
192    ) -> LifetimeRefSet<'a> {
193        self.collect_lifetimes(options, lifetimes)
194    }
195}
196
197impl UsesLifetimes for syn::TypePath {
198    fn uses_lifetimes<'a>(
199        &self,
200        options: &Options,
201        lifetimes: &'a LifetimeSet,
202    ) -> LifetimeRefSet<'a> {
203        let mut hits = self.path.uses_lifetimes(options, lifetimes);
204
205        if options.include_type_path_qself() {
206            hits.extend(self.qself.uses_lifetimes(options, lifetimes));
207        }
208
209        hits
210    }
211}
212
213impl UsesLifetimes for syn::ReturnType {
214    fn uses_lifetimes<'a>(
215        &self,
216        options: &Options,
217        lifetimes: &'a LifetimeSet,
218    ) -> LifetimeRefSet<'a> {
219        if let syn::ReturnType::Type(_, ref ty) = *self {
220            ty.uses_lifetimes(options, lifetimes)
221        } else {
222            Default::default()
223        }
224    }
225}
226
227impl UsesLifetimes for syn::PathArguments {
228    fn uses_lifetimes<'a>(
229        &self,
230        options: &Options,
231        lifetimes: &'a LifetimeSet,
232    ) -> LifetimeRefSet<'a> {
233        match *self {
234            syn::PathArguments::None => Default::default(),
235            syn::PathArguments::AngleBracketed(ref v) => v.uses_lifetimes(options, lifetimes),
236            syn::PathArguments::Parenthesized(ref v) => v.uses_lifetimes(options, lifetimes),
237        }
238    }
239}
240
241impl UsesLifetimes for syn::WherePredicate {
242    fn uses_lifetimes<'a>(
243        &self,
244        options: &Options,
245        lifetimes: &'a LifetimeSet,
246    ) -> LifetimeRefSet<'a> {
247        match *self {
248            syn::WherePredicate::Type(ref v) => v.uses_lifetimes(options, lifetimes),
249            syn::WherePredicate::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
250            // non-exhaustive enum
251            // TODO: replace panic with failible function
252            _ => panic!("Unknown syn::WherePredicate: {:?}", self),
253        }
254    }
255}
256
257impl UsesLifetimes for syn::GenericArgument {
258    fn uses_lifetimes<'a>(
259        &self,
260        options: &Options,
261        lifetimes: &'a LifetimeSet,
262    ) -> LifetimeRefSet<'a> {
263        match *self {
264            syn::GenericArgument::Type(ref v) => v.uses_lifetimes(options, lifetimes),
265            syn::GenericArgument::AssocType(ref v) => v.uses_lifetimes(options, lifetimes),
266            syn::GenericArgument::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
267            syn::GenericArgument::Constraint(ref v) => v.uses_lifetimes(options, lifetimes),
268            syn::GenericArgument::AssocConst(_) | syn::GenericArgument::Const(_) => {
269                Default::default()
270            }
271            // non-exhaustive enum
272            // TODO: replace panic with failible function
273            _ => panic!("Unknown syn::GenericArgument: {:?}", self),
274        }
275    }
276}
277
278impl UsesLifetimes for syn::GenericParam {
279    fn uses_lifetimes<'a>(
280        &self,
281        options: &Options,
282        lifetimes: &'a LifetimeSet,
283    ) -> LifetimeRefSet<'a> {
284        match *self {
285            syn::GenericParam::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
286            syn::GenericParam::Type(ref v) => v.uses_lifetimes(options, lifetimes),
287            syn::GenericParam::Const(ref v) => v.uses_lifetimes(options, lifetimes),
288        }
289    }
290}
291
292impl UsesLifetimes for syn::TypeParamBound {
293    fn uses_lifetimes<'a>(
294        &self,
295        options: &Options,
296        lifetimes: &'a LifetimeSet,
297    ) -> LifetimeRefSet<'a> {
298        match *self {
299            syn::TypeParamBound::Trait(ref v) => v.uses_lifetimes(options, lifetimes),
300            syn::TypeParamBound::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
301            // non-exhaustive enum
302            // TODO: replace panic with failible function
303            _ => panic!("Unknown syn::TypeParamBound: {:?}", self),
304        }
305    }
306}
307
308#[cfg(test)]
309mod tests {
310    use proc_macro2::Span;
311    use syn::{parse_quote, DeriveInput};
312
313    use super::UsesLifetimes;
314    use crate::usage::GenericsExt;
315    use crate::usage::Purpose::*;
316
317    #[test]
318    fn struct_named() {
319        let input: DeriveInput = parse_quote! {
320            struct Foo<'a, 'b: 'a> {
321                parent: &'b Bar,
322                child: &'a Baz,
323            }
324        };
325        let omitted = syn::Lifetime::new("'c", Span::call_site());
326
327        let lifetimes = {
328            let mut lt = input.generics.declared_lifetimes();
329            lt.insert(omitted);
330            lt
331        };
332
333        let matches = input.data.uses_lifetimes(&BoundImpl.into(), &lifetimes);
334        assert_eq!(matches.len(), 2);
335    }
336
337    #[test]
338    fn qself() {
339        let input: DeriveInput = parse_quote! {
340            struct Foo<'a, 'b: 'a> {
341                parent: &'b Bar,
342                child: <Bar<'a> as MyIterator>::Item,
343            }
344        };
345        let lifetimes = input.generics.declared_lifetimes();
346        let matches = input.data.uses_lifetimes(&BoundImpl.into(), &lifetimes);
347        assert_eq!(matches.len(), 1);
348
349        let decl_matches = input.data.uses_lifetimes(&Declare.into(), &lifetimes);
350        assert_eq!(decl_matches.len(), 2);
351    }
352}