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