| 1 | // vim: tw=80 |
| 2 | use super::*; |
| 3 | use syn::parse::{Parse, ParseStream}; |
| 4 | |
| 5 | /// Make any implicit lifetime parameters explicit |
| 6 | fn add_lifetime_parameters(sig: &mut Signature) { |
| 7 | fn add_to_trait_object(generics: &mut Generics, var: &Pat, to: &mut TypeTraitObject) { |
| 8 | let mut has_lifetime = false; |
| 9 | for bound in to.bounds.iter() { |
| 10 | if let TypeParamBound::Lifetime(_) = bound { |
| 11 | has_lifetime = true; |
| 12 | } |
| 13 | } |
| 14 | if ! has_lifetime { |
| 15 | let arg_ident = match *var { |
| 16 | Pat::Wild(_) => { |
| 17 | compile_error(var.span(), |
| 18 | "Mocked methods must have named arguments" ); |
| 19 | format_ident!("dont_care" ) |
| 20 | }, |
| 21 | Pat::Ident(ref pat_ident) => { |
| 22 | if let Some(r) = &pat_ident.by_ref { |
| 23 | compile_error(r.span(), |
| 24 | "Mockall does not support by-reference argument bindings" ); |
| 25 | } |
| 26 | if let Some((_at, subpat)) = &pat_ident.subpat { |
| 27 | compile_error(subpat.span(), |
| 28 | "Mockall does not support subpattern bindings" ); |
| 29 | } |
| 30 | pat_ident.ident.clone() |
| 31 | }, |
| 32 | _ => { |
| 33 | compile_error(var.span(), |
| 34 | "Unsupported argument type" ); |
| 35 | format_ident!("dont_care" ) |
| 36 | } |
| 37 | }; |
| 38 | let s = format!("'__mockall_{}" , arg_ident); |
| 39 | let span = Span::call_site(); |
| 40 | let lt = Lifetime::new(&s, span); |
| 41 | to.bounds.push(TypeParamBound::Lifetime(lt.clone())); |
| 42 | generics.lt_token.get_or_insert(Token); |
| 43 | generics.gt_token.get_or_insert(Token); |
| 44 | let gpl = GenericParam::Lifetime(LifetimeDef::new(lt)); |
| 45 | generics.params.push(gpl); |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | fn add_to_type(generics: &mut Generics, var: &Pat, ty: &mut Type) { |
| 50 | match ty { |
| 51 | Type::Array(ta) => add_to_type(generics, var, ta.elem.as_mut()), |
| 52 | Type::BareFn(_) => (), |
| 53 | Type::ImplTrait(_) => (), |
| 54 | Type::Path(_) => (), |
| 55 | Type::Ptr(_) => (), |
| 56 | Type::Reference(tr) => { |
| 57 | match tr.elem.as_mut() { |
| 58 | Type::Paren(tp) => { |
| 59 | if let Type::TraitObject(to) = tp.elem.as_mut() { |
| 60 | add_to_trait_object(generics, var, to); |
| 61 | } else { |
| 62 | add_to_type(generics, var, tr.elem.as_mut()); |
| 63 | } |
| 64 | }, |
| 65 | Type::TraitObject(to) => { |
| 66 | add_to_trait_object(generics, var, to); |
| 67 | // We need to wrap it in a Paren. Otherwise it won't be |
| 68 | // syntactically valid after we add a lifetime bound, |
| 69 | // due to a "ambiguous `+` in a type" error |
| 70 | *tr.elem = Type::Paren(TypeParen { |
| 71 | paren_token: token::Paren::default(), |
| 72 | elem: Box::new(Type::TraitObject(to.clone())) |
| 73 | }); |
| 74 | }, |
| 75 | _ => add_to_type(generics, var, tr.elem.as_mut()), |
| 76 | } |
| 77 | }, |
| 78 | Type::Slice(ts) => add_to_type(generics, var, ts.elem.as_mut()), |
| 79 | Type::Tuple(tt) => { |
| 80 | for ty in tt.elems.iter_mut() { |
| 81 | add_to_type(generics, var, ty) |
| 82 | } |
| 83 | }, |
| 84 | _ => compile_error(ty.span(), "unsupported type in this position" ) |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | for arg in sig.inputs.iter_mut() { |
| 89 | if let FnArg::Typed(pt) = arg { |
| 90 | add_to_type(&mut sig.generics, &pt.pat, &mut pt.ty) |
| 91 | } |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | /// Generate a #[derive(Debug)] Attribute |
| 96 | fn derive_debug() -> Attribute { |
| 97 | Attribute { |
| 98 | pound_token: <Token![#]>::default(), |
| 99 | style: AttrStyle::Outer, |
| 100 | bracket_token: token::Bracket::default(), |
| 101 | path: Path::from(format_ident!("derive" )), |
| 102 | tokens: quote!((Debug)) |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | /// Add "Mock" to the front of the named type |
| 107 | fn mock_ident_in_type(ty: &mut Type) { |
| 108 | match ty { |
| 109 | Type::Path(type_path) => { |
| 110 | if type_path.path.segments.len() != 1 { |
| 111 | compile_error(type_path.path.span(), |
| 112 | "mockall_derive only supports structs defined in the current module" ); |
| 113 | return; |
| 114 | } |
| 115 | let ident = &mut type_path.path.segments.last_mut().unwrap().ident; |
| 116 | *ident = gen_mock_ident(ident) |
| 117 | }, |
| 118 | x => { |
| 119 | compile_error(x.span(), |
| 120 | "mockall_derive only supports mocking traits and structs" ); |
| 121 | } |
| 122 | }; |
| 123 | } |
| 124 | |
| 125 | /// Performs transformations on the ItemImpl to make it mockable |
| 126 | fn mockable_item_impl(mut impl_: ItemImpl, name: &Ident, generics: &Generics) |
| 127 | -> ItemImpl |
| 128 | { |
| 129 | mock_ident_in_type(&mut impl_.self_ty); |
| 130 | for item in impl_.items.iter_mut() { |
| 131 | if let ImplItem::Method(ref mut iim) = item { |
| 132 | mockable_method(iim, name, generics); |
| 133 | } |
| 134 | } |
| 135 | impl_ |
| 136 | } |
| 137 | |
| 138 | /// Performs transformations on the method to make it mockable |
| 139 | fn mockable_method(meth: &mut ImplItemMethod, name: &Ident, generics: &Generics) |
| 140 | { |
| 141 | demutify(&mut meth.sig.inputs); |
| 142 | deselfify_args(&mut meth.sig.inputs, name, generics); |
| 143 | add_lifetime_parameters(&mut meth.sig); |
| 144 | deimplify(&mut meth.sig.output); |
| 145 | dewhereselfify(&mut meth.sig.generics); |
| 146 | if let ReturnType::Type(_, ty) = &mut meth.sig.output { |
| 147 | deselfify(ty, name, generics); |
| 148 | deanonymize(ty); |
| 149 | } |
| 150 | sanity_check_sig(&meth.sig); |
| 151 | } |
| 152 | |
| 153 | /// Performs transformations on the method to make it mockable |
| 154 | fn mockable_trait_method( |
| 155 | meth: &mut TraitItemMethod, |
| 156 | name: &Ident, |
| 157 | generics: &Generics) |
| 158 | { |
| 159 | demutify(&mut meth.sig.inputs); |
| 160 | deselfify_args(&mut meth.sig.inputs, name, generics); |
| 161 | add_lifetime_parameters(&mut meth.sig); |
| 162 | deimplify(&mut meth.sig.output); |
| 163 | dewhereselfify(&mut meth.sig.generics); |
| 164 | if let ReturnType::Type(_, ty) = &mut meth.sig.output { |
| 165 | deselfify(ty, name, generics); |
| 166 | deanonymize(ty); |
| 167 | } |
| 168 | sanity_check_sig(&meth.sig); |
| 169 | } |
| 170 | |
| 171 | /// Generates a mockable item impl from a trait method definition |
| 172 | fn mockable_trait(trait_: ItemTrait, name: &Ident, generics: &Generics) |
| 173 | -> ItemImpl |
| 174 | { |
| 175 | let items = trait_.items.into_iter() |
| 176 | .map(|ti| { |
| 177 | match ti { |
| 178 | TraitItem::Method(mut tim) => { |
| 179 | mockable_trait_method(&mut tim, name, generics); |
| 180 | ImplItem::Method(tim2iim(tim, &Visibility::Inherited)) |
| 181 | }, |
| 182 | TraitItem::Const(tic) => { |
| 183 | ImplItem::Const(tic2iic(tic, &Visibility::Inherited)) |
| 184 | }, |
| 185 | TraitItem::Type(tit) => { |
| 186 | ImplItem::Type(tit2iit(tit, &Visibility::Inherited)) |
| 187 | }, |
| 188 | _ => { |
| 189 | compile_error(ti.span(), "Unsupported in this context" ); |
| 190 | ImplItem::Verbatim(TokenStream::new()) |
| 191 | } |
| 192 | } |
| 193 | }).collect::<Vec<_>>(); |
| 194 | let mut trait_path = Path::from(trait_.ident); |
| 195 | let mut struct_path = Path::from(name.clone()); |
| 196 | let (_, stg, _) = generics.split_for_impl(); |
| 197 | let (_, ttg, _) = trait_.generics.split_for_impl(); |
| 198 | if let Ok(abga) = parse2::<AngleBracketedGenericArguments>(quote!(#stg)) { |
| 199 | struct_path.segments.last_mut().unwrap().arguments = |
| 200 | PathArguments::AngleBracketed(abga); |
| 201 | } |
| 202 | if let Ok(abga) = parse2::<AngleBracketedGenericArguments>(quote!(#ttg)) { |
| 203 | trait_path.segments.last_mut().unwrap().arguments = |
| 204 | PathArguments::AngleBracketed(abga); |
| 205 | } |
| 206 | let self_ty = Box::new(Type::Path(TypePath{ |
| 207 | qself: None, |
| 208 | path: struct_path, |
| 209 | })); |
| 210 | ItemImpl { |
| 211 | attrs: trait_.attrs, |
| 212 | defaultness: None, |
| 213 | unsafety: trait_.unsafety, |
| 214 | impl_token: <Token![impl]>::default(), |
| 215 | generics: generics.clone(), |
| 216 | trait_: Some((None, trait_path, <Token![for]>::default())), |
| 217 | self_ty, |
| 218 | brace_token: trait_.brace_token, |
| 219 | items |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | fn sanity_check_sig(sig: &Signature) { |
| 224 | for arg in sig.inputs.iter() { |
| 225 | if let FnArg::Typed(pt) = arg { |
| 226 | if let Type::ImplTrait(it) = pt.ty.as_ref() { |
| 227 | let bounds = &it.bounds; |
| 228 | let s = format!( |
| 229 | "Mockall does not support \"impl trait \" in argument position. Use \"T: {} \" instead" , |
| 230 | quote!(#bounds) |
| 231 | ); |
| 232 | compile_error(it.span(), &s); |
| 233 | } |
| 234 | } |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | /// Converts a TraitItemConst into an ImplItemConst |
| 239 | fn tic2iic(tic: TraitItemConst, vis: &syn::Visibility) -> ImplItemConst { |
| 240 | let span = tic.span(); |
| 241 | let (eq_token, expr) = tic.default.unwrap_or_else(|| { |
| 242 | compile_error(span, |
| 243 | "Mocked associated consts must have a default implementation" ); |
| 244 | (<Token![=]>::default(), Expr::Verbatim(TokenStream::new())) |
| 245 | }); |
| 246 | ImplItemConst { |
| 247 | attrs: tic.attrs, |
| 248 | vis: vis.clone(), |
| 249 | defaultness: None, |
| 250 | const_token: tic.const_token, |
| 251 | ident: tic.ident, |
| 252 | colon_token: tic.colon_token, |
| 253 | ty: tic.ty, |
| 254 | eq_token, |
| 255 | expr, |
| 256 | semi_token: tic.semi_token |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | /// Converts a TraitItemMethod into an ImplItemMethod |
| 261 | fn tim2iim(m: syn::TraitItemMethod, vis: &syn::Visibility) |
| 262 | -> syn::ImplItemMethod |
| 263 | { |
| 264 | let empty_block = Block { |
| 265 | brace_token: token::Brace::default(), |
| 266 | stmts: Vec::new() |
| 267 | }; |
| 268 | syn::ImplItemMethod{ |
| 269 | attrs: m.attrs, |
| 270 | vis: vis.clone(), |
| 271 | defaultness: None, |
| 272 | sig: m.sig, |
| 273 | block: empty_block |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | /// Converts a TraitItemType into an ImplItemType |
| 278 | fn tit2iit(tit: TraitItemType, vis: &Visibility) -> ImplItemType { |
| 279 | let span = tit.span(); |
| 280 | let (eq_token, ty) = tit.default.unwrap_or_else(|| { |
| 281 | compile_error(span, |
| 282 | "associated types in mock! must be fully specified" ); |
| 283 | (token::Eq::default(), Type::Verbatim(TokenStream::new())) |
| 284 | }); |
| 285 | ImplItemType { |
| 286 | attrs: tit.attrs, |
| 287 | vis: vis.clone(), |
| 288 | defaultness: None, |
| 289 | type_token: tit.type_token, |
| 290 | ident: tit.ident, |
| 291 | generics: tit.generics, |
| 292 | eq_token, |
| 293 | ty, |
| 294 | semi_token: tit.semi_token, |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | pub(crate) struct MockableStruct { |
| 299 | pub attrs: Vec<Attribute>, |
| 300 | pub consts: Vec<ImplItemConst>, |
| 301 | pub generics: Generics, |
| 302 | /// Inherent methods of the mockable struct |
| 303 | pub methods: Vec<ImplItemMethod>, |
| 304 | pub name: Ident, |
| 305 | pub vis: Visibility, |
| 306 | pub impls: Vec<ItemImpl> |
| 307 | } |
| 308 | |
| 309 | impl MockableStruct { |
| 310 | /// Does this struct derive Debug? |
| 311 | pub fn derives_debug(&self) -> bool { |
| 312 | self.attrs.iter() |
| 313 | .any(|attr| { |
| 314 | if let Ok(Meta::List(ml)) = attr.parse_meta() { |
| 315 | let i = ml.path.get_ident(); |
| 316 | if i.map_or(false, |i| *i == "derive" ) { |
| 317 | ml.nested.iter() |
| 318 | .any(|nm| { |
| 319 | if let NestedMeta::Meta(m) = nm { |
| 320 | let i = m.path().get_ident(); |
| 321 | i.map_or(false, |i| *i == "Debug" ) |
| 322 | } else { |
| 323 | false |
| 324 | } |
| 325 | }) |
| 326 | } else { |
| 327 | false |
| 328 | } |
| 329 | } else { |
| 330 | false |
| 331 | } |
| 332 | }) |
| 333 | } |
| 334 | } |
| 335 | |
| 336 | impl From<(Attrs, ItemTrait)> for MockableStruct { |
| 337 | fn from((attrs, item_trait): (Attrs, ItemTrait)) -> MockableStruct { |
| 338 | let trait_ = attrs.substitute_trait(&item_trait); |
| 339 | let mut attrs = trait_.attrs.clone(); |
| 340 | attrs.push(derive_debug()); |
| 341 | let vis = trait_.vis.clone(); |
| 342 | let name = gen_mock_ident(&trait_.ident); |
| 343 | let generics = trait_.generics.clone(); |
| 344 | let impls = vec![mockable_trait(trait_, &name, &generics)]; |
| 345 | MockableStruct { |
| 346 | attrs, |
| 347 | consts: Vec::new(), |
| 348 | vis, |
| 349 | name, |
| 350 | generics, |
| 351 | methods: Vec::new(), |
| 352 | impls |
| 353 | } |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | impl From<ItemImpl> for MockableStruct { |
| 358 | fn from(mut item_impl: ItemImpl) -> MockableStruct { |
| 359 | let name = match &*item_impl.self_ty { |
| 360 | Type::Path(type_path) => { |
| 361 | let n = find_ident_from_path(&type_path.path).0; |
| 362 | gen_mock_ident(&n) |
| 363 | }, |
| 364 | x => { |
| 365 | compile_error(x.span(), |
| 366 | "mockall_derive only supports mocking traits and structs" ); |
| 367 | Ident::new("" , Span::call_site()) |
| 368 | } |
| 369 | }; |
| 370 | let mut attrs = item_impl.attrs.clone(); |
| 371 | attrs.push(derive_debug()); |
| 372 | let mut consts = Vec::new(); |
| 373 | let generics = item_impl.generics.clone(); |
| 374 | let mut methods = Vec::new(); |
| 375 | let pub_token = Token); |
| 376 | let vis = Visibility::Public(VisPublic{pub_token}); |
| 377 | let mut impls = Vec::new(); |
| 378 | if let Some((bang, _path, _)) = &item_impl.trait_ { |
| 379 | if bang.is_some() { |
| 380 | compile_error(bang.span(), "Unsupported by automock" ); |
| 381 | } |
| 382 | |
| 383 | // Substitute any associated types in this ItemImpl. |
| 384 | // NB: this would not be necessary if the user always fully |
| 385 | // qualified them, e.g. `<Self as MyTrait>::MyType` |
| 386 | let mut attrs = Attrs::default(); |
| 387 | for item in item_impl.items.iter() { |
| 388 | match item { |
| 389 | ImplItem::Const(_iic) => |
| 390 | (), |
| 391 | ImplItem::Method(_meth) => |
| 392 | (), |
| 393 | ImplItem::Type(ty) => { |
| 394 | attrs.attrs.insert(ty.ident.clone(), ty.ty.clone()); |
| 395 | }, |
| 396 | x => compile_error(x.span(), "Unsupported by automock" ) |
| 397 | } |
| 398 | } |
| 399 | attrs.substitute_item_impl(&mut item_impl); |
| 400 | impls.push(mockable_item_impl(item_impl, &name, &generics)); |
| 401 | } else { |
| 402 | for item in item_impl.items.into_iter() { |
| 403 | match item { |
| 404 | ImplItem::Method(mut meth) => { |
| 405 | mockable_method(&mut meth, &name, &item_impl.generics); |
| 406 | methods.push(meth) |
| 407 | }, |
| 408 | ImplItem::Const(iic) => consts.push(iic), |
| 409 | // Rust doesn't allow types in an inherent impl |
| 410 | x => compile_error(x.span(), |
| 411 | "Unsupported by Mockall in this context" ), |
| 412 | } |
| 413 | } |
| 414 | }; |
| 415 | MockableStruct { |
| 416 | attrs, |
| 417 | consts, |
| 418 | generics, |
| 419 | methods, |
| 420 | name, |
| 421 | vis, |
| 422 | impls, |
| 423 | } |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | impl Parse for MockableStruct { |
| 428 | fn parse(input: ParseStream) -> syn::parse::Result<Self> { |
| 429 | let attrs = input.call(syn::Attribute::parse_outer)?; |
| 430 | let vis: syn::Visibility = input.parse()?; |
| 431 | let original_name: syn::Ident = input.parse()?; |
| 432 | let mut generics: syn::Generics = input.parse()?; |
| 433 | let wc: Option<syn::WhereClause> = input.parse()?; |
| 434 | generics.where_clause = wc; |
| 435 | let name = gen_mock_ident(&original_name); |
| 436 | let impl_content; |
| 437 | let _brace_token = braced!(impl_content in input); |
| 438 | let mut consts = Vec::new(); |
| 439 | let mut methods = Vec::new(); |
| 440 | while !impl_content.is_empty() { |
| 441 | let item: ImplItem = impl_content.parse()?; |
| 442 | match item { |
| 443 | ImplItem::Method(mut iim) => { |
| 444 | mockable_method(&mut iim, &name, &generics); |
| 445 | methods.push(iim); |
| 446 | }, |
| 447 | ImplItem::Const(iic) => consts.push(iic), |
| 448 | _ => { |
| 449 | return Err(input.error("Unsupported in this context" )); |
| 450 | } |
| 451 | } |
| 452 | } |
| 453 | |
| 454 | let mut impls = Vec::new(); |
| 455 | while !input.is_empty() { |
| 456 | let item: Item = input.parse()?; |
| 457 | match item { |
| 458 | Item::Trait(it) => { |
| 459 | let note = "Deprecated mock! syntax. Instead of \"trait X \", write \"impl X for Y \". See PR #205" ; |
| 460 | let mut impl_ = mockable_trait(it, &name, &generics); |
| 461 | impl_.attrs.push(Attribute { |
| 462 | pound_token: <token::Pound>::default(), |
| 463 | style: AttrStyle::Outer, |
| 464 | bracket_token: token::Bracket::default(), |
| 465 | path: Path::from(format_ident!("deprecated" )), |
| 466 | tokens: quote!((since = "0.9.0" , note = #note)), |
| 467 | }); |
| 468 | impls.push(impl_) |
| 469 | }, |
| 470 | Item::Impl(ii) => |
| 471 | impls.push(mockable_item_impl(ii, &name, &generics)), |
| 472 | _ => return Err(input.error("Unsupported in this context" )), |
| 473 | } |
| 474 | } |
| 475 | |
| 476 | Ok( |
| 477 | MockableStruct { |
| 478 | attrs, |
| 479 | consts, |
| 480 | generics, |
| 481 | methods, |
| 482 | name, |
| 483 | vis, |
| 484 | impls |
| 485 | } |
| 486 | ) |
| 487 | } |
| 488 | } |
| 489 | |
| 490 | #[cfg (test)] |
| 491 | mod t { |
| 492 | use super::*; |
| 493 | |
| 494 | mod add_lifetime_parameters { |
| 495 | use super::*; |
| 496 | |
| 497 | #[test] |
| 498 | fn array() { |
| 499 | let mut meth: TraitItemMethod = parse2(quote!( |
| 500 | fn foo(&self, x: [&dyn T; 1]); |
| 501 | )).unwrap(); |
| 502 | add_lifetime_parameters(&mut meth.sig); |
| 503 | assert_eq!( |
| 504 | quote!(fn foo<'__mockall_x>(&self, x: [&(dyn T + '__mockall_x); 1]);) |
| 505 | .to_string(), |
| 506 | quote!(#meth).to_string() |
| 507 | ); |
| 508 | } |
| 509 | |
| 510 | #[test] |
| 511 | fn bare_fn_with_named_args() { |
| 512 | let mut meth: TraitItemMethod = parse2(quote!( |
| 513 | fn foo(&self, x: fn(&dyn T)); |
| 514 | )).unwrap(); |
| 515 | add_lifetime_parameters(&mut meth.sig); |
| 516 | assert_eq!( |
| 517 | quote!(fn foo(&self, x: fn(&dyn T));).to_string(), |
| 518 | quote!(#meth).to_string() |
| 519 | ); |
| 520 | } |
| 521 | |
| 522 | #[test] |
| 523 | fn plain() { |
| 524 | let mut meth: TraitItemMethod = parse2(quote!( |
| 525 | fn foo(&self, x: &dyn T); |
| 526 | )).unwrap(); |
| 527 | add_lifetime_parameters(&mut meth.sig); |
| 528 | assert_eq!( |
| 529 | quote!(fn foo<'__mockall_x>(&self, x: &(dyn T + '__mockall_x));) |
| 530 | .to_string(), |
| 531 | quote!(#meth).to_string() |
| 532 | ); |
| 533 | } |
| 534 | |
| 535 | #[test] |
| 536 | fn slice() { |
| 537 | let mut meth: TraitItemMethod = parse2(quote!( |
| 538 | fn foo(&self, x: &[&dyn T]); |
| 539 | )).unwrap(); |
| 540 | add_lifetime_parameters(&mut meth.sig); |
| 541 | assert_eq!( |
| 542 | quote!(fn foo<'__mockall_x>(&self, x: &[&(dyn T + '__mockall_x)]);) |
| 543 | .to_string(), |
| 544 | quote!(#meth).to_string() |
| 545 | ); |
| 546 | } |
| 547 | |
| 548 | #[test] |
| 549 | fn tuple() { |
| 550 | let mut meth: TraitItemMethod = parse2(quote!( |
| 551 | fn foo(&self, x: (&dyn T, u32)); |
| 552 | )).unwrap(); |
| 553 | add_lifetime_parameters(&mut meth.sig); |
| 554 | assert_eq!( |
| 555 | quote!(fn foo<'__mockall_x>(&self, x: (&(dyn T + '__mockall_x), u32));) |
| 556 | .to_string(), |
| 557 | quote!(#meth).to_string() |
| 558 | ); |
| 559 | } |
| 560 | |
| 561 | #[test] |
| 562 | fn with_anonymous_lifetime() { |
| 563 | let mut meth: TraitItemMethod = parse2(quote!( |
| 564 | fn foo(&self, x: &(dyn T + '_)); |
| 565 | )).unwrap(); |
| 566 | add_lifetime_parameters(&mut meth.sig); |
| 567 | assert_eq!( |
| 568 | quote!(fn foo(&self, x: &(dyn T + '_));).to_string(), |
| 569 | quote!(#meth).to_string() |
| 570 | ); |
| 571 | } |
| 572 | |
| 573 | #[test] |
| 574 | fn with_parens() { |
| 575 | let mut meth: TraitItemMethod = parse2(quote!( |
| 576 | fn foo(&self, x: &(dyn T)); |
| 577 | )).unwrap(); |
| 578 | add_lifetime_parameters(&mut meth.sig); |
| 579 | assert_eq!( |
| 580 | quote!(fn foo<'__mockall_x>(&self, x: &(dyn T + '__mockall_x));) |
| 581 | .to_string(), |
| 582 | quote!(#meth).to_string() |
| 583 | ); |
| 584 | } |
| 585 | |
| 586 | #[test] |
| 587 | fn with_lifetime_parameter() { |
| 588 | let mut meth: TraitItemMethod = parse2(quote!( |
| 589 | fn foo<'a>(&self, x: &(dyn T + 'a)); |
| 590 | )).unwrap(); |
| 591 | add_lifetime_parameters(&mut meth.sig); |
| 592 | assert_eq!( |
| 593 | quote!(fn foo<'a>(&self, x: &(dyn T + 'a));).to_string(), |
| 594 | quote!(#meth).to_string() |
| 595 | ); |
| 596 | } |
| 597 | |
| 598 | #[test] |
| 599 | fn with_static_lifetime() { |
| 600 | let mut meth: TraitItemMethod = parse2(quote!( |
| 601 | fn foo(&self, x: &(dyn T + 'static)); |
| 602 | )).unwrap(); |
| 603 | add_lifetime_parameters(&mut meth.sig); |
| 604 | assert_eq!( |
| 605 | quote!(fn foo(&self, x: &(dyn T + 'static));).to_string(), |
| 606 | quote!(#meth).to_string() |
| 607 | ); |
| 608 | } |
| 609 | |
| 610 | } |
| 611 | |
| 612 | mod sanity_check_sig { |
| 613 | use super::*; |
| 614 | |
| 615 | #[test] |
| 616 | #[should_panic (expected = "Mockall does not support \"impl trait \" in argument position. Use \"T: SomeTrait \" instead." )] |
| 617 | fn impl_trait() { |
| 618 | let meth: ImplItemMethod = parse2(quote!( |
| 619 | fn foo(&self, x: impl SomeTrait); |
| 620 | )).unwrap(); |
| 621 | sanity_check_sig(&meth.sig); |
| 622 | } |
| 623 | } |
| 624 | } |
| 625 | |