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