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::BareFnArg, ty); |
116 | uses_lifetimes!(syn::Binding, ty); |
117 | uses_lifetimes!(syn::BoundLifetimes, lifetimes); |
118 | uses_lifetimes!(syn::Constraint, bounds); |
119 | uses_lifetimes!(syn::DataEnum, variants); |
120 | uses_lifetimes!(syn::DataStruct, fields); |
121 | uses_lifetimes!(syn::DataUnion, fields); |
122 | uses_lifetimes!(syn::Field, ty); |
123 | uses_lifetimes!(syn::FieldsNamed, named); |
124 | uses_lifetimes!(syn::LifetimeDef, lifetime, bounds); |
125 | uses_lifetimes!(syn::ParenthesizedGenericArguments, inputs, output); |
126 | uses_lifetimes!(syn::Path, segments); |
127 | uses_lifetimes!(syn::PathSegment, arguments); |
128 | uses_lifetimes!(syn::PredicateEq, lhs_ty, rhs_ty); |
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::TypeParen, elem); |
138 | uses_lifetimes!(syn::TypePtr, elem); |
139 | uses_lifetimes!(syn::TypeReference, lifetime, elem); |
140 | uses_lifetimes!(syn::TypeSlice, elem); |
141 | uses_lifetimes!(syn::TypeTuple, elems); |
142 | uses_lifetimes!(syn::TypeTraitObject, bounds); |
143 | uses_lifetimes!(syn::Variant, fields); |
144 | |
145 | impl UsesLifetimes for syn::Data { |
146 | fn uses_lifetimes<'a>( |
147 | &self, |
148 | options: &Options, |
149 | lifetimes: &'a LifetimeSet, |
150 | ) -> LifetimeRefSet<'a> { |
151 | match *self { |
152 | syn::Data::Struct(ref v: &DataStruct) => v.uses_lifetimes(options, lifetimes), |
153 | syn::Data::Enum(ref v: &DataEnum) => v.uses_lifetimes(options, lifetimes), |
154 | syn::Data::Union(ref v: &DataUnion) => v.uses_lifetimes(options, lifetimes), |
155 | } |
156 | } |
157 | } |
158 | |
159 | impl UsesLifetimes for Type { |
160 | fn uses_lifetimes<'a>( |
161 | &self, |
162 | options: &Options, |
163 | lifetimes: &'a LifetimeSet, |
164 | ) -> LifetimeRefSet<'a> { |
165 | match *self { |
166 | Type::Slice(ref v) => v.uses_lifetimes(options, lifetimes), |
167 | Type::Array(ref v) => v.uses_lifetimes(options, lifetimes), |
168 | Type::Ptr(ref v) => v.uses_lifetimes(options, lifetimes), |
169 | Type::Reference(ref v) => v.uses_lifetimes(options, lifetimes), |
170 | Type::BareFn(ref v) => v.uses_lifetimes(options, lifetimes), |
171 | Type::Tuple(ref v) => v.uses_lifetimes(options, lifetimes), |
172 | Type::Path(ref v) => v.uses_lifetimes(options, lifetimes), |
173 | Type::Paren(ref v) => v.uses_lifetimes(options, lifetimes), |
174 | Type::Group(ref v) => v.uses_lifetimes(options, lifetimes), |
175 | Type::TraitObject(ref v) => v.uses_lifetimes(options, lifetimes), |
176 | Type::ImplTrait(ref v) => v.uses_lifetimes(options, lifetimes), |
177 | Type::Macro(_) | Type::Verbatim(_) | Type::Infer(_) | Type::Never(_) => { |
178 | Default::default() |
179 | } |
180 | _ => panic!("Unknown syn::Type: {:?}" , self), |
181 | } |
182 | } |
183 | } |
184 | |
185 | impl UsesLifetimes for syn::Fields { |
186 | fn uses_lifetimes<'a>( |
187 | &self, |
188 | options: &Options, |
189 | lifetimes: &'a LifetimeSet, |
190 | ) -> LifetimeRefSet<'a> { |
191 | self.collect_lifetimes(options, lifetimes) |
192 | } |
193 | } |
194 | |
195 | impl UsesLifetimes for syn::TypePath { |
196 | fn uses_lifetimes<'a>( |
197 | &self, |
198 | options: &Options, |
199 | lifetimes: &'a LifetimeSet, |
200 | ) -> LifetimeRefSet<'a> { |
201 | let mut hits: HashSet<&Lifetime, BuildHasherDefault<…>> = self.path.uses_lifetimes(options, lifetimes); |
202 | |
203 | if options.include_type_path_qself() { |
204 | hits.extend(self.qself.uses_lifetimes(options, lifetimes)); |
205 | } |
206 | |
207 | hits |
208 | } |
209 | } |
210 | |
211 | impl UsesLifetimes for syn::ReturnType { |
212 | fn uses_lifetimes<'a>( |
213 | &self, |
214 | options: &Options, |
215 | lifetimes: &'a LifetimeSet, |
216 | ) -> LifetimeRefSet<'a> { |
217 | if let syn::ReturnType::Type(_, ref ty: &Box) = *self { |
218 | ty.uses_lifetimes(options, lifetimes) |
219 | } else { |
220 | Default::default() |
221 | } |
222 | } |
223 | } |
224 | |
225 | impl UsesLifetimes for syn::PathArguments { |
226 | fn uses_lifetimes<'a>( |
227 | &self, |
228 | options: &Options, |
229 | lifetimes: &'a LifetimeSet, |
230 | ) -> LifetimeRefSet<'a> { |
231 | match *self { |
232 | syn::PathArguments::None => Default::default(), |
233 | syn::PathArguments::AngleBracketed(ref v: &AngleBracketedGenericArguments) => v.uses_lifetimes(options, lifetimes), |
234 | syn::PathArguments::Parenthesized(ref v: &ParenthesizedGenericArguments) => v.uses_lifetimes(options, lifetimes), |
235 | } |
236 | } |
237 | } |
238 | |
239 | impl UsesLifetimes for syn::WherePredicate { |
240 | fn uses_lifetimes<'a>( |
241 | &self, |
242 | options: &Options, |
243 | lifetimes: &'a LifetimeSet, |
244 | ) -> LifetimeRefSet<'a> { |
245 | match *self { |
246 | syn::WherePredicate::Type(ref v: &PredicateType) => v.uses_lifetimes(options, lifetimes), |
247 | syn::WherePredicate::Lifetime(ref v: &PredicateLifetime) => v.uses_lifetimes(options, lifetimes), |
248 | syn::WherePredicate::Eq(ref v: &PredicateEq) => v.uses_lifetimes(options, lifetimes), |
249 | } |
250 | } |
251 | } |
252 | |
253 | impl UsesLifetimes for syn::GenericArgument { |
254 | fn uses_lifetimes<'a>( |
255 | &self, |
256 | options: &Options, |
257 | lifetimes: &'a LifetimeSet, |
258 | ) -> LifetimeRefSet<'a> { |
259 | match *self { |
260 | syn::GenericArgument::Type(ref v: &Type) => v.uses_lifetimes(options, lifetimes), |
261 | syn::GenericArgument::Binding(ref v: &Binding) => v.uses_lifetimes(options, lifetimes), |
262 | syn::GenericArgument::Lifetime(ref v: &Lifetime) => v.uses_lifetimes(options, lifetimes), |
263 | syn::GenericArgument::Constraint(ref v: &Constraint) => v.uses_lifetimes(options, lifetimes), |
264 | syn::GenericArgument::Const(_) => Default::default(), |
265 | } |
266 | } |
267 | } |
268 | |
269 | impl UsesLifetimes for syn::TypeParamBound { |
270 | fn uses_lifetimes<'a>( |
271 | &self, |
272 | options: &Options, |
273 | lifetimes: &'a LifetimeSet, |
274 | ) -> LifetimeRefSet<'a> { |
275 | match *self { |
276 | syn::TypeParamBound::Trait(ref v: &TraitBound) => v.uses_lifetimes(options, lifetimes), |
277 | syn::TypeParamBound::Lifetime(ref v: &Lifetime) => v.uses_lifetimes(options, lifetimes), |
278 | } |
279 | } |
280 | } |
281 | |
282 | #[cfg (test)] |
283 | mod tests { |
284 | use proc_macro2::Span; |
285 | use syn::parse_quote; |
286 | use syn::DeriveInput; |
287 | |
288 | use super::UsesLifetimes; |
289 | use crate::usage::GenericsExt; |
290 | use crate::usage::Purpose::*; |
291 | |
292 | #[test ] |
293 | fn struct_named() { |
294 | let input: DeriveInput = parse_quote! { |
295 | struct Foo<'a, 'b: 'a> { |
296 | parent: &'b Bar, |
297 | child: &'a Baz, |
298 | } |
299 | }; |
300 | let omitted = syn::Lifetime::new("'c" , Span::call_site()); |
301 | |
302 | let lifetimes = { |
303 | let mut lt = input.generics.declared_lifetimes(); |
304 | lt.insert(omitted); |
305 | lt |
306 | }; |
307 | |
308 | let matches = input.data.uses_lifetimes(&BoundImpl.into(), &lifetimes); |
309 | assert_eq!(matches.len(), 2); |
310 | } |
311 | |
312 | #[test ] |
313 | fn qself() { |
314 | let input: DeriveInput = parse_quote! { |
315 | struct Foo<'a, 'b: 'a> { |
316 | parent: &'b Bar, |
317 | child: <Bar<'a> as MyIterator>::Item, |
318 | } |
319 | }; |
320 | let lifetimes = input.generics.declared_lifetimes(); |
321 | let matches = input.data.uses_lifetimes(&BoundImpl.into(), &lifetimes); |
322 | assert_eq!(matches.len(), 1); |
323 | |
324 | let decl_matches = input.data.uses_lifetimes(&Declare.into(), &lifetimes); |
325 | assert_eq!(decl_matches.len(), 2); |
326 | } |
327 | } |
328 | |