1 | // vim: tw=80 |
2 | //! Proc Macros for use with Mockall |
3 | //! |
4 | //! You probably don't want to use this crate directly. Instead, you should use |
5 | //! its reexports via the [`mockall`](https://docs.rs/mockall/latest/mockall) |
6 | //! crate. |
7 | |
8 | #![cfg_attr (feature = "nightly_derive" , feature(proc_macro_diagnostic))] |
9 | #![cfg_attr (test, deny(warnings))] |
10 | |
11 | use cfg_if::cfg_if; |
12 | use proc_macro2::{Span, TokenStream}; |
13 | use quote::{ToTokens, format_ident, quote}; |
14 | use std::{ |
15 | env, |
16 | hash::BuildHasherDefault |
17 | }; |
18 | use syn::{ |
19 | *, |
20 | punctuated::Punctuated, |
21 | spanned::Spanned |
22 | }; |
23 | |
24 | mod automock; |
25 | mod mock_function; |
26 | mod mock_item; |
27 | mod mock_item_struct; |
28 | mod mock_trait; |
29 | mod mockable_item; |
30 | mod mockable_struct; |
31 | use crate::automock::Attrs; |
32 | use crate::mockable_struct::MockableStruct; |
33 | use crate::mock_item::MockItem; |
34 | use crate::mock_item_struct::MockItemStruct; |
35 | use crate::mockable_item::MockableItem; |
36 | |
37 | // Define deterministic aliases for these common types. |
38 | type HashMap<K, V> = std::collections::HashMap<K, V, BuildHasherDefault<std::collections::hash_map::DefaultHasher>>; |
39 | type HashSet<K> = std::collections::HashSet<K, BuildHasherDefault<std::collections::hash_map::DefaultHasher>>; |
40 | |
41 | cfg_if! { |
42 | // proc-macro2's Span::unstable method requires the nightly feature, and it |
43 | // doesn't work in test mode. |
44 | // https://github.com/alexcrichton/proc-macro2/issues/159 |
45 | if #[cfg(all(feature = "nightly_derive" , not(test)))] { |
46 | fn compile_error(span: Span, msg: &str) { |
47 | span.unstable() |
48 | .error(msg) |
49 | .emit(); |
50 | } |
51 | } else { |
52 | fn compile_error(_span: Span, msg: &str) { |
53 | panic!("{}. More information may be available when mockall is built with the \"nightly \" feature." , msg); |
54 | } |
55 | } |
56 | } |
57 | |
58 | fn deanonymize_lifetime(lt: &mut Lifetime) { |
59 | if lt.ident == "_" { |
60 | lt.ident = format_ident!("static" ); |
61 | } |
62 | } |
63 | |
64 | fn deanonymize_path(path: &mut Path) { |
65 | for seg in path.segments.iter_mut() { |
66 | match &mut seg.arguments { |
67 | PathArguments::None => (), |
68 | PathArguments::AngleBracketed(abga) => { |
69 | for ga in abga.args.iter_mut() { |
70 | if let GenericArgument::Lifetime(lt) = ga { |
71 | deanonymize_lifetime(lt) |
72 | } |
73 | } |
74 | }, |
75 | _ => compile_error(seg.arguments.span(), |
76 | "Methods returning functions are TODO" ), |
77 | } |
78 | } |
79 | } |
80 | |
81 | /// Replace any references to the anonymous lifetime `'_` with `'static`. |
82 | fn deanonymize(literal_type: &mut Type) { |
83 | match literal_type { |
84 | Type::Array(ta) => deanonymize(ta.elem.as_mut()), |
85 | Type::BareFn(tbf) => { |
86 | if let ReturnType::Type(_, ref mut bt) = tbf.output { |
87 | deanonymize(bt.as_mut()); |
88 | } |
89 | for input in tbf.inputs.iter_mut() { |
90 | deanonymize(&mut input.ty); |
91 | } |
92 | }, |
93 | Type::Group(tg) => deanonymize(tg.elem.as_mut()), |
94 | Type::Infer(_) => (), |
95 | Type::Never(_) => (), |
96 | Type::Paren(tp) => deanonymize(tp.elem.as_mut()), |
97 | Type::Path(tp) => { |
98 | if let Some(ref mut qself) = tp.qself { |
99 | deanonymize(qself.ty.as_mut()); |
100 | } |
101 | deanonymize_path(&mut tp.path); |
102 | }, |
103 | Type::Ptr(tptr) => deanonymize(tptr.elem.as_mut()), |
104 | Type::Reference(tr) => { |
105 | if let Some(lt) = tr.lifetime.as_mut() { |
106 | deanonymize_lifetime(lt) |
107 | } |
108 | deanonymize(tr.elem.as_mut()); |
109 | }, |
110 | Type::Slice(s) => deanonymize(s.elem.as_mut()), |
111 | Type::TraitObject(tto) => { |
112 | for tpb in tto.bounds.iter_mut() { |
113 | match tpb { |
114 | TypeParamBound::Trait(tb) => deanonymize_path(&mut tb.path), |
115 | TypeParamBound::Lifetime(lt) => deanonymize_lifetime(lt), |
116 | } |
117 | } |
118 | }, |
119 | Type::Tuple(tt) => { |
120 | for ty in tt.elems.iter_mut() { |
121 | deanonymize(ty) |
122 | } |
123 | } |
124 | x => compile_error(x.span(), "Unimplemented type for deanonymize" ) |
125 | } |
126 | } |
127 | |
128 | // If there are any closures in the argument list, turn them into boxed |
129 | // functions |
130 | fn declosurefy(gen: &Generics, args: &Punctuated<FnArg, Token![,]>) -> |
131 | (Generics, Vec<FnArg>, Vec<TokenStream>) |
132 | { |
133 | let mut hm = HashMap::default(); |
134 | |
135 | let mut save_fn_types = |ident: &Ident, tpb: &TypeParamBound| { |
136 | if let TypeParamBound::Trait(tb) = tpb { |
137 | let fident = &tb.path.segments.last().unwrap().ident; |
138 | if ["Fn" , "FnMut" , "FnOnce" ].iter().any(|s| fident == *s) { |
139 | let newty: Type = parse2(quote!(Box<dyn #tb>)).unwrap(); |
140 | let subst_ty: Type = parse2(quote!(#ident)).unwrap(); |
141 | assert!(hm.insert(subst_ty, newty).is_none(), |
142 | "A generic parameter had two Fn bounds?" ); |
143 | } |
144 | } |
145 | }; |
146 | |
147 | // First, build a HashMap of all Fn generic types |
148 | for g in gen.params.iter() { |
149 | if let GenericParam::Type(tp) = g { |
150 | for tpb in tp.bounds.iter() { |
151 | save_fn_types(&tp.ident, tpb); |
152 | } |
153 | } |
154 | } |
155 | if let Some(wc) = &gen.where_clause { |
156 | for pred in wc.predicates.iter() { |
157 | if let WherePredicate::Type(pt) = pred { |
158 | let bounded_ty = &pt.bounded_ty; |
159 | if let Ok(ident) = parse2::<Ident>(quote!(#bounded_ty)) { |
160 | for tpb in pt.bounds.iter() { |
161 | save_fn_types(&ident, tpb); |
162 | } |
163 | } else { |
164 | // We can't yet handle where clauses this complicated |
165 | } |
166 | } |
167 | } |
168 | } |
169 | |
170 | // Then remove those types from both the Generics' params and where clause |
171 | let should_remove = |ident: &Ident| { |
172 | let ty: Type = parse2(quote!(#ident)).unwrap(); |
173 | hm.contains_key(&ty) |
174 | }; |
175 | let params = gen.params.iter() |
176 | .filter(|g| { |
177 | if let GenericParam::Type(tp) = g { |
178 | !should_remove(&tp.ident) |
179 | } else { |
180 | true |
181 | } |
182 | }).cloned() |
183 | .collect::<Punctuated<_, _>>(); |
184 | let mut wc2 = gen.where_clause.clone(); |
185 | if let Some(wc) = &mut wc2 { |
186 | wc.predicates = wc.predicates.iter() |
187 | .filter(|wp| { |
188 | if let WherePredicate::Type(pt) = wp { |
189 | let bounded_ty = &pt.bounded_ty; |
190 | if let Ok(ident) = parse2::<Ident>(quote!(#bounded_ty)) { |
191 | !should_remove(&ident) |
192 | } else { |
193 | // We can't yet handle where clauses this complicated |
194 | true |
195 | } |
196 | } else { |
197 | true |
198 | } |
199 | }).cloned() |
200 | .collect::<Punctuated<_, _>>(); |
201 | if wc.predicates.is_empty() { |
202 | wc2 = None; |
203 | } |
204 | } |
205 | let outg = Generics { |
206 | lt_token: if params.is_empty() { None } else { gen.lt_token }, |
207 | gt_token: if params.is_empty() { None } else { gen.gt_token }, |
208 | params, |
209 | where_clause: wc2 |
210 | }; |
211 | |
212 | // Next substitute Box<Fn> into the arguments |
213 | let outargs = args.iter().map(|arg| { |
214 | if let FnArg::Typed(pt) = arg { |
215 | let mut immutable_pt = pt.clone(); |
216 | demutify_arg(&mut immutable_pt); |
217 | if let Some(newty) = hm.get(&pt.ty) { |
218 | FnArg::Typed(PatType { |
219 | attrs: Vec::default(), |
220 | pat: immutable_pt.pat, |
221 | colon_token: pt.colon_token, |
222 | ty: Box::new(newty.clone()) |
223 | }) |
224 | } else { |
225 | FnArg::Typed(PatType { |
226 | attrs: Vec::default(), |
227 | pat: immutable_pt.pat, |
228 | colon_token: pt.colon_token, |
229 | ty: pt.ty.clone() |
230 | }) |
231 | } |
232 | } else { |
233 | arg.clone() |
234 | } |
235 | }).collect(); |
236 | |
237 | // Finally, Box any closure arguments |
238 | // use filter_map to remove the &self argument |
239 | let callargs = args.iter().filter_map(|arg| { |
240 | match arg { |
241 | FnArg::Typed(pt) => { |
242 | let mut pt2 = pt.clone(); |
243 | demutify_arg(&mut pt2); |
244 | let pat = &pt2.pat; |
245 | if pat_is_self(pat) { |
246 | None |
247 | } else if hm.contains_key(&pt.ty) { |
248 | Some(quote!(Box::new(#pat))) |
249 | } else { |
250 | Some(quote!(#pat)) |
251 | } |
252 | }, |
253 | FnArg::Receiver(_) => None, |
254 | } |
255 | }).collect(); |
256 | (outg, outargs, callargs) |
257 | } |
258 | |
259 | /// Replace any "impl trait" types with "Box<dyn trait>" or equivalent. |
260 | fn deimplify(rt: &mut ReturnType) { |
261 | if let ReturnType::Type(_, ty) = rt { |
262 | if let Type::ImplTrait(ref tit) = &**ty { |
263 | let needs_pin = tit.bounds |
264 | .iter() |
265 | .any(|tpb| { |
266 | if let TypeParamBound::Trait(tb) = tpb { |
267 | if let Some(seg) = tb.path.segments.last() { |
268 | seg.ident == "Future" || seg.ident == "Stream" |
269 | } else { |
270 | // It might still be a Future, but we can't guess |
271 | // what names it might be imported under. Too bad. |
272 | false |
273 | } |
274 | } else { |
275 | false |
276 | } |
277 | }); |
278 | let bounds = &tit.bounds; |
279 | if needs_pin { |
280 | *ty = parse2(quote!(::std::pin::Pin<Box<dyn #bounds>>)).unwrap(); |
281 | } else { |
282 | *ty = parse2(quote!(Box<dyn #bounds>)).unwrap(); |
283 | } |
284 | } |
285 | } |
286 | } |
287 | |
288 | /// Remove any generics that place constraints on Self. |
289 | fn dewhereselfify(generics: &mut Generics) { |
290 | if let Some(ref mut wc) = &mut generics.where_clause { |
291 | let new_predicates = wc.predicates.iter() |
292 | .filter(|wp| match wp { |
293 | WherePredicate::Type(pt) => { |
294 | pt.bounded_ty != parse2(quote!(Self)).unwrap() |
295 | }, |
296 | _ => true |
297 | }).cloned() |
298 | .collect::<Punctuated<WherePredicate, Token![,]>>(); |
299 | wc.predicates = new_predicates; |
300 | } |
301 | if generics.where_clause.as_ref() |
302 | .map(|wc| wc.predicates.is_empty()) |
303 | .unwrap_or(false) |
304 | { |
305 | generics.where_clause = None; |
306 | } |
307 | } |
308 | |
309 | /// Remove any mutability qualifiers from a method's argument list |
310 | fn demutify(inputs: &mut Punctuated<FnArg, token::Comma>) { |
311 | for arg in inputs.iter_mut() { |
312 | match arg { |
313 | FnArg::Receiver(r) => if r.reference.is_none() { |
314 | r.mutability = None |
315 | }, |
316 | FnArg::Typed(pt) => demutify_arg(pt), |
317 | } |
318 | } |
319 | } |
320 | |
321 | /// Remove any "mut" from a method argument's binding. |
322 | fn demutify_arg(arg: &mut PatType) { |
323 | match *arg.pat { |
324 | Pat::Wild(_) => { |
325 | compile_error(arg.span(), |
326 | "Mocked methods must have named arguments" ); |
327 | }, |
328 | Pat::Ident(ref mut pat_ident) => { |
329 | if let Some(r) = &pat_ident.by_ref { |
330 | compile_error(r.span(), |
331 | "Mockall does not support by-reference argument bindings" ); |
332 | } |
333 | if let Some((_at, subpat)) = &pat_ident.subpat { |
334 | compile_error(subpat.span(), |
335 | "Mockall does not support subpattern bindings" ); |
336 | } |
337 | pat_ident.mutability = None; |
338 | }, |
339 | _ => { |
340 | compile_error(arg.span(), "Unsupported argument type" ); |
341 | } |
342 | }; |
343 | } |
344 | |
345 | fn deselfify_path(path: &mut Path, actual: &Ident, generics: &Generics) { |
346 | for seg in path.segments.iter_mut() { |
347 | if seg.ident == "Self" { |
348 | seg.ident = actual.clone(); |
349 | if let PathArguments::None = seg.arguments { |
350 | if !generics.params.is_empty() { |
351 | let args = generics.params.iter() |
352 | .map(|gp| { |
353 | match gp { |
354 | GenericParam::Type(tp) => { |
355 | let ident = tp.ident.clone(); |
356 | GenericArgument::Type( |
357 | Type::Path( |
358 | TypePath { |
359 | qself: None, |
360 | path: Path::from(ident) |
361 | } |
362 | ) |
363 | ) |
364 | }, |
365 | GenericParam::Lifetime(ld) =>{ |
366 | GenericArgument::Lifetime( |
367 | ld.lifetime.clone() |
368 | ) |
369 | } |
370 | _ => unimplemented!(), |
371 | } |
372 | }).collect::<Punctuated<_, _>>(); |
373 | seg.arguments = PathArguments::AngleBracketed( |
374 | AngleBracketedGenericArguments { |
375 | colon2_token: None, |
376 | lt_token: generics.lt_token.unwrap(), |
377 | args, |
378 | gt_token: generics.gt_token.unwrap(), |
379 | } |
380 | ); |
381 | } |
382 | } else { |
383 | compile_error(seg.arguments.span(), |
384 | "Type arguments after Self are unexpected" ); |
385 | } |
386 | } |
387 | if let PathArguments::AngleBracketed(abga) = &mut seg.arguments |
388 | { |
389 | for arg in abga.args.iter_mut() { |
390 | match arg { |
391 | GenericArgument::Type(ty) => |
392 | deselfify(ty, actual, generics), |
393 | GenericArgument::Binding(b) => |
394 | deselfify(&mut b.ty, actual, generics), |
395 | _ => /* Nothing to do */(), |
396 | } |
397 | } |
398 | } |
399 | } |
400 | } |
401 | |
402 | /// Replace any references to `Self` in `literal_type` with `actual`. |
403 | /// `generics` is the Generics field of the parent struct. Useful for |
404 | /// constructor methods. |
405 | fn deselfify(literal_type: &mut Type, actual: &Ident, generics: &Generics) { |
406 | match literal_type { |
407 | Type::Slice(s) => { |
408 | deselfify(s.elem.as_mut(), actual, generics); |
409 | }, |
410 | Type::Array(a) => { |
411 | deselfify(a.elem.as_mut(), actual, generics); |
412 | }, |
413 | Type::Ptr(p) => { |
414 | deselfify(p.elem.as_mut(), actual, generics); |
415 | }, |
416 | Type::Reference(r) => { |
417 | deselfify(r.elem.as_mut(), actual, generics); |
418 | }, |
419 | Type::Tuple(tuple) => { |
420 | for elem in tuple.elems.iter_mut() { |
421 | deselfify(elem, actual, generics); |
422 | } |
423 | } |
424 | Type::Path(type_path) => { |
425 | if let Some(ref mut qself) = type_path.qself { |
426 | deselfify(qself.ty.as_mut(), actual, generics); |
427 | } |
428 | deselfify_path(&mut type_path.path, actual, generics); |
429 | }, |
430 | Type::Paren(p) => { |
431 | deselfify(p.elem.as_mut(), actual, generics); |
432 | }, |
433 | Type::Group(g) => { |
434 | deselfify(g.elem.as_mut(), actual, generics); |
435 | }, |
436 | Type::Macro(_) | Type::Verbatim(_) => { |
437 | compile_error(literal_type.span(), |
438 | "mockall_derive does not support this type as a return argument" ); |
439 | }, |
440 | Type::TraitObject(tto) => { |
441 | // Change types like `dyn Self` into `dyn MockXXX`. |
442 | for bound in tto.bounds.iter_mut() { |
443 | if let TypeParamBound::Trait(t) = bound { |
444 | deselfify_path(&mut t.path, actual, generics); |
445 | } |
446 | } |
447 | }, |
448 | Type::ImplTrait(_) => { |
449 | /* Should've already been flagged as a compile_error */ |
450 | }, |
451 | Type::BareFn(_) => { |
452 | /* Bare functions can't have Self arguments. Nothing to do */ |
453 | }, |
454 | Type::Infer(_) | Type::Never(_) => |
455 | { |
456 | /* Nothing to do */ |
457 | }, |
458 | _ => compile_error(literal_type.span(), "Unsupported type" ), |
459 | } |
460 | } |
461 | |
462 | /// Change any `Self` in a method's arguments' types with `actual`. |
463 | /// `generics` is the Generics field of the parent struct. |
464 | fn deselfify_args( |
465 | args: &mut Punctuated<FnArg, Token![,]>, |
466 | actual: &Ident, |
467 | generics: &Generics) |
468 | { |
469 | for arg in args.iter_mut() { |
470 | if let FnArg::Typed(pt) = arg { |
471 | deselfify(pt.ty.as_mut(), actual, generics) |
472 | } |
473 | } |
474 | } |
475 | |
476 | fn find_ident_from_path(path: &Path) -> (Ident, PathArguments) { |
477 | if path.segments.len() != 1 { |
478 | compile_error(path.span(), |
479 | "mockall_derive only supports structs defined in the current module" ); |
480 | return (Ident::new("" , path.span()), PathArguments::None); |
481 | } |
482 | let last_seg = path.segments.last().unwrap(); |
483 | (last_seg.ident.clone(), last_seg.arguments.clone()) |
484 | } |
485 | |
486 | fn find_lifetimes_in_tpb(bound: &TypeParamBound) -> HashSet<Lifetime> { |
487 | let mut ret = HashSet::default(); |
488 | match bound { |
489 | TypeParamBound::Lifetime(lt) => { |
490 | ret.insert(lt.clone()); |
491 | }, |
492 | TypeParamBound::Trait(tb) => { |
493 | ret.extend(find_lifetimes_in_path(&tb.path)); |
494 | }, |
495 | }; |
496 | ret |
497 | } |
498 | |
499 | fn find_lifetimes_in_path(path: &Path) -> HashSet<Lifetime> { |
500 | let mut ret = HashSet::default(); |
501 | for seg in path.segments.iter() { |
502 | if let PathArguments::AngleBracketed(abga) = &seg.arguments { |
503 | for arg in abga.args.iter() { |
504 | match arg { |
505 | GenericArgument::Lifetime(lt) => { |
506 | ret.insert(lt.clone()); |
507 | }, |
508 | GenericArgument::Type(ty) => { |
509 | ret.extend(find_lifetimes(ty)); |
510 | }, |
511 | GenericArgument::Binding(b) => { |
512 | ret.extend(find_lifetimes(&b.ty)); |
513 | }, |
514 | GenericArgument::Constraint(c) => { |
515 | for bound in c.bounds.iter() { |
516 | ret.extend(find_lifetimes_in_tpb(bound)); |
517 | } |
518 | }, |
519 | GenericArgument::Const(_) => () |
520 | } |
521 | } |
522 | } |
523 | } |
524 | ret |
525 | } |
526 | |
527 | fn find_lifetimes(ty: &Type) -> HashSet<Lifetime> { |
528 | match ty { |
529 | Type::Array(ta) => find_lifetimes(ta.elem.as_ref()), |
530 | Type::Group(tg) => find_lifetimes(tg.elem.as_ref()), |
531 | Type::Infer(_ti) => HashSet::default(), |
532 | Type::Never(_tn) => HashSet::default(), |
533 | Type::Paren(tp) => find_lifetimes(tp.elem.as_ref()), |
534 | Type::Path(tp) => { |
535 | let mut ret = find_lifetimes_in_path(&tp.path); |
536 | if let Some(qs) = &tp.qself { |
537 | ret.extend(find_lifetimes(qs.ty.as_ref())); |
538 | } |
539 | ret |
540 | }, |
541 | Type::Ptr(tp) => find_lifetimes(tp.elem.as_ref()), |
542 | Type::Reference(tr) => { |
543 | let mut ret = find_lifetimes(tr.elem.as_ref()); |
544 | if let Some(lt) = &tr.lifetime { |
545 | ret.insert(lt.clone()); |
546 | } |
547 | ret |
548 | }, |
549 | Type::Slice(ts) => find_lifetimes(ts.elem.as_ref()), |
550 | Type::TraitObject(tto) => { |
551 | let mut ret = HashSet::default(); |
552 | for bound in tto.bounds.iter() { |
553 | ret.extend(find_lifetimes_in_tpb(bound)); |
554 | } |
555 | ret |
556 | } |
557 | Type::Tuple(tt) => { |
558 | let mut ret = HashSet::default(); |
559 | for ty in tt.elems.iter() { |
560 | ret.extend(find_lifetimes(ty)); |
561 | } |
562 | ret |
563 | }, |
564 | Type::ImplTrait(tit) => { |
565 | let mut ret = HashSet::default(); |
566 | for tpb in tit.bounds.iter() { |
567 | ret.extend(find_lifetimes_in_tpb(tpb)); |
568 | } |
569 | ret |
570 | }, |
571 | _ => { |
572 | compile_error(ty.span(), "unsupported type in this context" ); |
573 | HashSet::default() |
574 | } |
575 | } |
576 | } |
577 | |
578 | |
579 | struct AttrFormatter<'a>{ |
580 | attrs: &'a [Attribute], |
581 | async_trait: bool, |
582 | doc: bool, |
583 | } |
584 | |
585 | impl<'a> AttrFormatter<'a> { |
586 | fn new(attrs: &'a [Attribute]) -> AttrFormatter<'a> { |
587 | Self { |
588 | attrs, |
589 | async_trait: true, |
590 | doc: true |
591 | } |
592 | } |
593 | |
594 | fn async_trait(&mut self, allowed: bool) -> &mut Self { |
595 | self.async_trait = allowed; |
596 | self |
597 | } |
598 | |
599 | fn doc(&mut self, allowed: bool) -> &mut Self { |
600 | self.doc = allowed; |
601 | self |
602 | } |
603 | |
604 | // XXX This logic requires that attributes are imported with their |
605 | // standard names. |
606 | #[allow (clippy::needless_bool)] |
607 | #[allow (clippy::if_same_then_else)] |
608 | fn format(&mut self) -> Vec<Attribute> { |
609 | self.attrs.iter() |
610 | .cloned() |
611 | .filter(|attr| { |
612 | let i = attr.path.get_ident(); |
613 | if i.is_none() { |
614 | false |
615 | } else if *i.as_ref().unwrap() == "derive" { |
616 | // We can't usefully derive any traits. Ignore them |
617 | false |
618 | } else if *i.as_ref().unwrap() == "doc" { |
619 | self.doc |
620 | } else if *i.as_ref().unwrap() == "async_trait" { |
621 | self.async_trait |
622 | } else if *i.as_ref().unwrap() == "instrument" { |
623 | // We can't usefully instrument the mock method, so just |
624 | // ignore this attribute. |
625 | // https://docs.rs/tracing/0.1.23/tracing/attr.instrument.html |
626 | false |
627 | } else { |
628 | true |
629 | } |
630 | }).collect() |
631 | } |
632 | } |
633 | |
634 | /// Determine if this Pat is any kind of `self` binding |
635 | fn pat_is_self(pat: &Pat) -> bool { |
636 | if let Pat::Ident(pi) = pat { |
637 | pi.ident == "self" |
638 | } else { |
639 | false |
640 | } |
641 | } |
642 | |
643 | /// Add `levels` `super::` to the path. Return the number of levels added. |
644 | fn supersuperfy_path(path: &mut Path, levels: usize) -> usize { |
645 | if let Some(t) = path.segments.last_mut() { |
646 | match &mut t.arguments { |
647 | PathArguments::None => (), |
648 | PathArguments::AngleBracketed(ref mut abga) => { |
649 | for arg in abga.args.iter_mut() { |
650 | match arg { |
651 | GenericArgument::Type(ref mut ty) => { |
652 | *ty = supersuperfy(ty, levels); |
653 | }, |
654 | GenericArgument::Binding(ref mut binding) => { |
655 | binding.ty = supersuperfy(&binding.ty, levels); |
656 | }, |
657 | GenericArgument::Constraint(ref mut constraint) => { |
658 | supersuperfy_bounds(&mut constraint.bounds, levels); |
659 | }, |
660 | _ => (), |
661 | } |
662 | } |
663 | }, |
664 | PathArguments::Parenthesized(ref mut pga) => { |
665 | for input in pga.inputs.iter_mut() { |
666 | *input = supersuperfy(input, levels); |
667 | } |
668 | if let ReturnType::Type(_, ref mut ty) = pga.output { |
669 | *ty = Box::new(supersuperfy(ty, levels)); |
670 | } |
671 | }, |
672 | } |
673 | } |
674 | if let Some(t) = path.segments.first() { |
675 | if t.ident == "super" { |
676 | let mut ident = format_ident!("super" ); |
677 | ident.set_span(path.segments.span()); |
678 | let ps = PathSegment { |
679 | ident, |
680 | arguments: PathArguments::None |
681 | }; |
682 | for _ in 0..levels { |
683 | path.segments.insert(0, ps.clone()); |
684 | } |
685 | levels |
686 | } else { |
687 | 0 |
688 | } |
689 | } else { |
690 | 0 |
691 | } |
692 | } |
693 | |
694 | /// Replace any references to `super::X` in `original` with `super::super::X`. |
695 | fn supersuperfy(original: &Type, levels: usize) -> Type { |
696 | let mut output = original.clone(); |
697 | fn recurse(t: &mut Type, levels: usize) { |
698 | match t { |
699 | Type::Slice(s) => { |
700 | recurse(s.elem.as_mut(), levels); |
701 | }, |
702 | Type::Array(a) => { |
703 | recurse(a.elem.as_mut(), levels); |
704 | }, |
705 | Type::Ptr(p) => { |
706 | recurse(p.elem.as_mut(), levels); |
707 | }, |
708 | Type::Reference(r) => { |
709 | recurse(r.elem.as_mut(), levels); |
710 | }, |
711 | Type::BareFn(bfn) => { |
712 | if let ReturnType::Type(_, ref mut bt) = bfn.output { |
713 | recurse(bt.as_mut(), levels); |
714 | } |
715 | for input in bfn.inputs.iter_mut() { |
716 | recurse(&mut input.ty, levels); |
717 | } |
718 | }, |
719 | Type::Tuple(tuple) => { |
720 | for elem in tuple.elems.iter_mut() { |
721 | recurse(elem, levels); |
722 | } |
723 | } |
724 | Type::Path(type_path) => { |
725 | let added = supersuperfy_path(&mut type_path.path, levels); |
726 | if let Some(ref mut qself) = type_path.qself { |
727 | recurse(qself.ty.as_mut(), levels); |
728 | qself.position += added; |
729 | } |
730 | }, |
731 | Type::Paren(p) => { |
732 | recurse(p.elem.as_mut(), levels); |
733 | }, |
734 | Type::Group(g) => { |
735 | recurse(g.elem.as_mut(), levels); |
736 | }, |
737 | Type::Macro(_) | Type::Verbatim(_) => { |
738 | compile_error(t.span(), |
739 | "mockall_derive does not support this type in this position" ); |
740 | }, |
741 | Type::TraitObject(tto) => { |
742 | for bound in tto.bounds.iter_mut() { |
743 | if let TypeParamBound::Trait(tb) = bound { |
744 | supersuperfy_path(&mut tb.path, levels); |
745 | } |
746 | } |
747 | }, |
748 | Type::ImplTrait(_) => { |
749 | /* Should've already been flagged as a compile error */ |
750 | }, |
751 | Type::Infer(_) | Type::Never(_) => |
752 | { |
753 | /* Nothing to do */ |
754 | }, |
755 | _ => compile_error(t.span(), "Unsupported type" ), |
756 | } |
757 | } |
758 | recurse(&mut output, levels); |
759 | output |
760 | } |
761 | |
762 | fn supersuperfy_generics(generics: &mut Generics, levels: usize) { |
763 | for param in generics.params.iter_mut() { |
764 | if let GenericParam::Type(tp) = param { |
765 | supersuperfy_bounds(&mut tp.bounds, levels); |
766 | if let Some(ty) = tp.default.as_mut() { |
767 | *ty = supersuperfy(ty, levels); |
768 | } |
769 | } |
770 | } |
771 | if let Some(wc) = generics.where_clause.as_mut() { |
772 | for wp in wc.predicates.iter_mut() { |
773 | if let WherePredicate::Type(pt) = wp { |
774 | pt.bounded_ty = supersuperfy(&pt.bounded_ty, levels); |
775 | supersuperfy_bounds(&mut pt.bounds, levels); |
776 | } |
777 | } |
778 | } |
779 | } |
780 | |
781 | fn supersuperfy_bounds( |
782 | bounds: &mut Punctuated<TypeParamBound, Token![+]>, |
783 | levels: usize) |
784 | { |
785 | for bound in bounds.iter_mut() { |
786 | if let TypeParamBound::Trait(tb) = bound { |
787 | supersuperfy_path(&mut tb.path, levels); |
788 | } |
789 | } |
790 | } |
791 | |
792 | /// Generate a suitable mockall::Key generic paramter from any Generics |
793 | fn gen_keyid(g: &Generics) -> impl ToTokens { |
794 | match g.params.len() { |
795 | 0 => quote!(<()>), |
796 | 1 => { |
797 | let (_, tg, _) = g.split_for_impl(); |
798 | quote!(#tg) |
799 | }, |
800 | _ => { |
801 | // Rust doesn't support variadic Generics, so mockall::Key must |
802 | // always have exactly one generic type. We need to add parentheses |
803 | // around whatever type generics the caller passes. |
804 | let tps = g.type_params() |
805 | .map(|tp| tp.ident.clone()) |
806 | .collect::<Punctuated::<Ident, Token![,]>>(); |
807 | quote!(<(#tps)>) |
808 | } |
809 | } |
810 | } |
811 | |
812 | /// Generate a mock identifier from the regular one: eg "Foo" => "MockFoo" |
813 | fn gen_mock_ident(ident: &Ident) -> Ident { |
814 | format_ident!("Mock{}" , ident) |
815 | } |
816 | |
817 | /// Generate an identifier for the mock struct's private module: eg "Foo" => |
818 | /// "__mock_Foo" |
819 | fn gen_mod_ident(struct_: &Ident, trait_: Option<&Ident>) -> Ident { |
820 | if let Some(t) = trait_ { |
821 | format_ident!("__mock_{}_{}" , struct_, t) |
822 | } else { |
823 | format_ident!("__mock_{}" , struct_) |
824 | } |
825 | } |
826 | |
827 | /// Combine two Generics structs, producing a new one that has the union of |
828 | /// their parameters. |
829 | fn merge_generics(x: &Generics, y: &Generics) -> Generics { |
830 | /// Compare only the identifiers of two GenericParams |
831 | fn cmp_gp_idents(x: &GenericParam, y: &GenericParam) -> bool { |
832 | use GenericParam::*; |
833 | |
834 | match (x, y) { |
835 | (Type(xtp), Type(ytp)) => xtp.ident == ytp.ident, |
836 | (Lifetime(xld), Lifetime(yld)) => xld.lifetime == yld.lifetime, |
837 | (Const(xc), Const(yc)) => xc.ident == yc.ident, |
838 | _ => false |
839 | } |
840 | } |
841 | |
842 | /// Compare only the identifiers of two WherePredicates |
843 | fn cmp_wp_idents(x: &WherePredicate, y: &WherePredicate) -> bool { |
844 | use WherePredicate::*; |
845 | |
846 | match (x, y) { |
847 | (Type(xpt), Type(ypt)) => xpt.bounded_ty == ypt.bounded_ty, |
848 | (Lifetime(xpl), Lifetime(ypl)) => xpl.lifetime == ypl.lifetime, |
849 | (Eq(xeq), Eq(yeq)) => xeq.lhs_ty == yeq.lhs_ty, |
850 | _ => false |
851 | } |
852 | } |
853 | |
854 | let mut out = if x.lt_token.is_none() && x.where_clause.is_none() { |
855 | y.clone() |
856 | } else if y.lt_token.is_none() && y.where_clause.is_none() { |
857 | x.clone() |
858 | } else { |
859 | let mut out = x.clone(); |
860 | // First merge the params |
861 | 'outer_param: for yparam in y.params.iter() { |
862 | // XXX: O(n^2) loop |
863 | for outparam in out.params.iter_mut() { |
864 | if cmp_gp_idents(outparam, yparam) { |
865 | if let (GenericParam::Type(ref mut ot), |
866 | GenericParam::Type(yt)) = (outparam, yparam) |
867 | { |
868 | ot.attrs.extend(yt.attrs.iter().cloned()); |
869 | ot.colon_token = ot.colon_token.or(yt.colon_token); |
870 | ot.eq_token = ot.eq_token.or(yt.eq_token); |
871 | if ot.default.is_none() { |
872 | ot.default = yt.default.clone(); |
873 | } |
874 | // XXX this might result in duplicate bounds |
875 | if ot.bounds != yt.bounds { |
876 | ot.bounds.extend(yt.bounds.iter().cloned()); |
877 | } |
878 | } |
879 | continue 'outer_param; |
880 | } |
881 | } |
882 | out.params.push(yparam.clone()); |
883 | } |
884 | out |
885 | }; |
886 | // Then merge the where clauses |
887 | match (&mut out.where_clause, &y.where_clause) { |
888 | (_, None) => (), |
889 | (None, Some(wc)) => out.where_clause = Some(wc.clone()), |
890 | (Some(out_wc), Some(y_wc)) => { |
891 | 'outer_wc: for ypred in y_wc.predicates.iter() { |
892 | // XXX: O(n^2) loop |
893 | for outpred in out_wc.predicates.iter_mut() { |
894 | if cmp_wp_idents(outpred, ypred) { |
895 | if let (WherePredicate::Type(ref mut ot), |
896 | WherePredicate::Type(yt)) = (outpred, ypred) |
897 | { |
898 | match (&mut ot.lifetimes, &yt.lifetimes) { |
899 | (_, None) => (), |
900 | (None, Some(bl)) => |
901 | ot.lifetimes = Some(bl.clone()), |
902 | (Some(obl), Some(ybl)) => |
903 | // XXX: might result in duplicates |
904 | obl.lifetimes.extend( |
905 | ybl.lifetimes.iter().cloned()), |
906 | }; |
907 | // XXX: might result in duplicate bounds |
908 | if ot.bounds != yt.bounds { |
909 | ot.bounds.extend(yt.bounds.iter().cloned()) |
910 | } |
911 | } |
912 | continue 'outer_wc; |
913 | } |
914 | } |
915 | out_wc.predicates.push(ypred.clone()); |
916 | } |
917 | } |
918 | } |
919 | out |
920 | } |
921 | |
922 | /// Transform a Vec of lifetimes into a Generics |
923 | fn lifetimes_to_generics(lv: &Punctuated<LifetimeDef, Token![,]>)-> Generics { |
924 | if lv.is_empty() { |
925 | Generics::default() |
926 | } else { |
927 | let params = lv.iter() |
928 | .map(|lt| GenericParam::Lifetime(lt.clone())) |
929 | .collect(); |
930 | Generics { |
931 | lt_token: Some(Token![<](lv[0].span())), |
932 | gt_token: Some(Token![>](lv[0].span())), |
933 | params, |
934 | where_clause: None |
935 | } |
936 | } |
937 | } |
938 | |
939 | /// Split a generics list into three: one for type generics and where predicates |
940 | /// that relate to the signature, one for lifetimes that relate to the arguments |
941 | /// only, and one for lifetimes that relate to the return type only. |
942 | fn split_lifetimes( |
943 | generics: Generics, |
944 | args: &[FnArg], |
945 | rt: &ReturnType) |
946 | -> (Generics, |
947 | Punctuated<LifetimeDef, token::Comma>, |
948 | Punctuated<LifetimeDef, token::Comma>) |
949 | { |
950 | if generics.lt_token.is_none() { |
951 | return (generics, Default::default(), Default::default()); |
952 | } |
953 | |
954 | // Check which types and lifetimes are referenced by the arguments |
955 | let mut alts = HashSet::<Lifetime>::default(); |
956 | let mut rlts = HashSet::<Lifetime>::default(); |
957 | for arg in args { |
958 | match arg { |
959 | FnArg::Receiver(r) => { |
960 | if let Some((_, Some(lt))) = &r.reference { |
961 | alts.insert(lt.clone()); |
962 | } |
963 | }, |
964 | FnArg::Typed(pt) => { |
965 | alts.extend(find_lifetimes(pt.ty.as_ref())); |
966 | }, |
967 | }; |
968 | }; |
969 | |
970 | if let ReturnType::Type(_, ty) = rt { |
971 | rlts.extend(find_lifetimes(ty)); |
972 | } |
973 | |
974 | let mut tv = Punctuated::new(); |
975 | let mut alv = Punctuated::new(); |
976 | let mut rlv = Punctuated::new(); |
977 | for p in generics.params.into_iter() { |
978 | match p { |
979 | GenericParam::Lifetime(ltd) if rlts.contains(<d.lifetime) => |
980 | rlv.push(ltd), |
981 | GenericParam::Lifetime(ltd) if alts.contains(<d.lifetime) => |
982 | alv.push(ltd), |
983 | GenericParam::Lifetime(_) => { |
984 | // Probably a lifetime parameter from the impl block that isn't |
985 | // used by this particular method |
986 | }, |
987 | GenericParam::Type(_) => tv.push(p), |
988 | _ => (), |
989 | } |
990 | } |
991 | |
992 | let tg = if tv.is_empty() { |
993 | Generics::default() |
994 | } else { |
995 | Generics { |
996 | lt_token: generics.lt_token, |
997 | gt_token: generics.gt_token, |
998 | params: tv, |
999 | where_clause: generics.where_clause |
1000 | } |
1001 | }; |
1002 | |
1003 | (tg, alv, rlv) |
1004 | } |
1005 | |
1006 | /// Return the visibility that should be used for expectation!, given the |
1007 | /// original method's visibility. |
1008 | /// |
1009 | /// # Arguments |
1010 | /// - `vis`: Original visibility of the item |
1011 | /// - `levels`: How many modules will the mock item be nested in? |
1012 | fn expectation_visibility(vis: &Visibility, levels: usize) |
1013 | -> Visibility |
1014 | { |
1015 | if levels == 0 { |
1016 | return vis.clone(); |
1017 | } |
1018 | |
1019 | let in_token = Token![in](vis.span()); |
1020 | let super_token = Token![super](vis.span()); |
1021 | match vis { |
1022 | Visibility::Inherited => { |
1023 | // Private items need pub(in super::[...]) for each level |
1024 | let mut path = Path::from(super_token); |
1025 | for _ in 1..levels { |
1026 | path.segments.push(super_token.into()); |
1027 | } |
1028 | Visibility::Restricted(VisRestricted{ |
1029 | pub_token: Token![pub](vis.span()), |
1030 | paren_token: token::Paren::default(), |
1031 | in_token: Some(in_token), |
1032 | path: Box::new(path) |
1033 | }) |
1034 | }, |
1035 | Visibility::Restricted(vr) => { |
1036 | // crate => don't change |
1037 | // in crate::* => don't change |
1038 | // super => in super::super::super |
1039 | // self => in super::super |
1040 | // in anything_else => super::super::anything_else |
1041 | if vr.path.segments.first().unwrap().ident == "crate" { |
1042 | vr.clone().into() |
1043 | } else { |
1044 | let mut out = vr.clone(); |
1045 | out.in_token = Some(in_token); |
1046 | for _ in 0..levels { |
1047 | out.path.segments.insert(0, super_token.into()); |
1048 | } |
1049 | out.into() |
1050 | } |
1051 | }, |
1052 | _ => vis.clone() |
1053 | } |
1054 | } |
1055 | |
1056 | fn staticize(generics: &Generics) -> Generics { |
1057 | let mut ret = generics.clone(); |
1058 | for lt in ret.lifetimes_mut() { |
1059 | lt.lifetime = Lifetime::new("'static" , Span::call_site()); |
1060 | }; |
1061 | ret |
1062 | } |
1063 | |
1064 | fn mock_it<M: Into<MockableItem>>(inputs: M) -> TokenStream |
1065 | { |
1066 | let mockable: MockableItem = inputs.into(); |
1067 | let mock = MockItem::from(mockable); |
1068 | let ts = mock.into_token_stream(); |
1069 | if env::var("MOCKALL_DEBUG" ).is_ok() { |
1070 | println!("{}" , ts); |
1071 | } |
1072 | ts |
1073 | } |
1074 | |
1075 | fn do_mock_once(input: TokenStream) -> TokenStream |
1076 | { |
1077 | let item: MockableStruct = match syn::parse2(input) { |
1078 | Ok(mock) => mock, |
1079 | Err(err) => { |
1080 | return err.to_compile_error(); |
1081 | } |
1082 | }; |
1083 | mock_it(item) |
1084 | } |
1085 | |
1086 | fn do_mock(input: TokenStream) -> TokenStream |
1087 | { |
1088 | cfg_if! { |
1089 | if #[cfg(reprocheck)] { |
1090 | let ts_a = do_mock_once(input.clone()); |
1091 | let ts_b = do_mock_once(input.clone()); |
1092 | assert_eq!(ts_a.to_string(), ts_b.to_string()); |
1093 | } |
1094 | } |
1095 | do_mock_once(input) |
1096 | } |
1097 | |
1098 | #[proc_macro ] |
1099 | pub fn mock(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
1100 | do_mock(input.into()).into() |
1101 | } |
1102 | |
1103 | #[proc_macro_attribute ] |
1104 | pub fn automock (attrs: proc_macro::TokenStream, input: proc_macro::TokenStream) |
1105 | -> proc_macro::TokenStream |
1106 | { |
1107 | let attrs: proc_macro2::TokenStream = attrs.into(); |
1108 | let input: proc_macro2::TokenStream = input.into(); |
1109 | do_automock(attrs, input).into() |
1110 | } |
1111 | |
1112 | fn do_automock_once(attrs: TokenStream, input: TokenStream) -> TokenStream { |
1113 | let mut output = input.clone(); |
1114 | let attrs: Attrs = match parse2(attrs) { |
1115 | Ok(a) => a, |
1116 | Err(err) => { |
1117 | return err.to_compile_error(); |
1118 | } |
1119 | }; |
1120 | let item: Item = match parse2(input) { |
1121 | Ok(item) => item, |
1122 | Err(err) => { |
1123 | return err.to_compile_error(); |
1124 | } |
1125 | }; |
1126 | output.extend(mock_it((attrs, item))); |
1127 | output |
1128 | } |
1129 | |
1130 | fn do_automock(attrs: TokenStream, input: TokenStream) -> TokenStream { |
1131 | cfg_if! { |
1132 | if #[cfg(reprocheck)] { |
1133 | let ts_a = do_automock_once(attrs.clone(), input.clone()); |
1134 | let ts_b = do_automock_once(attrs.clone(), input.clone()); |
1135 | assert_eq!(ts_a.to_string(), ts_b.to_string()); |
1136 | } |
1137 | } |
1138 | do_automock_once(attrs, input) |
1139 | } |
1140 | |
1141 | #[cfg (test)] |
1142 | mod t { |
1143 | use super::*; |
1144 | |
1145 | fn assert_contains(output: &str, tokens: TokenStream) { |
1146 | let s = tokens.to_string(); |
1147 | assert!(output.contains(&s), "output does not contain {:?}" , &s); |
1148 | } |
1149 | |
1150 | fn assert_not_contains(output: &str, tokens: TokenStream) { |
1151 | let s = tokens.to_string(); |
1152 | assert!(!output.contains(&s), "output does not contain {:?}" , &s); |
1153 | } |
1154 | |
1155 | /// Various tests for overall code generation that are hard or impossible to |
1156 | /// write as integration tests |
1157 | mod mock { |
1158 | use std::str::FromStr; |
1159 | use super::super::*; |
1160 | use super::*; |
1161 | |
1162 | #[test] |
1163 | fn inherent_method_visibility() { |
1164 | let code = r#" |
1165 | Foo { |
1166 | fn foo(&self); |
1167 | pub fn bar(&self); |
1168 | pub(crate) fn baz(&self); |
1169 | pub(super) fn bean(&self); |
1170 | pub(in crate::outer) fn boom(&self); |
1171 | } |
1172 | "# ; |
1173 | let ts = proc_macro2::TokenStream::from_str(code).unwrap(); |
1174 | let output = do_mock(ts).to_string(); |
1175 | assert_not_contains(&output, quote!(pub fn foo)); |
1176 | assert!(!output.contains(") fn foo" )); |
1177 | assert_contains(&output, quote!(pub fn bar)); |
1178 | assert_contains(&output, quote!(pub(crate) fn baz)); |
1179 | assert_contains(&output, quote!(pub(super) fn bean)); |
1180 | assert_contains(&output, quote!(pub(in crate::outer) fn boom)); |
1181 | |
1182 | assert_not_contains(&output, quote!(pub fn expect_foo)); |
1183 | assert!(!output.contains("pub fn expect_foo" )); |
1184 | assert!(!output.contains(") fn expect_foo" )); |
1185 | assert_contains(&output, quote!(pub fn expect_bar)); |
1186 | assert_contains(&output, quote!(pub(crate) fn expect_baz)); |
1187 | assert_contains(&output, quote!(pub(super) fn expect_bean)); |
1188 | assert_contains(&output, quote!(pub(in crate::outer) fn expect_boom)); |
1189 | } |
1190 | |
1191 | #[test] |
1192 | fn specific_impl() { |
1193 | let code = r#" |
1194 | pub Foo<T: 'static> {} |
1195 | impl Bar for Foo<u32> { |
1196 | fn bar(&self); |
1197 | } |
1198 | impl Bar for Foo<i32> { |
1199 | fn bar(&self); |
1200 | } |
1201 | "# ; |
1202 | let ts = proc_macro2::TokenStream::from_str(code).unwrap(); |
1203 | let output = do_mock(ts).to_string(); |
1204 | assert_contains(&output, quote!(impl Bar for MockFoo<u32>)); |
1205 | assert_contains(&output, quote!(impl Bar for MockFoo<i32>)); |
1206 | // Ensure we don't duplicate the checkpoint function |
1207 | assert_not_contains(&output, quote!( |
1208 | self.Bar_expectations.checkpoint(); |
1209 | self.Bar_expectations.checkpoint(); |
1210 | )); |
1211 | // The expect methods should return specific types, not generic ones |
1212 | assert_contains(&output, quote!( |
1213 | pub fn expect_bar(&mut self) -> &mut __mock_MockFoo_Bar::__bar::Expectation<u32> |
1214 | )); |
1215 | assert_contains(&output, quote!( |
1216 | pub fn expect_bar(&mut self) -> &mut __mock_MockFoo_Bar::__bar::Expectation<i32> |
1217 | )); |
1218 | } |
1219 | } |
1220 | |
1221 | /// Various tests for overall code generation that are hard or impossible to |
1222 | /// write as integration tests |
1223 | mod automock { |
1224 | use std::str::FromStr; |
1225 | use super::super::*; |
1226 | use super::*; |
1227 | |
1228 | #[test] |
1229 | fn doc_comments() { |
1230 | let code = r#" |
1231 | mod foo { |
1232 | /// Function docs |
1233 | pub fn bar() { unimplemented!() } |
1234 | } |
1235 | "# ; |
1236 | let ts = proc_macro2::TokenStream::from_str(code).unwrap(); |
1237 | let attrs_ts = proc_macro2::TokenStream::from_str("" ).unwrap(); |
1238 | let output = do_automock(attrs_ts, ts).to_string(); |
1239 | assert_contains(&output, quote!(#[doc=" Function docs" ] pub fn bar)); |
1240 | } |
1241 | |
1242 | #[test] |
1243 | fn method_visibility() { |
1244 | let code = r#" |
1245 | impl Foo { |
1246 | fn foo(&self) {} |
1247 | pub fn bar(&self) {} |
1248 | pub(super) fn baz(&self) {} |
1249 | pub(crate) fn bang(&self) {} |
1250 | pub(in super::x) fn bean(&self) {} |
1251 | }"# ; |
1252 | let ts = proc_macro2::TokenStream::from_str(code).unwrap(); |
1253 | let attrs_ts = proc_macro2::TokenStream::from_str("" ).unwrap(); |
1254 | let output = do_automock(attrs_ts, ts).to_string(); |
1255 | assert_not_contains(&output, quote!(pub fn foo)); |
1256 | assert!(!output.contains(") fn foo" )); |
1257 | assert_not_contains(&output, quote!(pub fn expect_foo)); |
1258 | assert!(!output.contains(") fn expect_foo" )); |
1259 | assert_contains(&output, quote!(pub fn bar)); |
1260 | assert_contains(&output, quote!(pub fn expect_bar)); |
1261 | assert_contains(&output, quote!(pub(super) fn baz)); |
1262 | assert_contains(&output, quote!(pub(super) fn expect_baz)); |
1263 | assert_contains(&output, quote!(pub ( crate ) fn bang)); |
1264 | assert_contains(&output, quote!(pub ( crate ) fn expect_bang)); |
1265 | assert_contains(&output, quote!(pub ( in super :: x ) fn bean)); |
1266 | assert_contains(&output, quote!(pub ( in super :: x ) fn expect_bean)); |
1267 | } |
1268 | |
1269 | #[test] |
1270 | #[should_panic (expected = "can only mock inline modules" )] |
1271 | fn external_module() { |
1272 | let code = r#"mod foo;"# ; |
1273 | let ts = proc_macro2::TokenStream::from_str(code).unwrap(); |
1274 | let attrs_ts = proc_macro2::TokenStream::from_str("" ).unwrap(); |
1275 | do_automock(attrs_ts, ts).to_string(); |
1276 | } |
1277 | |
1278 | #[test] |
1279 | fn trait_visibility() { |
1280 | let code = r#" |
1281 | pub(super) trait Foo {} |
1282 | "# ; |
1283 | let attrs_ts = proc_macro2::TokenStream::from_str("" ).unwrap(); |
1284 | let ts = proc_macro2::TokenStream::from_str(code).unwrap(); |
1285 | let output = do_automock(attrs_ts, ts).to_string(); |
1286 | assert_contains(&output, quote!(pub ( super ) struct MockFoo)); |
1287 | } |
1288 | } |
1289 | |
1290 | mod deimplify { |
1291 | use super::*; |
1292 | |
1293 | fn check_deimplify(orig_ts: TokenStream, expected_ts: TokenStream) { |
1294 | let mut orig: ReturnType = parse2(orig_ts).unwrap(); |
1295 | let expected: ReturnType = parse2(expected_ts).unwrap(); |
1296 | deimplify(&mut orig); |
1297 | assert_eq!(quote!(#orig).to_string(), quote!(#expected).to_string()); |
1298 | } |
1299 | |
1300 | // Future is a special case |
1301 | #[test] |
1302 | fn impl_future() { |
1303 | check_deimplify( |
1304 | quote!(-> impl Future<Output=i32>), |
1305 | quote!(-> ::std::pin::Pin<Box<dyn Future<Output=i32>>>) |
1306 | ); |
1307 | } |
1308 | |
1309 | // Future is a special case, wherever it appears |
1310 | #[test] |
1311 | fn impl_future_reverse() { |
1312 | check_deimplify( |
1313 | quote!(-> impl Send + Future<Output=i32>), |
1314 | quote!(-> ::std::pin::Pin<Box<dyn Send + Future<Output=i32>>>) |
1315 | ); |
1316 | } |
1317 | |
1318 | // Stream is a special case |
1319 | #[test] |
1320 | fn impl_stream() { |
1321 | check_deimplify( |
1322 | quote!(-> impl Stream<Item=i32>), |
1323 | quote!(-> ::std::pin::Pin<Box<dyn Stream<Item=i32>>>) |
1324 | ); |
1325 | } |
1326 | |
1327 | #[test] |
1328 | fn impl_trait() { |
1329 | check_deimplify( |
1330 | quote!(-> impl Foo), |
1331 | quote!(-> Box<dyn Foo>) |
1332 | ); |
1333 | } |
1334 | |
1335 | // With extra bounds |
1336 | #[test] |
1337 | fn impl_trait2() { |
1338 | check_deimplify( |
1339 | quote!(-> impl Foo + Send), |
1340 | quote!(-> Box<dyn Foo + Send>) |
1341 | ); |
1342 | } |
1343 | } |
1344 | |
1345 | mod deselfify { |
1346 | use super::*; |
1347 | |
1348 | fn check_deselfify( |
1349 | orig_ts: TokenStream, |
1350 | actual_ts: TokenStream, |
1351 | generics_ts: TokenStream, |
1352 | expected_ts: TokenStream) |
1353 | { |
1354 | let mut ty: Type = parse2(orig_ts).unwrap(); |
1355 | let actual: Ident = parse2(actual_ts).unwrap(); |
1356 | let generics: Generics = parse2(generics_ts).unwrap(); |
1357 | let expected: Type = parse2(expected_ts).unwrap(); |
1358 | deselfify(&mut ty, &actual, &generics); |
1359 | assert_eq!(quote!(#ty).to_string(), |
1360 | quote!(#expected).to_string()); |
1361 | } |
1362 | |
1363 | #[test] |
1364 | fn future() { |
1365 | check_deselfify( |
1366 | quote!(Box<dyn Future<Output=Self>>), |
1367 | quote!(Foo), |
1368 | quote!(), |
1369 | quote!(Box<dyn Future<Output=Foo>>) |
1370 | ); |
1371 | } |
1372 | |
1373 | #[test] |
1374 | fn qself() { |
1375 | check_deselfify( |
1376 | quote!(<Self as Self>::Self), |
1377 | quote!(Foo), |
1378 | quote!(), |
1379 | quote!(<Foo as Foo>::Foo) |
1380 | ); |
1381 | } |
1382 | |
1383 | #[test] |
1384 | fn trait_object() { |
1385 | check_deselfify( |
1386 | quote!(Box<dyn Self>), |
1387 | quote!(Foo), |
1388 | quote!(), |
1389 | quote!(Box<dyn Foo>) |
1390 | ); |
1391 | } |
1392 | |
1393 | // A trait object with multiple bounds |
1394 | #[test] |
1395 | fn trait_object2() { |
1396 | check_deselfify( |
1397 | quote!(Box<dyn Self + Send>), |
1398 | quote!(Foo), |
1399 | quote!(), |
1400 | quote!(Box<dyn Foo + Send>) |
1401 | ); |
1402 | } |
1403 | } |
1404 | |
1405 | mod dewhereselfify { |
1406 | use super::*; |
1407 | |
1408 | #[test] |
1409 | fn lifetime() { |
1410 | let mut meth: ImplItemMethod = parse2(quote!( |
1411 | fn foo<'a>(&self) where 'a: 'static, Self: Sized; |
1412 | )).unwrap(); |
1413 | let expected: ImplItemMethod = parse2(quote!( |
1414 | fn foo<'a>(&self) where 'a: 'static; |
1415 | )).unwrap(); |
1416 | dewhereselfify(&mut meth.sig.generics); |
1417 | assert_eq!(meth, expected); |
1418 | } |
1419 | |
1420 | #[test] |
1421 | fn normal_method() { |
1422 | let mut meth: ImplItemMethod = parse2(quote!( |
1423 | fn foo(&self) where Self: Sized; |
1424 | )).unwrap(); |
1425 | let expected: ImplItemMethod = parse2(quote!( |
1426 | fn foo(&self); |
1427 | )).unwrap(); |
1428 | dewhereselfify(&mut meth.sig.generics); |
1429 | assert_eq!(meth, expected); |
1430 | } |
1431 | |
1432 | #[test] |
1433 | fn with_real_generics() { |
1434 | let mut meth: ImplItemMethod = parse2(quote!( |
1435 | fn foo<T>(&self, t: T) where Self: Sized, T: Copy; |
1436 | )).unwrap(); |
1437 | let expected: ImplItemMethod = parse2(quote!( |
1438 | fn foo<T>(&self, t: T) where T: Copy; |
1439 | )).unwrap(); |
1440 | dewhereselfify(&mut meth.sig.generics); |
1441 | assert_eq!(meth, expected); |
1442 | } |
1443 | } |
1444 | |
1445 | mod gen_keyid { |
1446 | use super::*; |
1447 | |
1448 | fn check_gen_keyid(orig: TokenStream, expected: TokenStream) { |
1449 | let g: Generics = parse2(orig).unwrap(); |
1450 | let keyid = gen_keyid(&g); |
1451 | assert_eq!(quote!(#keyid).to_string(), quote!(#expected).to_string()); |
1452 | } |
1453 | |
1454 | #[test] |
1455 | fn empty() { |
1456 | check_gen_keyid(quote!(), quote!(<()>)); |
1457 | } |
1458 | |
1459 | #[test] |
1460 | fn onetype() { |
1461 | check_gen_keyid(quote!(<T>), quote!(<T>)); |
1462 | } |
1463 | |
1464 | #[test] |
1465 | fn twotypes() { |
1466 | check_gen_keyid(quote!(<T, V>), quote!(<(T, V)>)); |
1467 | } |
1468 | } |
1469 | |
1470 | mod merge_generics { |
1471 | use super::*; |
1472 | |
1473 | #[test] |
1474 | fn both() { |
1475 | let mut g1: Generics = parse2(quote!(<T: 'static, V: Copy> )).unwrap(); |
1476 | let wc1: WhereClause = parse2(quote!(where T: Default)).unwrap(); |
1477 | g1.where_clause = Some(wc1); |
1478 | |
1479 | let mut g2: Generics = parse2(quote!(<Q: Send, V: Clone>)).unwrap(); |
1480 | let wc2: WhereClause = parse2(quote!(where T: Sync, Q: Debug)).unwrap(); |
1481 | g2.where_clause = Some(wc2); |
1482 | |
1483 | let gm = super::merge_generics(&g1, &g2); |
1484 | let gm_wc = &gm.where_clause; |
1485 | |
1486 | let ge: Generics = parse2(quote!( |
1487 | <T: 'static, V: Copy + Clone, Q: Send> |
1488 | )).unwrap(); |
1489 | let wce: WhereClause = parse2(quote!( |
1490 | where T: Default + Sync, Q: Debug |
1491 | )).unwrap(); |
1492 | |
1493 | assert_eq!(quote!(#ge #wce).to_string(), |
1494 | quote!(#gm #gm_wc).to_string()); |
1495 | } |
1496 | |
1497 | #[test] |
1498 | fn eq() { |
1499 | let mut g1: Generics = parse2(quote!(<T: 'static, V: Copy> )).unwrap(); |
1500 | let wc1: WhereClause = parse2(quote!(where T: Default)).unwrap(); |
1501 | g1.where_clause = Some(wc1.clone()); |
1502 | |
1503 | let gm = super::merge_generics(&g1, &g1); |
1504 | let gm_wc = &gm.where_clause; |
1505 | |
1506 | assert_eq!(quote!(#g1 #wc1).to_string(), |
1507 | quote!(#gm #gm_wc).to_string()); |
1508 | } |
1509 | |
1510 | #[test] |
1511 | fn lhs_only() { |
1512 | let mut g1: Generics = parse2(quote!(<T: 'static, V: Copy> )).unwrap(); |
1513 | let wc1: WhereClause = parse2(quote!(where T: Default)).unwrap(); |
1514 | g1.where_clause = Some(wc1.clone()); |
1515 | |
1516 | let g2 = Generics::default(); |
1517 | |
1518 | let gm = super::merge_generics(&g1, &g2); |
1519 | let gm_wc = &gm.where_clause; |
1520 | |
1521 | assert_eq!(quote!(#g1 #wc1).to_string(), |
1522 | quote!(#gm #gm_wc).to_string()); |
1523 | } |
1524 | |
1525 | #[test] |
1526 | fn lhs_wc_only() { |
1527 | let mut g1 = Generics::default(); |
1528 | let wc1: WhereClause = parse2(quote!(where T: Default)).unwrap(); |
1529 | g1.where_clause = Some(wc1.clone()); |
1530 | |
1531 | let g2 = Generics::default(); |
1532 | |
1533 | let gm = super::merge_generics(&g1, &g2); |
1534 | let gm_wc = &gm.where_clause; |
1535 | |
1536 | assert_eq!(quote!(#g1 #wc1).to_string(), |
1537 | quote!(#gm #gm_wc).to_string()); |
1538 | } |
1539 | |
1540 | #[test] |
1541 | fn rhs_only() { |
1542 | let g1 = Generics::default(); |
1543 | let mut g2: Generics = parse2(quote!(<Q: Send, V: Clone>)).unwrap(); |
1544 | let wc2: WhereClause = parse2(quote!(where T: Sync, Q: Debug)).unwrap(); |
1545 | g2.where_clause = Some(wc2.clone()); |
1546 | |
1547 | let gm = super::merge_generics(&g1, &g2); |
1548 | let gm_wc = &gm.where_clause; |
1549 | |
1550 | assert_eq!(quote!(#g2 #wc2).to_string(), |
1551 | quote!(#gm #gm_wc).to_string()); |
1552 | } |
1553 | } |
1554 | |
1555 | mod supersuperfy { |
1556 | use super::*; |
1557 | |
1558 | fn check_supersuperfy(orig: TokenStream, expected: TokenStream) { |
1559 | let orig_ty: Type = parse2(orig).unwrap(); |
1560 | let expected_ty: Type = parse2(expected).unwrap(); |
1561 | let output = supersuperfy(&orig_ty, 1); |
1562 | assert_eq!(quote!(#output).to_string(), |
1563 | quote!(#expected_ty).to_string()); |
1564 | } |
1565 | |
1566 | #[test] |
1567 | fn array() { |
1568 | check_supersuperfy( |
1569 | quote!([super::X; n]), |
1570 | quote!([super::super::X; n]) |
1571 | ); |
1572 | } |
1573 | |
1574 | #[test] |
1575 | fn barefn() { |
1576 | check_supersuperfy( |
1577 | quote!(fn(super::A) -> super::B), |
1578 | quote!(fn(super::super::A) -> super::super::B) |
1579 | ); |
1580 | } |
1581 | |
1582 | #[test] |
1583 | fn group() { |
1584 | let orig = TypeGroup { |
1585 | group_token: token::Group::default(), |
1586 | elem: Box::new(parse2(quote!(super::T)).unwrap()) |
1587 | }; |
1588 | let expected = TypeGroup { |
1589 | group_token: token::Group::default(), |
1590 | elem: Box::new(parse2(quote!(super::super::T)).unwrap()) |
1591 | }; |
1592 | let output = supersuperfy(&Type::Group(orig), 1); |
1593 | assert_eq!(quote!(#output).to_string(), |
1594 | quote!(#expected).to_string()); |
1595 | } |
1596 | |
1597 | // Just check that it doesn't panic |
1598 | #[test] |
1599 | fn infer() { |
1600 | check_supersuperfy( quote!(_), quote!(_)); |
1601 | } |
1602 | |
1603 | // Just check that it doesn't panic |
1604 | #[test] |
1605 | fn never() { |
1606 | check_supersuperfy( quote!(!), quote!(!)); |
1607 | } |
1608 | |
1609 | #[test] |
1610 | fn paren() { |
1611 | check_supersuperfy( |
1612 | quote!((super::X)), |
1613 | quote!((super::super::X)) |
1614 | ); |
1615 | } |
1616 | |
1617 | #[test] |
1618 | fn path() { |
1619 | check_supersuperfy( |
1620 | quote!(::super::SuperT<u32>), |
1621 | quote!(::super::super::SuperT<u32>) |
1622 | ); |
1623 | } |
1624 | |
1625 | #[test] |
1626 | fn path_with_qself() { |
1627 | check_supersuperfy( |
1628 | quote!(<super::X as super::Y>::Foo<u32>), |
1629 | quote!(<super::super::X as super::super::Y>::Foo<u32>), |
1630 | ); |
1631 | } |
1632 | |
1633 | #[test] |
1634 | fn angle_bracketed_generic_arguments() { |
1635 | check_supersuperfy( |
1636 | quote!(mod_::T<super::X>), |
1637 | quote!(mod_::T<super::super::X>) |
1638 | ); |
1639 | } |
1640 | |
1641 | #[test] |
1642 | fn ptr() { |
1643 | check_supersuperfy( |
1644 | quote!(*const super::X), |
1645 | quote!(*const super::super::X) |
1646 | ); |
1647 | } |
1648 | |
1649 | #[test] |
1650 | fn reference() { |
1651 | check_supersuperfy( |
1652 | quote!(&'a mut super::X), |
1653 | quote!(&'a mut super::super::X) |
1654 | ); |
1655 | } |
1656 | |
1657 | #[test] |
1658 | fn slice() { |
1659 | check_supersuperfy( |
1660 | quote!([super::X]), |
1661 | quote!([super::super::X]) |
1662 | ); |
1663 | } |
1664 | |
1665 | #[test] |
1666 | fn trait_object() { |
1667 | check_supersuperfy( |
1668 | quote!(dyn super::X + super::Y), |
1669 | quote!(dyn super::super::X + super::super::Y) |
1670 | ); |
1671 | } |
1672 | |
1673 | #[test] |
1674 | fn tuple() { |
1675 | check_supersuperfy( |
1676 | quote!((super::A, super::B)), |
1677 | quote!((super::super::A, super::super::B)) |
1678 | ); |
1679 | } |
1680 | } |
1681 | |
1682 | mod supersuperfy_generics { |
1683 | use super::*; |
1684 | |
1685 | fn check_supersuperfy_generics( |
1686 | orig: TokenStream, |
1687 | orig_wc: TokenStream, |
1688 | expected: TokenStream, |
1689 | expected_wc: TokenStream) |
1690 | { |
1691 | let mut orig_g: Generics = parse2(orig).unwrap(); |
1692 | orig_g.where_clause = parse2(orig_wc).unwrap(); |
1693 | let mut expected_g: Generics = parse2(expected).unwrap(); |
1694 | expected_g.where_clause = parse2(expected_wc).unwrap(); |
1695 | let mut output: Generics = orig_g; |
1696 | supersuperfy_generics(&mut output, 1); |
1697 | let (o_ig, o_tg, o_wc) = output.split_for_impl(); |
1698 | let (e_ig, e_tg, e_wc) = expected_g.split_for_impl(); |
1699 | assert_eq!(quote!(#o_ig).to_string(), quote!(#e_ig).to_string()); |
1700 | assert_eq!(quote!(#o_tg).to_string(), quote!(#e_tg).to_string()); |
1701 | assert_eq!(quote!(#o_wc).to_string(), quote!(#e_wc).to_string()); |
1702 | } |
1703 | |
1704 | #[test] |
1705 | fn default() { |
1706 | check_supersuperfy_generics( |
1707 | quote!(<T: X = super::Y>), quote!(), |
1708 | quote!(<T: X = super::super::Y>), quote!(), |
1709 | ); |
1710 | } |
1711 | |
1712 | #[test] |
1713 | fn empty() { |
1714 | check_supersuperfy_generics(quote!(), quote!(), quote!(), quote!()); |
1715 | } |
1716 | |
1717 | #[test] |
1718 | fn everything() { |
1719 | check_supersuperfy_generics( |
1720 | quote!(<T: super::A = super::B>), |
1721 | quote!(where super::C: super::D), |
1722 | quote!(<T: super::super::A = super::super::B>), |
1723 | quote!(where super::super::C: super::super::D), |
1724 | ); |
1725 | } |
1726 | |
1727 | #[test] |
1728 | fn bound() { |
1729 | check_supersuperfy_generics( |
1730 | quote!(<T: super::A>), quote!(), |
1731 | quote!(<T: super::super::A>), quote!(), |
1732 | ); |
1733 | } |
1734 | |
1735 | #[test] |
1736 | fn closure() { |
1737 | check_supersuperfy_generics( |
1738 | quote!(<F: Fn(u32) -> super::SuperT>), quote!(), |
1739 | quote!(<F: Fn(u32) -> super::super::SuperT>), quote!(), |
1740 | ); |
1741 | } |
1742 | |
1743 | #[test] |
1744 | fn wc_bounded_ty() { |
1745 | check_supersuperfy_generics( |
1746 | quote!(), quote!(where super::T: X), |
1747 | quote!(), quote!(where super::super::T: X), |
1748 | ); |
1749 | } |
1750 | |
1751 | #[test] |
1752 | fn wc_bounds() { |
1753 | check_supersuperfy_generics( |
1754 | quote!(), quote!(where T: super::X), |
1755 | quote!(), quote!(where T: super::super::X), |
1756 | ); |
1757 | } |
1758 | } |
1759 | } |
1760 | |