1use fnv::FnvHashSet;
2use syn::punctuated::Punctuated;
3use syn::{Lifetime, Type};
4
5use crate::usage::Options;
6
7/// A set of lifetimes.
8pub type LifetimeSet = FnvHashSet<Lifetime>;
9
10/// A set of references to lifetimes.
11pub 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.
15pub 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.
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.
27 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.
39pub trait CollectLifetimes {
40 /// Consume an iterator, accumulating all lifetimes in the elements which occur in `lifetimes`.
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.
48 fn collect_lifetimes_cloned(self, options: &Options, lifetimes: &LifetimeSet) -> LifetimeSet;
49}
50
51impl<'i, I, T> CollectLifetimes for T
52where
53 T: IntoIterator<Item = &'i I>,
54 I: 'i + UsesLifetimes,
55{
56 fn collect_lifetimes<'a>(
57 self,
58 options: &Options,
59 lifetimes: &'a LifetimeSet,
60 ) -> LifetimeRefSet<'a> {
61 self.into_iter()
62 .fold(init:Default::default(), |mut state: HashSet<&Lifetime, BuildHasherDefault<…>>, value: &I| {
63 state.extend(iter:value.uses_lifetimes(options, lifetimes));
64 state
65 })
66 }
67
68 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
76impl<T: UsesLifetimes> UsesLifetimes for Vec<T> {
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
86impl<T: UsesLifetimes, U> UsesLifetimes for Punctuated<T, U> {
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
96impl<T: UsesLifetimes> UsesLifetimes for Option<T> {
97 fn uses_lifetimes<'a>(
98 &self,
99 options: &Options,
100 lifetimes: &'a LifetimeSet,
101 ) -> LifetimeRefSet<'a> {
102 self.as_ref()
103 .map(|v: &T| v.uses_lifetimes(options, lifetimes))
104 .unwrap_or_default()
105 }
106}
107
108impl UsesLifetimes for Lifetime {
109 fn uses_lifetimes<'a>(&self, _: &Options, lifetimes: &'a LifetimeSet) -> LifetimeRefSet<'a> {
110 lifetimes.iter().filter(|lt: &&Lifetime| *lt == self).collect()
111 }
112}
113
114uses_lifetimes!(syn::AngleBracketedGenericArguments, args);
115uses_lifetimes!(syn::AssocType, ty);
116uses_lifetimes!(syn::BareFnArg, ty);
117uses_lifetimes!(syn::BoundLifetimes, lifetimes);
118uses_lifetimes!(syn::ConstParam, ty);
119uses_lifetimes!(syn::Constraint, bounds);
120uses_lifetimes!(syn::DataEnum, variants);
121uses_lifetimes!(syn::DataStruct, fields);
122uses_lifetimes!(syn::DataUnion, fields);
123uses_lifetimes!(syn::Field, ty);
124uses_lifetimes!(syn::FieldsNamed, named);
125uses_lifetimes!(syn::LifetimeParam, lifetime, bounds);
126uses_lifetimes!(syn::ParenthesizedGenericArguments, inputs, output);
127uses_lifetimes!(syn::Path, segments);
128uses_lifetimes!(syn::PathSegment, arguments);
129uses_lifetimes!(syn::PredicateLifetime, lifetime, bounds);
130uses_lifetimes!(syn::PredicateType, lifetimes, bounded_ty, bounds);
131uses_lifetimes!(syn::QSelf, ty);
132uses_lifetimes!(syn::TraitBound, path, lifetimes);
133uses_lifetimes!(syn::TypeArray, elem);
134uses_lifetimes!(syn::TypeBareFn, inputs, output);
135uses_lifetimes!(syn::TypeGroup, elem);
136uses_lifetimes!(syn::TypeImplTrait, bounds);
137uses_lifetimes!(syn::TypeParam, bounds);
138uses_lifetimes!(syn::TypeParen, elem);
139uses_lifetimes!(syn::TypePtr, elem);
140uses_lifetimes!(syn::TypeReference, lifetime, elem);
141uses_lifetimes!(syn::TypeSlice, elem);
142uses_lifetimes!(syn::TypeTuple, elems);
143uses_lifetimes!(syn::TypeTraitObject, bounds);
144uses_lifetimes!(syn::Variant, fields);
145
146impl UsesLifetimes for syn::Data {
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: &DataStruct) => v.uses_lifetimes(options, lifetimes),
154 syn::Data::Enum(ref v: &DataEnum) => v.uses_lifetimes(options, lifetimes),
155 syn::Data::Union(ref v: &DataUnion) => v.uses_lifetimes(options, lifetimes),
156 }
157 }
158}
159
160impl UsesLifetimes for Type {
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
186impl UsesLifetimes for syn::Fields {
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
196impl UsesLifetimes for syn::TypePath {
197 fn uses_lifetimes<'a>(
198 &self,
199 options: &Options,
200 lifetimes: &'a LifetimeSet,
201 ) -> LifetimeRefSet<'a> {
202 let mut hits: HashSet<&Lifetime, BuildHasherDefault<…>> = 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
212impl UsesLifetimes for syn::ReturnType {
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: &Box) = *self {
219 ty.uses_lifetimes(options, lifetimes)
220 } else {
221 Default::default()
222 }
223 }
224}
225
226impl UsesLifetimes for syn::PathArguments {
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: &AngleBracketedGenericArguments) => v.uses_lifetimes(options, lifetimes),
235 syn::PathArguments::Parenthesized(ref v: &ParenthesizedGenericArguments) => v.uses_lifetimes(options, lifetimes),
236 }
237 }
238}
239
240impl UsesLifetimes for syn::WherePredicate {
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: &PredicateType) => v.uses_lifetimes(options, lifetimes),
248 syn::WherePredicate::Lifetime(ref v: &PredicateLifetime) => 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
256impl UsesLifetimes for syn::GenericArgument {
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: &Type) => v.uses_lifetimes(options, lifetimes),
264 syn::GenericArgument::AssocType(ref v: &AssocType) => v.uses_lifetimes(options, lifetimes),
265 syn::GenericArgument::Lifetime(ref v: &Lifetime) => v.uses_lifetimes(options, lifetimes),
266 syn::GenericArgument::Constraint(ref v: &Constraint) => 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
277impl UsesLifetimes for syn::GenericParam {
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: &LifetimeParam) => v.uses_lifetimes(options, lifetimes),
285 syn::GenericParam::Type(ref v: &TypeParam) => v.uses_lifetimes(options, lifetimes),
286 syn::GenericParam::Const(ref v: &ConstParam) => v.uses_lifetimes(options, lifetimes),
287 }
288 }
289}
290
291impl UsesLifetimes for syn::TypeParamBound {
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: &TraitBound) => v.uses_lifetimes(options, lifetimes),
299 syn::TypeParamBound::Lifetime(ref v: &Lifetime) => 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)]
308mod 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]
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]
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