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. |
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. |
39 | pub 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 | |
51 | impl<'i, I, T> CollectLifetimes for T |
52 | where |
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 | |
76 | impl<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 | |
86 | impl<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 | |
96 | impl<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 | |
108 | impl 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 | |
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 { |
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 | |
160 | impl 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 | |
186 | impl 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 | |
196 | impl 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 | |
212 | impl 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 | |
226 | impl 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 | |
240 | impl 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 | |
256 | impl 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 | |
277 | impl 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 | |
291 | impl 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)] |
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 ] |
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 | |